OO Design Principles Software Architecture VO (706.706) Roman Kern Institute for Interactive Systems and Data Science, TU Graz 2018-10-10 Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 1 / 69
Outline 1 Introduction 2 Open-Closed Principle 3 Liskov Substitution Princple 4 Single Responsibility Principle 5 Law of Demeter 6 Enterprise Architecture Principles Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 2 / 69
Introduction Overview of OO principles Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 3 / 69
Software Design Design is the process of inventing new software entities to satisfy requirements Software entities might be different things depending on the level of granularity and programming paradigm Procedural programming: procedures, variables, OO programming: classes, objects, relations between them, Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 4 / 69
Software Design Design is not an exact entity For example, you can calculate how fast is an algorithm But you cannot measure how good a design is However, there are certain design principles and design rules that you should follow Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 5 / 69
Software Design These design principles come from the experience of numerous programmers Some of the experiences can be built directly into a programming language E.g. absence of direct multiple inheritance in Java It was a design decision to leave it out of the language because of problems it might cause Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 6 / 69
OO design principles The design principles that we will discuss lay the foundation of solid OO practices: 1 Open-Closed principle 2 Design by contract/liskov substitution principle 3 Single responsibility principle 4 Law of Demeter Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 7 / 69
Open-Closed Principle Open for extension, closed for modification Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 8 / 69
Open-Closed Principle: Example Online publication system An online publication system allows users to search in publications by the year of publication. Search functionality is supported by a search filter that filters publications by comparing their publication year with a user query. Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 9 / 69
Open-Closed Principle: Example public class Filter { public List filteronyearofpublication(collection pubs, int year) { List<Publication> filtered = new ArrayList<Publication>(); for (Publication pub : pubs) { if (pub.getyearofpublication() == year) { filtered.add(pub); return filtered; Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 10 / 69
Open-Closed Principle: Example Online publication system Extend the search filter to also filter by the title. Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 11 / 69
Open-Closed Principle: Example public class Filter {... public List filterontitleofpublication(collection pubs, String title) { List<Publication> filtered = new ArrayList<Publication>(); for (Publication pub : pubs) { if (pub.gettitleofpublication().equals(title)) { filtered.add(pub); return filtered; Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 12 / 69
Open-Closed Principle This is the cornerstone principle of OOD All software changes during its life cycle Programmers face ever changing functional requirements But need to design software that is stable (i.e. does not change) Software entities should be open for extension, but closed for modification by B. Meyer Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 13 / 69
Open-Closed Principle In OO terms Classes should be open for extension, but closed for modification In other words you should be able to extend a class without modifying it Open for extension: behaviour of classes can be extended to satisfy changing requirements Closed for modification: classes themselves are not allowed to change Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 14 / 69
Open-Closed Principle At first this seems contradictory If you need a modified behaviour of a class just change that class This is however wrong The answer here is abstraction In OO it is possible to create abstractions with fixed design but limitless behaviours Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 15 / 69
Open-Closed Principle Abstractions through inheritance - changed behaviour through polymorphism Inheritance: abstract base classes or interfaces You must decide early on in the design which parts of the system will be expanded later and which will stay fixed Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 16 / 69
Open-Closed Principle The design is extended by adding new code and classes by inheriting existing base classes Modifying the existing classes is not needed Completed and tested code is declared closed, and it is never modified Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 17 / 69
Open-Closed Principle: Example We violated the OCP before To change the behaviour we changed the base class Both methods differ only in one statement: selection statement This is where we need to design for change We need to refactor Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 18 / 69
Open-Closed Principle: Example public class Filter { public Vector filterpublication(collection pubs, Selector selector) { List<Publication> filtered = new ArrayList<Publication>(); for (Publication pub : pubs) { if (selector.ispubselected(pub)) { filtered.add(pub); return filtered; Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 19 / 69
Open-Closed Principle: Example public interface Selector { public boolean ispubselected(publication pub); Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 20 / 69
Open-Closed Principle: Example public class YearSelector implements Selector { private final int year; public YearSelector(int year) { this.year = year; public boolean ispubselected(publication pub) { if (pub.getyearofpublication() == year) { return true; return false; Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 21 / 69
Open-Closed Principle: Example public class TitleSelector implements Selector { private final String title; public TitleSelector(String title) { this.title = title; public boolean ispubselected(publication pub) { if (pub.gettitleofpublication().equals(title)) { return true; return false; Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 22 / 69
Open-Closed Principle Now filtering is open for extension, i.e. adding filters for author, pub type Still filtering is closed for modification - no need to modify the Filter class With the class being open for extension, it is highly reusable and can be used to satisfy a ever changing list of requirements The class is extremely maintainable, as it never needs to change Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 23 / 69
Disadvantages of instanceof instanceof operator in Java checks whether an object is of a certain type Typical use of this operator is in if-elseif-else or switch type statement Depending on the type of an object you initiate actions Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 24 / 69
Disadvantages of instanceof... public void cookpizza(pizza pizza) { if (pizza instanceof ThinCrustPizza) { ((ThinCrustPizza)pizza).cookInWoodFireOven(); else if (pizza instanceof PanPizza) { ((PanPizza)pizza).cookInGreasyPan();... Note: At least have an else throw new IllegalArgumentException(); Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 25 / 69
Disadvantages of instanceof This is a direct violation of OCP This class can not be extended without modifying it Suppose there is a new kind of pizza You need to extend the above class with a new elseif that handles the new type Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 26 / 69
Disadvantages of instanceof You can always design your classes so that they do not need instanceof operator The key again is inheritance + polymorphism In certain cases you will need an extra design construct But it pays off because you get the benefits of the OCP Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 27 / 69
Disadvantages of instanceof abstract public Pizza { public void cook() { placeoncookingsurface(); placeincookingdevice(); int cooktime = getcooktime(); letitcook(cooktime); removefromcookingdevice(); abstract public void placeincookingdevice();... Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 28 / 69
Disadvantages of instanceof... public void cookpizza(pizza pizza) { pizza.cook();... Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 29 / 69
Disadvantages of instanceof We applied an extra design construct here: Template Method cook() method defines a general cook algorithm It calls a number of template methods that are abstract Subclasses need to implement that behaviour by implementing template methods Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 30 / 69
Liskov Substitution Princple Preconditions, Postconditions & Invariants Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 31 / 69
Design by contract Developed by B. Meyer Its a contract between a class and its clients (other classes that use it) Specifies rules that need to be satisfied by the class and by the clients i.e., an expression that evaluates to true Preconditions and postconditions for methods, invariants for the class Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 32 / 69
Design by contract Example: Stack class Precondition for push(x) method: the stack is non-full Postcondition for push(x) method: x is on the top of the stack Invariants: stack size is non-negative, count is less than max_size, Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 33 / 69
Design by contract In some languages you can formally specify such rules With predicate logic you can even check the correctness of such rules Certain approaches to generate code from such specifications In Java: assert keyword to check if rules are satisfied: Testing Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 34 / 69
Liskov Substitution Principle Closely related with Design by Contract Depends on preconditions and postconditions Definition If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behaviour of P is unchanged when o1 is substituted for o2 then S is a subtype of T. Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 35 / 69
Liskov Substitution Principle What does it mean? Anywhere you specify a base type, you should be able to use an instance of a derived type. Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 36 / 69
Liskov Substitution Principle But with polymorphism we can do it anyway, right? That is true but here we are concerned with the program correctness We are concerned with the question what happens when subclasses reimplement (override) methods The overridden methods need to satisfy rules specified by the superclass Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 37 / 69
Liskov Substitution Principle public interface Coffee { public CaffeineContent getcaffeinecontent(); public class EthiopianCoffee { public CaffeineContent getcaffeinecontent(){ return new CaffeineContent(100); public class DecafCoffee { public CaffeineContent getcaffeinecontent(){ return null; Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 38 / 69
Liskov Substitution Principle Then this code will break in some cases (postcondition was not satisfied)... public void buycoffee(coffee coffee) { CaffeineContent caf = coffee.getcaffeinecontent(); if(caf.isstrong()) {...... Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 39 / 69
Liskov Substitution Principle But is DecafCoffee really Coffee? Maybe we need to reconsider (refactor) our class hierarchy For example, HotBeverage as superclass Coffee and DecafCoffee as subclasses at the same level of the hierarchy Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 40 / 69
Liskov Substitution Principle Rule: It is allowed to weaken preconditions and strengthen postconditions in overridden methods, but not vice versa If you can not satisfy this rule then you need to refactor the class hierarchy Typically, those two types should be in the same level of the hierarchy There is a superclass for both of these classes Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 41 / 69
Liskov Substitution Principle Bank Accounts A bank manages different types of accounts for its customers. There is a standard current account that can be closed only if the balance on that account is positive. To close another type of account, a so-called timed current account, the balance needs to be positive and it needs to be open for at least 6 months. Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 42 / 69
Liskov Substitution Principle public class CurrentAccount { protected double balance = 0.0; public boolean close() { if (balance > 0.0) { return true; return false;... Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 43 / 69
Liskov Substitution Principle public class TimedCurrentAccount extends CurrentAccount { private int date = 0; public boolean close() { if ((balance > 0.0) && (date > 180) { return true; return false; Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 44 / 69
Liskov Substitution Principle public class Customer {... public void manageaccount(currentaccount account) {...... account.close(); // dangerous Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 45 / 69
Liskov Substitution Principle Customer knows that the account balance is positive A TimedCurrentAccount however will not be always closed What is the problem here? We strengthened precondition It was: balance > 0 Now: balance > 0 + more than 6 months since we opened the account Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 46 / 69
Liskov Substitution Principle Well, TimedCurrentAccount is not really a subtype of CurrentAccount Refactor: Account is the superclass with close() method and stronger preconditions CurrentAccount is a subclass that weakens preconditions TimedCurrentAccount is a subclass with the same precondition We can substitute CurrentAcount and TimedCurrentAccount for Account But we can not substitute CurrentAccount and TimedCurrentAccount, nor should we! Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 47 / 69
Liskov Substitution Principle Collection library Implement a collection library with list and set collections. Both of the collections store arbitrary number of objects. Lists store objects in a sequence. Sets can not have duplicate objects. A collection can be copied into another collection. This includes copying of all objects from one collection to another collection. Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 48 / 69
Liskov Substitution Principle public class List {... public void add(object element) { elements[count++] = element; // copy arrays public Object get(int i){ return elements[i]; public boolean haselement(object element){...... Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 49 / 69
Liskov Substitution Principle... public void copy(list dest) { for(int i=0; i < elements.length; i++) { dest.add(elements[i]); Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 50 / 69
Liskov Substitution Principle public class Set extends List{... // we inherit other methods public void add(object element) { if(!haselement(element)) { elements[count++] = element; // copy arrays Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 51 / 69
Liskov Substitution Principle Postcondition: all objects should be copied Postcondition of copy() method is violated We have a list: 1, 2, 3, 1, 2 When we copy it into a set: 1, 2, 3 Violation! Set is not really a List! Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 52 / 69
Liskov Substitution Principle Consequence of LSP You should be very careful with overriding Very often you will need to refactor the class hierarchy Overriding is inheritance with reuse of implementation Recollect inheritance should be reuse of interface Better: use abstract base classes and interfaces to ensure reuse of interface Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 53 / 69
Single Responsibility Principle Do one thing only Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 54 / 69
Single Responsibility Principle An entity (an object, a class) should have only a single responsibility A responsibility is in fact a reason to change An entity should have only single reason to change Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 55 / 69
Single Responsibility Principle public class Student {... public void readstudent(int id) { // read student data from a database... public String printhtml() { // print student data in a nice HTML format... Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 56 / 69
Single Responsibility Principle Student class has two responsibilities: reading from a database and printing HTML Two reasons for change: if a table is modified and if another HTML format is needed We need to separate responsibilities: reading and printing This is also very often called: separation of concerns Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 57 / 69
Single Responsibility Principle public class Student {... public void readstudent(int id) { // read student data from a database... Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 58 / 69
Single Responsibility Principle public class StudentPrinter {... public String printstudent(student student) { // print in a nice HTML format... Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 59 / 69
Law of Demeter Which methods to call Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 60 / 69
Law of Demeter A method of an object should only invoke methods of: 1 Itself 2 Its parameters 3 Objects it creates 4 Its members Methods should not invoke methods of other objects members Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 61 / 69
Law of Demeter public class StudentPrinter {... public String printstudent(student student) {... // violation String course = student.getcourse(i).getname(); Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 62 / 69
Law of Demeter public class Student {... public String getcoursename(int i) { return courses[i].getname(); Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 63 / 69
Law of Demeter Chaining of method calls is dangerous because you depend on all parts of that chain Provide a method in Student class that gets you names of the courses That method delegates to the Course class If the Course class changes only Student class needs to be updated not StudentPrinter Consequence for composition/delegation: only delegate to own members! Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 64 / 69
Enterprise Architecture Principles Collection of best practices Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 65 / 69
Enterprise Architecture Principles Reuse Existing components must be reused where applicable Instead of developing them from scratch In practice there is often a trade-off, as the existing component might not exactly match the requirements Optionally, extend the existing component Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 66 / 69
Enterprise Architecture Principles Buy rather than build If a component exist (commercially or open-source) that match the requirements, it should rather be used Unless there are strategical (e.g. competition) or technical issues Motivation: higher quality of existing solutions, might provide extra functionality Often, it is cheaper to use existing solutions instead of developing them Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 67 / 69
Enterprise Architecture Principles Single point of view If there are multiple sources of data (e.g. file-system and database system) Develop components to make them appear as a single source Motivation: will lead to more simple components (that use the source), logic to combine the sources is bundled in a single place Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 68 / 69
The End Next: Analysis and Design Roman Kern (ISDS, TU Graz) OO Design Principles 2018-10-10 69 / 69