jump from “Horse extends Animal” examples to design patterns way too early. – For most of them this is simply confusing or rather useless at that stage. So it represents an obstacle in understanding OOD (Object Oriented Design) and doing it freely, naturally. • There are so many patterns available today that attempting to find one that solves your design challenges could take longer than discovering an efficient solution by yourself. • But how can you find it yourself if you aren't designing an animal farm application or a geometrical shapes application?! If you are developing such apps, lucky you!
practices about OOP and OOD a programmer needs to firmly grasp before he or she jumps into applying design patterns. • These are called principles of object orientation • - “Are you talking about these Object Oriented Programming principles ?” • Inheritance • Abstraction • Encapsulation • Polymorphism • - “NO, these are just concepts, and are good for exams or interviews questions, but just enumerating them doesn't offer you much support in designing the application.”
Classes define/Objects have: • State : their properties or attributes • Behavior : their methods – Classes bind together: • Implementation : physical code that does something – the HOW • Specification : a promise to do something, a contract – the WHAT
new here, we knew this) • Implementation and/or specification • Three types of inheritance for a class • Traditional : extending a class – class inherits both Implementation and Specification • Interface : implementing an interface – class inherits pure Specification • Logic : using a trait – class inherits pure Implementation
inheritance to only have in child: – (most of) the parent's specification → you'll also get the implementation • To have a fairly different implementation in child → a lot of method overrides are needed • Solution: what you need is to extract an interface from the old class and make both the old and the new classes implement it – (most of) the parent's implementation → you'll also get the specification • To have a fairly different specification (set of contracts) in child → child class ends doing multiple things (has multiple responsibilities), which gives a bad smell to the code • Solution 1: extract that atomic logic from the old class into another class and make the old and new classes aggregate an instance of that class and delegate tasks to it. • Solution 2: in languages that allow horizontal inheritance (for example traits in PHP) extract that implementation into a trait and use the trait in both classes. • Rule of thumb: if it seems it can be achieved using class inheritance then it might need a refactoring of the existing code.
public properties → similar to global variables – Instance public properties → not as global as class properties but still • Downsides – Any class in the application can depend on them → tight coupling – Any class in the application can change them → anomalies / bugs class Employee { public static $maximumSeniority; public static $minimumSeniority; public $seniority; public $bonusCalculator; }
Advantages over public property case – No other class couples to the name and type of the property → it can change without side effects • Example: change $seniority representation from years to months so that some class logic becomes simpler, but continue exposing it as years – Setter → can be used to impose validation, type cast, save to external storage – Getter → can be used to format data or to get data on the fly from external storage class Employee { private $seniority; public function getSeniority(){ return $this->seniority; } public function setSeniority($seniority){ $this->seniority = $seniority; } }
properties from it's algorithms/logic. class Employee { private $seniority; function getSeniorityGrade(){ $months = $this->seniority * 12; return (int)($months / 10); } } Assumes $this->seniority contains a number of years. What if implementation needs to change so that seniority holds months? All computation methods of the class must be revised.
of the property class Employee { // name changed, now holds months private $seniorityMonths; // implementation changed, still returns years public function getSeniority(){ return (int)($this->seniorityMonths / 12); } function getSeniorityGrade(){ // no changes were required here $months = $this->getSeniority() * 12; return (int)($months / 10); } } This method remains the same no matter what type of time quantity the seniority related property really holds. The getter will return always years, because that's its contract.
a getter on the same property can lead to dependencies hard to spot – Scenario: Some object calls the setter to set a valid value for the property and the some other object calls the getter of that property assuming that the property was previously set – Simply put: The second object depends on the first one to set a valid value before it uses that property's value. • Preventing mutation → write only the getter, making the property read-only – Apply this for all properties and the object becomes immutable (value-object) • Preventing access → write only the setter, making the property write-only – This scheme can be applied where you want just to make an object configurable by others → set values are used internally by that object in some algorithms
EUR $bonusCalculator = new SeniorityBonus(100); // employee has worked 10 years for employer $employee = new Employee(10, $bonusCalculator); print $employee->computeBonus(); // 150, OK // … // somewhere else in the code (maybe a cycle) // changed by mistake to suit new employee $bonusCalculator->setBaseBonus(110); $other = new Employee(20, $bonusCalculator); // … // somewhere else the bonus amount is needed for $employee print $employee->computeBonus(); // 155, WRONG BONUS !!!
$seniority; function __construct($seniority, BonusCalculator $bonusCalculator){ $this->seniority = (float) $seniority; $this->bonusCalculator = clone $bonusCalculator; } } $bonusCalculator = new SeniorityBonus(100); $employee = new Employee(10, $bonusCalculator); // $employee has now a clone of the SeniorityBonus instance print $employee->computeBonus(); // 150, OK /... $bonusCalculator->setBaseBonus(110); $other = new Employee(20, $bonusCalculator); //... print $employee->computeBonus(); // 150, OK Same thing with reference property setters, too!
// simple getter implementation just returns the property → function getBonusCalculator(){ return clone $this->bonusCalculator; } // … the rest of the code } // employee's bonus calculator has a base bonus = 100 $bonusCalculator = $employee->getBonusCalculator(); // returns reference to cloned // prepare calculator for another employee $bonusCalculator->setBaseBonus(110); $anotherEmployee = new Employee(20, $bonusCalculator); // ... somewhere else in the code print $employee->computeBonus(); // 150, OK
to a class for its private and protected properties is not enough! • Case analysis for property types WHEN / WHAT Property value type scalar Property value type array Property value type object Allow passing properties values to a constructor Encap. Granted Optional: validation, cast, transformation Use only for an options argument. Avoid object elements or make defensive copies. Encap. not granted unless defensive copy is saved Provide getter for a hidden property Encap. Granted Optional: cast transformation, formatting Better not to return an array from a property getter. Return Iterator instead. Encap. not granted unless defensive copy is returned Provide setter for a hidden property Encap. Granted Optional: validation, cast transformation Better not to set an array at once. Use an addAll(Iterator $iterable) or addOne($elem) and enforce validation / checking on each. Encap. not granted unless defensive copy is saved Note: for resource type is better not to provide accessor and mutator. Encapsulate it completely.
make use of code once written – Advertised as the “Holy Grail of object orientation, the Nirvana of programming” • Maintenance (Maintainability) – (ease of) the process of adapting a system to requirements change – A study stated that for every dollar spent in development there are two dollars spent in maintenance (Walker Royce, The Rational Edge) • Reuse and maintenance are in some situations contradictory – Dependency comes with reuse as a bonus, and complicated dependencies are difficult to maintain in the long run. • Conclusion • Reuse is important but maintainability of code is more important. • Don't waste time in figuring out how to reuse the most of any object, instead use that time to plan for change.
– Package design principles • Definition of a 'principle' – a fundamental, primary, or general law or truth from which others are derived → building blocks for object oriented design
all know about SOLID, we remember it because it is such a powerful acronym. – “It's there any cost of being so easy to remember?” – “Yes! They left some letters out, and important ones too!” • S – SRP – Single Responsibility Principle • O – OCP – Open-Closed Principle • L – LSP – Liskov Substitution Principle • I – ISP – Interface Segregation Principle • D – DIP – Dependency Inversion Principle • C – CRP – Composite Reuse Principle • P – PLK – Principle of Least Knowledge • Others … ?
have only a single responsibility • OCP – classes (code) should be open for extension, but closed for modification • LSP – objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program • ISP – many client-specific interfaces are better than one general-purpose interface • DIP – one should depend upon abstractions; do not depend upon concretions
over inheritance. • Variant: Delegation rather than inheritance as a reuse mechanism. • Common mistake: to consider the class inheritance as the primary mechanism of reuse. • 3 reuse mechanisms: – Inheritance – vertical reuse – Composition/Delegation – Traits (or similar) – horizontal reuse
different bonuses for different programmers (junior, mid, senior)? Should we extend the Programmer class just to override computeBonus() in different ways? NO! Is the bonus aspect enough reason to expand the hierarchy? NO! Task: give bonuses to employees according to their position in organization. Inheritance based solution: extend Employee
at runtime give personalized bonuses to employees having the same position. - we can also go further and apply a Composite Pattern that would allow us to combine bonus types for an employee...suddenly sky is the limit!
unit should only talk to its immediate friends; don't talk to strangers. • LoD applied to OOD translates to: – Unit = method F ( within object O ) – Immediate friends = methods belonging to the object: • O (the $this in F) • which is a property of O • which is created in F • which is an argument of method F • returned by a method of O
that we should look for ( NetObjectives – Code Qualities and Practices , misko.hevery.com/code-reviewers-guide ) Testability No Redundancy Encapsulation Correct Coupling Strong Cohesion Readability Quantifies how easy to unit test are your classes. In order to have good testability here is an open list of best practices : • Apply object oriented design principles (SOLID + CP) • Avoid using singletons, registries or service locators • Avoid using static fields and static methods • Avoid the use of initialize methods • Avoid use on new keyword in constructors • Avoid anything other than property assignment in constructor • Avoid conditionals or loops in a constructor • Favor Dependency Injection
that we should look for ( NetObjectives – Code Qualities and Practices , misko.hevery.com/code-reviewers-guide ) Testability No Redundancy Encapsulation Correct Coupling Strong Cohesion Readability • Warning signs • Code becomes fragile • Code becomes hard to maintain and extend • Code becomes hard to read • What to do • Avoid copy-paste, instead refactor out common behavior into a new method or class • Redundancy can appear due to design flaws and not only copy-paste, so put effort into good design • No redundancy is difficult to achieve
that we should look for ( NetObjectives – Code Qualities and Practices , misko.hevery.com/code-reviewers-guide ) Testability No Redundancy Encapsulation Correct Coupling Strong Cohesion Readability • Warning sign: • When you must know how the code you are using is implanted in order to use it properly. • See the previous discussion about encapsulation
that we should look for ( NetObjectives – Code Qualities and Practices , misko.hevery.com/code-reviewers-guide ) Testability No Redundancy Encapsulation Correct Coupling Strong Cohesion Readability • Warning signs: • Classes that do multiple, unrelated things • Methods that do unrelated things • Presence of “god objects” or omniscient objects • What to do: • Apply Single Responsibility Principle to both classes and methods
that we should look for ( NetObjectives – Code Qualities and Practices , misko.hevery.com/code-reviewers-guide ) Testability No Redundancy Encapsulation Correct Coupling Strong Cohesion Readability • Warning signs: • Occurrence of side-effects : unexpected errors arise due to making changes elsewhere • What to do: • Apply Dependency Inversion Principle : depend upon abstractions instead of concretions • Apply Composite Reuse Principle : favor composition over inheritance → leads to Strategy pattern • Apply Principle of Least Knowledge : do not talk to strangers
that we should look for ( NetObjectives – Code Qualities and Practices , misko.hevery.com/code-reviewers-guide ) Testability No Redundancy Encapsulation Correct Coupling Strong Cohesion Readability • The easiest to spot is the lack of readability • What to do: • Use some coding practices that produce more readable code, such as Programming by Intention. • Avoid poor naming. • Use nouns and verbs wisely. • Length of names should be proportional with the size of the scope. • Avoid big classes and big methods. Break them into atomic meaningful smaller constructs.
oriented code we write controls information flow. • The big picture / breakdown Messages mixes Object Oriented Programming Information Control means means Some haters say that all we do is CRUD. Are they right? It seems that way, therefore let's do it with style at least! Method arguments and return Object methods Logic
: objects communicate exchanging messages • Let – Logic be represented by $obj – Message be represented by $msg • Then we have these three possible communication patterns – ASK $msg = $obj->get() – TELL $obj->set($msg) – TRANSLATE $msg2 = $obj->do($msg1)
: objects communicate exchanging messages • Let – Logic be represented by $obj – Message be represented by $msg • Then we have these three possible communication patterns – ASK $msg = $obj->get() – TELL $obj->set($msg) – TRANSLATE $msg2 = $obj->do($msg1) ASK and TELL tend to favor message over logic
: objects communicate exchanging messages • Let – Logic be represented by $obj – Message be represented by $msg • Then we have these three possible communication patterns – ASK $msg = $obj->get() – TELL $obj->set($msg) – TRANSLATE $msg2 = $obj->do($msg1) ASK and TELL tend to favor message over logic TRANSLATE tends to favor logic over message
roles starting from these communication patterns ? • We can observe 5 recurring roles (Anthony Ferrara) : Emphasis on STATE ? Emphasis on LOGIC ? Communication patterns Examples Representer (represents a thing) YES User defined state NO ASK TELL Domain entities Doer (does something) NO YES ASK Email sender, Logger, Service … Translator (takes some values and returns other) NO YES TRANSLATE Array utils, String utils, Date formatter, ... Dispatcher (controls flow) YES System defined state NO TRANSLATE Front Controller, Event Manager Maker (makes other objects) YES System defined state YES ASK Factory
of planning a system of interacting objects for the purpose of solving a software problem” - Wikipedia – It is not about choosing the correct design pattern, but about planning the best interaction between objects • Design patterns are important and should be learned and used, but they must lay on a solid foundation of object oriented knowledge.
agile design and patterns articles • blog.ircmaxell.com → Anthony Ferrara's blog • misko.hevery.com/code-reviewers-guide → testability as a code quality • google.com → graphics