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

Learn these before learning design patterns

Learn these before learning design patterns

Presentation about OOD principles held for colleagues.

dan-homorodean

November 26, 2015
Tweet

More Decks by dan-homorodean

Other Decks in Technology

Transcript

  1. The problem is that... • When learning OOP people usually

    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!
  2. The solution is... • There are some principles and best

    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.”
  3. Some prerequisites for reasoning • Let's decompose the concepts –

    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
  4. Back to concepts ➔ Inheritance ➔ Abstraction ➔ Encapsulation ➔

    Polymorphism OH NO! He gets all theoretic about it! NO! On the contrary, We'll see them applied this time!
  5. Regarding inheritance • Classes inherit • State and/or Behavior (nothing

    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
  6. Inheritance traps • If you plan to use traditional class

    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.
  7. Regarding encapsulation • Remember: What you hide you can change.

    • Aspects of encapsulation: 1. Un-encapsulated code pitfalls 2. Encapsulating members identity 3. Self-encapsulating members 4. Preventing mutation or access 5. Encapsulating reference properties 6. Avoiding getter encapsulation breakage 7. Final thoughts
  8. Un-encapsulated code pitfalls • This means public properties – Class

    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; }
  9. Encapsulating members identity • This means adding setters/getters (mutators/accessors) •

    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; } }
  10. Self-encapsulating members (1) • This means hiding a class' own

    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.
  11. Self-encapsulating members (2) • The version of code using self-encapsulation

    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.
  12. Preventing mutation or access • Using both a setter and

    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
  13. Encapsulating reference properties (1) interface BonusCalculator { function computeBonus(Employee $employee);

    } class SeniorityBonus implements BonusCalculator { private $baseBonus; function __construct($baseBonus){ $this->baseBonus = $baseBonus; } function setBaseBonus($bonus){ $this->baseBonus = $bonus; } function computeBonus(Employee $employee){ $seniority = $employee->getSeniority(); return $this->baseBonus + $seniority * $this->baseBonus * 0.05; } }
  14. Encapsulating reference properties (2) class Employee { private $bonusCalculator; private

    $seniority; function __construct($seniority, BonusCalculator $bonusCalculator){ $this->seniority = (float) $seniority; $this->bonusCalculator = $bonusCalculator; } function computeBonus(){ return $this->bonusCalculator->computeBonus($this); } }
  15. Encapsulating reference properties (3) // seniority base bonus of 100

    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 !!!
  16. Encapsulating reference properties (4) class Employee { private $bonusCalculator; private

    $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!
  17. Avoiding getter/setter encapsulation breakage (1) class Employee { private $bonusCalculator;

    //... // simple getter implementation just returns the property → function getBonusCalculator(){ return $this->bonusCalculator; } // ... } // employee's bonus calculator has a base bonus = 100 $bonusCalculator = $employee->getBonusCalculator(); // prepare calculator for another employee $bonusCalculator->setBaseBonus(110); $anotherEmployee = new Employee(20, $bonusCalculator); // ... somewhere else in the code print $employee->computeBonus(); // 155, WRONG BONUS !!!
  18. Avoiding getter/setter encapsulation breakage (2) class Employee { private $bonusCalculator;

    // 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
  19. Final thoughts on encapsulation • Just adding setters and getters

    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.
  20. A few words about reuse and maintenance • Reuse –

    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.
  21. Next, to principles • Two kinds – Class design principles

    – 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
  22. It's SOLID, but is it solid enough ? • We

    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 … ?
  23. Just to remember ... • SRP – a class should

    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
  24. Composite Reuse Principle • Statement: Favor polymorphic composition of objects

    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
  25. CRP Example Reuse through inheritance Note: What if we want

    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
  26. CRP Example Reuse through composition Advantages: - we can dynamically

    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!
  27. Principle of Least Knowledge (Law of Demeter) • Statement: each

    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
  28. LoD – bad example (1) class AppointmentRegistry { private $appointments;

    function getAppointments() { //... } function removeAppointment(Appointment $appointment) { //... } } class Appointment { private $date; private $time; private $specialty; private $pacient; // ... setters and getters }
  29. LoD – bad example (2) class PracticeOffice { private $appointmentRegistry;

    function getAppointmentRegistry() {/* … */} } class Pacient { private $code; function getAppointmentsByDate(PracticeOffice $practiceOffice, $date) { $all = $practiceOffice ->getAppointmentRegistry() ->getAppointments(); $selected = array(); foreach ($all as $current) { if ($current->getDate() == $date && $current->getPacient() == $this) { $selected[] = $current; } } return $selected; } // ...
  30. LoD – bad example (3) // ... function cancelAppointmentsByDate(PracticeOffice $practiceOffice,

    $date) { $appointments = $this->getAppointmentsByDate($practiceOffice, $date); foreach ($appointments as $appointment) { $practiceOffice ->getAppointmentRegistry() ->removeAppointment($appointment); } } }
  31. LoD – good example (1) class Pacient { private $code;

    function getAppointmentsByDate(PracticeOffice $practiceOffice, $date) { return $practiceOffice->getPacientAppointmentsByDate($this, $date); } function cancelAppointmentsByDate(PracticeOffice $practiceOffice, $date) { $practiceOffice->cancelPacientAppointmentsByDate($this, $date); } }
  32. LoD – good example (2) class PracticeOffice { private $appointmentRegistry;

    function providePacientAppointmentsByDate(Pacient $pacient, $date) { $appointments = $appointmentRegistry->getAppointments(); $selectedAppointments = array(); foreach ($appointments as $current) { if ($current->getDate() == $date && $current->getPacient() == $pacient) { $selectedAppointments[] = $current; } } return $selectedAppointments; } function cancelPacientAppointmentsByDate(Pacient $pacient, $date) { $appointments = $appointmentRegistry->getAppointments(); foreach ($appointments as $current) { if ($current->getDate() == $date && $current->getPacient() == $pacient) { $appointmentRegistry->removeAppointment($current); } } } } PracticeOffice becomes a facade
  33. Code qualities • There are at least 6 code qualities

    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
  34. Code qualities • There are at least 6 code qualities

    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
  35. Code qualities • There are at least 6 code qualities

    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
  36. Code qualities • There are at least 6 code qualities

    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
  37. Code qualities • There are at least 6 code qualities

    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
  38. Code qualities • There are at least 6 code qualities

    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.
  39. OOP and Information Flow • Almost every line of object

    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
  40. Atoms of inter-objects communication • The basic idea of OOP

    : 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)
  41. Atoms of inter-objects communication • The basic idea of OOP

    : 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
  42. Atoms of inter-objects communication • The basic idea of OOP

    : 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
  43. Going further ... • Can we infer some general object

    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
  44. Closing notes • Object Oriented Design – “Is the process

    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.
  45. Sources • wikipedia.org → principles and patterns • netobjectives.com →

    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