This talk goes through many of the Object-Oriented Programming principles and characteristics. Things that all developers should have in mind while writing code.
4. public class Rectangle { private int height; private int width; public Rectangle( int height, int width) { this. height = height; this. width = width; } public int getHeight() { return this .height; } public void setHeight( int height) { this .height = height; } public int getWidth() { return this .width; } public void setWidth( int width) { this. width = width; } } public class Square extends Rectangle { public Square( int size) { super(size, size); } public int setHeight( int height) { return super. setHeight(height); } public int setWidth( int width) { return super. setWidth(width); } } What will happen when we call the setters? Class Invariant Invariant fix >>
5.
6. “ Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it. “ LSP violation public void drawShape(Shape s) { if (s instanceof Square) { drawSquare((Square) s); } else if (s instanceof Circle){ drawCircle((Circle) s); } } Liskov Substitution Principle - LSP Behavioral subtyping LSP violations >>
7. A more subtle violation of LSP Rectangle r = new Square(); r.setHeight(5); r.setWidth(6); Violations - Without the fix, Square would be in an invalid state when used as a Rectangle; - With the fix, the Square setter methods weaken (violate) Rectangle’s post conditions, which state that dimensions can be modified independently Liskov Substitution Principle - LSP Fixing the design >>
8.
9. - Term was coined by Bertrand Meyer in 1986 Based on the mutual obligations and benefits principle. - Pre-condition : obligation for the client, benefit for the supplier - Post-condition : obligation for the supplier, benefit for the client - Invariant : Assumed on entry, guaranteed on exit Design by Contract DbC implications >>
10.
11. - DbC : It’s the client’s responsibility (obligation) to respect supplier’s pre-condition. Code should “fail-hard” - Defensive Programming : Supplier takes the responsibility to validate pre-condition In both cases, client needs to take an action. DbC vs Defensive Programming - Whatever you do, don’t hide bugs. If a pre-condition is not met, let the system blow or let the client handle it. - Don’t code default behaviour unless this is part of the requirement. Side effect >>
12.
13.
14.
15. Violation of POLA / POLS public class EventProcessor { public void processEvent(Event event) throws EventProcessorException { try { doSomethingWith(event); event.setState( PROCESSED ); // violation } catch (Exception e) { event.setState( FAILED ); // violation throw new EventProcessorException(e); } } } Possible undesired side effect. SRP >>
16.
17. Single Responsibility Principle - SRP Where do things go wrong? - SRP violations are most seen in systems developed without TDD and refactoring. - Classes closer to the system interface have a more generic and broader responsibility - Failure do identify responsibility / delegation ratio Cohesion >>
18. Cohesion The cornerstone of Object-Oriented Programming Class level >> Cambridge Dictionary Cohesion (noun) : when the members of a group or society are united. Cohesive (adjective) : united and working together effectively. Wikipedia In computer programming, cohesion is a measure of how strongly-related and focused the various responsibilities of a software module are. LCOM4: Lack of Cohesion Methods (metrics used by Sonar)
22. Coupling Types >> Coupling or dependency is the degree to which each program module relies on each one of the other modules - Low coupling often correlates with high cohesion, and vice versa. - Sign of good design and well maintainable code.
33. Information Hiding & Protected Variation OCP >> Information Hiding : Hide information about the design from other modules, at the points of difficult or likely to change. David Parnas Information hiding is Protected Variation, not data encapsulation To minimise change impacts, we aim for low coupling. To design for low coupling, we design for protected variations. Craig Larman Protected Variation : Identify points of predicted variation and create a stable interface around them. Alistair Cockburn
34.
35.
36. Feature Envy Other topics >> public void doSomething() { BigDecimal baseMonthlySalary = employee.getBaseMontlySalary(); int overtimeInHours = employee.getOverTimeInHours(); BigDecimal overtimeHourRate = employee.getOvertimeHourRate(); BigDecimal salaryToBePaid = baseMonthlySalary + (overtimeInHours * overtimeHourRate); // Do more stuff with the salary } public void doSomething() { BigDecimal salaryToBePaid = employee.getMonthlySalaryToBePaid(); // Do more stuff with the salary } A better implementation
37.
38. Wrap up There is always a good reason to break a rule or law, but it really needs to be a good one. The critical design tool for software development is a mind well educated in design principles. It is not the UML or any other technology Craig Larman Most software engineers don’t set out to create “bad designs”. Yet most software eventually degrades to the point where someone will declare the design to be unsound. Why does this happen? Was the design poor to begin with, or did the design actually degrade like a piece of rotten meat? At the heart of this issue is our lack of a good working definition of “bad” design . Uncle Bob Martin Questions >>
39. Questions London Software Craftsmanship Community – LSCC – http://www.londonswcraft.com Twitter: @sandromancuso
Notas del editor
Class invariants are established during construction and constantly maintained between calls to public methods.
LSP's importance is noticed mainly when it is violated. Client code needs to do instanceof or type casting.
LSP's importance is noticed mainly when it is violated. Client code needs to do instanceof or type casting. violation of the Liskov Substitution Principle since the methods will weaken (violate) the postconditions for the Rectangle setters, which state that dimensions can be modified independently.
LSP's importance is noticed mainly when it is violated. Client code needs to do instanceof or type casting. violation of the Liskov Substitution Principle since the methods will weaken (violate) the postconditions for the Rectangle setters, which state that dimensions can be modified independently.
Client pays fee (obligation) and get a product back (benefit). Supplier gives product to client (obligation) and get money in exchange (benefit). Three questions we need to ask when defining a method: - What does it expect? - What does it guarantee? - What does it maintain?
Testing: Unit testing tests a class in isolation, checking if it meets its contract assuming its clients and suppliers meet theirs Integration/Functional tests checks whether the various classes are working properly together
Pre-condition checks using assertions are quite often associated with Design by Contract. Some languages like Eifel have native support. In Java, frameworks like JContract are meant to do check pre-condition, post-condition and invariants using annotations.
Side effect examples: a function might modify a global or static variable , modify one of its arguments, raise an exception, write data to a display or file, read data, or call other side-effecting functions. Bugs due to "action at a distance" may arise because a program component is doing something at the wrong time, or affecting something it should not. It is very difficult, however, to track down which component is responsible. Side effects from innocent actions can put the program in an unknown state, so local data is not necessarily local. The solution in this particular scenario is to define which components should be interacting with which others. A proper design that accurately defines the interface between parts of a program, and that avoids shared states, can largely eliminate problems caused by action at a distance.
Red box: TradeService.processTrade(tradeXml) or Order.placeOrder(shoppingBasket) - The closer the code is to the system's interface, the broader and more generic its responsibility will be. The further away the code is from the system's interface, the narrower and more specific its responsibility will be. - The closer the class is to the system's interface, the more delegation the class will do. The further way the class is from the system's interface, less delegation the class will do.
God object is an object that knows too much or does too much . The God object is an example of an anti-pattern .
- Low coupling often correlates with high cohesion, and vice versa. - Sign of good design and well maintainable code.
Content coupling (high) Content coupling is when one module modifies or relies on the internal workings of another module (e.g., accessing local data of another module). Therefore changing the way the second module produces data (location, type, timing) will lead to changing the dependent module. Common coupling Common coupling is when two modules share the same global data (e.g., a global variable). Changing the shared resource implies changing all the modules using it. External coupling External coupling occurs when two modules share an externally imposed data format, communication protocol, or device interface.This is basically related to the communication to external tools and devices. Control coupling Control coupling is one module controlling the flow of another, by passing it information on what to do (e.g., passing a what-to-do flag). Stamp coupling (Data-structured coupling) Stamp coupling is when modules share a composite data structure and use only a part of it, possibly a different part (e.g., passing a whole record to a function that only needs one field of it). This may lead to changing the way a module reads a record because a field that the module doesn't need has been modified. Data coupling Data coupling is when modules share data through, for example, parameters. Each datum is an elementary piece, and these are the only data shared (e.g., passing an integer to a function that computes a square root). Message coupling (low) This is the loosest type of coupling. It can be achieved by state decentralization (as in objects) and component communication is done via parameters or message passing (see Message passing ).
Content coupling (high) Content coupling is when one module modifies or relies on the internal workings of another module (e.g., accessing local data of another module). Therefore changing the way the second module produces data (location, type, timing) will lead to changing the dependent module. Common coupling Common coupling is when two modules share the same global data (e.g., a global variable). Changing the shared resource implies changing all the modules using it. External coupling External coupling occurs when two modules share an externally imposed data format, communication protocol, or device interface.This is basically related to the communication to external tools and devices. Control coupling Control coupling is one module controlling the flow of another, by passing it information on what to do (e.g., passing a what-to-do flag). Stamp coupling (Data-structured coupling) Stamp coupling is when modules share a composite data structure and use only a part of it, possibly a different part (e.g., passing a whole record to a function that only needs one field of it). This may lead to changing the way a module reads a record because a field that the module doesn't need has been modified. Data coupling Data coupling is when modules share data through, for example, parameters. Each datum is an elementary piece, and these are the only data shared (e.g., passing an integer to a function that computes a square root). Message coupling (low) This is the loosest type of coupling. It can be achieved by state decentralization (as in objects) and component communication is done via parameters or message passing (see Message passing ).
Alistair Cockburn defined Protected Variation before knowing about OCP Craig Larman explains differences here: http://im.ufba.br/pub/MATA63/Documentos/ProtectedVariation%5B1%5D.pdf
With modern tools, TDD, automated refactoring tools, etc, I would not be too bothered. http://im.ufba.br/pub/MATA63/Documentos/ProtectedVariation%5B1%5D.pdf
What about encapsulation? How can we write tests for this?
What about encapsulation? How can we write tests for this?
With modern tools, TDD, automated refactoring tools, etc, I would not be too bothered. http://im.ufba.br/pub/MATA63/Documentos/ProtectedVariation%5B1%5D.pdf