Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The SOLID principles of OO

The SOLID principles of OO

or the rant* of post-OO programmer
A simple summary of SOLID principles w/ very simple examples in Java

-- http://peel.github.io

Piotr Limanowski

May 26, 2010
Tweet

More Decks by Piotr Limanowski

Other Decks in Programming

Transcript

  1. The SOLID principles of OO or the rant* of post-OO

    developer Piotr Limanowski KAOS / Burncity not an angry, bemused or dissapointed rant - just a rant
  2. Let’s roll... why even bother? Software rots like bad meat:

    Rigidity - cascading changes Fragility - problems in new areas with no direct relations Immobility - no possibility of reuse of a „reusable” code Complexity - overdesign; complex to understand Repetition - duplication, oh gawd Opacity - not human redable Broken Window Theory Broken windows drives a building into a smahsed and abandoned derelict
  3. Let’s roll... the SOLID Principles Single Responsibility Open Closed Liskov

    Substitution Interface Segregation Dependency Inversion
  4. There is one and only one reason to change a

    class President / Gov’t / Local Divide ’n Conquer: logical mergesort Divide problem to subproblems Divide subproblems to subsubproblems Divide until the solution is a single class
  5. INVALID public abstract class LibraryUser{ double balance; ArrayList<Book> rent; void

    payDue(double amount); void checkIn(Book b){} void checkOut(Book b){} } VALID public class LibraryUser{ private LibraryAccount account; public void LibraryUser(LibraryAccount account){ this.account=account; } public void checkOut(Book book){ account.prolong(); } public void checkIn(Book book){} public void upgradeAccount(LibraryAccount account){ this.account=account; } } interface LibraryAccount{ public void checkOut(Book book); public void checkIn(Book book); public void upgradeAccount(LibraryAccount account); } public class PaidAccount implements LibraryAccount{ public void checkOut(Book book){payDue()(...)} public void checkIn(Book book){} public void upgradeAccount(LibraryAccount account){} public void payDue(double due); } public class FreeAccount implements LibraryAccount{ public void checkOut(Book book){} public void checkIn(Book book){} public void upgradeAccount(LibraryAccount account){} } STRATEGY <<interface>> DETAILED STRATEGY <<class>> DETAILED STRATEGY <<class>> CONTEXT <<class>>
  6. Divide and conquer design Strategy Pattern Template Pattern If a

    single class (method) answers too many „Zachman” questions (how, where, what, why etc.), it’s a good indication that this class has too many responsibilities. THERE IS ONE AND ONLY ONE REASON TO CHANGE A CLASSFOR F@#$ SAKE!
  7. Software entities (classes, modules, functions) should be OPEN for EXTENSION,

    CLOSED for MODIFICATION Divide ’n Conquer: logical mergesort Divide problem to subproblems Divide subproblems to subsubproblems Divide until the solution is a single class
  8. INVALID public class LoanApprovalHandler{ public void approveLoan(PersonalValidator v){ if(validator.isValid()){} }

    } public class PersonalLoanValidator{ public boolean isValid(){} } public class LoanApprovalHandler{ public void approvePersonalLoan(PersonalLoanValidator v){ if(validator.isValid()){} } public void approveVehicleLoan (VehicleLoanValidator v){ if(validator.isValid()){} } //keep them comming } public class PersonalLoanValidator{ public boolean isValid(){} } public class VehicleLoanValidator{ public boolean isValid(){} } VALID public abstract class Validator{ public boolean isValid(); } public class PersonalLoanValidator extends Validator{ public boolean isValid(){} } public class VehicleLoanValidator extends Validator{ public boolean isValid(){} } public class LoanApprovalHandler{ public void approveLoan(Validator validator){ if ( validator.isValid()){} } }
  9. Making all member variables private so that the other parts

    of the code access them via the getters not directly. Avoiding typecasts at runtime - makes the code fragile and dependent on the classes under consideration, which means any new class might require editing the method to accommodate the cast for the new class. YOU SHOULD BE ABLE TO EXTEND A CLASSES’ BEHAVIOUR, WITHOUT MODIFYING IT HOLY RUNAWAY CODE, BATMAN!
  10. LISKOV SUBSTITUTION IF IT LOOKS LIKE A DUCK AND QUACKS

    LIKE A DUCK BUT NEEDS BATTERIES, YOU PROBABLY HAVE THE WRONG ABSTRACTION.
  11. Let q(x) be a property provable about objects x of

    type T. Then q(y) should be true for objects y of type S where S is a subtype of T. Changing the implementation doesn’t affect client’s code
  12. INVALID public class App { ! public static void main(String[]

    args) { ! ! // kod użytkownika ! ! String abc = "abc"; ! ! TextFilter filter = new AFilter(); ! ! System.out.println(filter.procced(abc)); ! ! filter = new BFilter(); ! ! System.out.println(filter.procced(abc)); ! } } // nasz kod interface TextFilter { ! public String procced(String text); } class AFilter implements TextFilter { ! public String procced(String text) { ! ! return text.replaceAll("a", ""); ! } } class BFilter implements TextFilter { ! public String procced(String text) { ! ! return text.replaceAll("b", ""); ! } } VALID public class App { ! public static void main(String[] args) { ! ! // kod użytkownika ! ! String abc = "abc"; ! ! TextFilter filter = new AFilter(); ! ! System.out.println(filter.procced(abc)); ! ! filter = new BFilter(); ! ! System.out.println(filter.procced(abc)); ! } } interface TextFilter { ! @Deprecated ! public String procced(String text); ! public String proccedAndThrow(String text) throws Exception; } class AFilter implements TextFilter { ! @Deprecated ! public String procced(String text) { ! ! return text.replaceAll("a", ""); ! } ! public String proccedAndThrow(String text) throws Exception { ! ! return text.replaceAll("a", ""); ! } } class BFilter implements TextFilter { ! @Deprecated ! public String procced(String text) { ! ! return text.replaceAll("b", ""); ! } ! public String proccedAndThrow(String text) throws Exception { ! ! return procced(text); ! } }
  13. Let q(x) be a property provable about objects x of

    type T. Then q(y) should be true for objects y of type S where S is a subtype of T. Changing the implementation doesn’t affect client’s code Number of interfaces (implementation is slower than inheritance) Factory pattern Unit testing Documentation
  14. INVALID public interface Worker { ! public void work(); public

    void receivePaycheck(); } public interface Manager { ! public void addWorker(Worker w); ! public void manage(); } public interface Accountant { ! public void enlist(Worker w); ! public void submitPaycheck(); } VALID public interface Worker { public void worker(); } public interface PaidWorker { public void receivePaycheck(); } public interface Manager { ! public void addWorker(Worker w); ! public void manage(); } public interface Accountant { ! public void enlist(Worker w); ! public void submitPaycheck(); } public interface WorkingMan extends PaidWorker, Worker { }
  15. Think in terms of the client not number of lines

    Factoring MAKE FINE GRAINED INTERFACES THAT ARE NT SPECIFIC SIMPLE AS THAT:
  16. High level modules should not depend upon low level modules.

    Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions. modules? abstractions? in Java? C++? or... is it AOP modules? DDD? No more spiderwebs = bad design Hard to write unit tests = bad design SRP lots of dependencies class is like another class that does the same but produces different output Examples: Battery chargers LogFiles gathering
  17. INVALID public class OrderProcessor{ public double CalculateTotal(Order order){ double itemTotal

    = order.getItemTotal(); double discount = DiscountCalc.calculateDiscount(order); double tax = 0.0; if (order.country == "US"){ tax = findTaxAmount(order); } else if (order.country == "UK"){ tax = findVatAmount(order); } double total=itemTotal-discount+tax; return total; } private double findVatAmount(Order order){ return 10.0; } private decimal FindTaxAmount(Order order){ return 10.0; } VALID public interface DiscountCalculator{ double calculateDiscount(Order order); } public interface TaxStrategy{ double findTaxAmount(Order order); } public class OrderProcessor{ private static final DiscountCalculator discountCalculator; private static final TaxStrategy taxStrategy; public OrderProcessor(DiscountCalculator discountCalculator, TaxStrategy taxStrategy){ this.taxStrategy = taxStrategy; this.discountCalculator = discountCalculator; } public double CalculateTotal(Order order){ double itemTotal=order.getItemTotal(); double discountAmount=DiscountCalc.CalculateDiscount(order); double tax=taxStrategy.findTaxAmount(order); double total=itemTotal-discountAmount+taxAmount; return total; } } public class DiscountCalculatorAdapter implements DiscountCalculator{ public double CalculateDiscount(Order order){ return DiscountCalculator.CalculateDiscount(order); } } public class USTaxStrategy implements TaxStrategy{ public decimal FindTaxAmount(Order order){ } } public class UKTaxStrategy implements TaxStrategy{ public decimal FindTaxAmount(Order order){ } } ▪ How to calculate the item total ▪ Finding the discount calculator and finding the discount ▪ Knowing what country codes mean ▪ Finding the correct taxing method for each country code ▪ Knowing how to calculate tax for each country ▪ Knowing how to combine all of the results into the correct final total ADAPTER <<class>>
  18. Always isolate the ugly stuff IoC containers: Google Guice Spring

    etc. One way to think of this is that lower level modules provide a service to higher level modules. The higher level modules specifies the interfaces for that service and the lower level module provides that service. DEPEND ON ABSTRACTIONS NOT NEVER NAY ONCRETIONS THE RULE IS: