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
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
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!
CLOSED for MODIFICATION Divide ’n Conquer: logical mergesort Divide problem to subproblems Divide subproblems to subsubproblems Divide until the solution is a single class
} 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()){} } }
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!
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
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 { }
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
= 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>>
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: