Object Oriented Software Design - I
Object Oriented Software Design - I
OO Design Principles
1 / 33
Outline
1 2 3 4 5 6 7
Principles of OO programming and design Single Responsibility Open/Close principle Liskovs Substitution Principle Interfaces Dependency Inversion Bibliography
OO Design Principles
2 / 33
SOLID
SOLID denotes the ve principles of good object oriented programming
Single responsibility Open-closed Liskov substitution Interface segregation Dependency inversion
it is a mnemonic acronym introduced by Robert C. Martin in the early 2000s which stands for ve basic patterns of object-oriented programming and design. The principles when applied together make it much more likely that a programmer will create a system that is easy to maintain and extend over time.
OO Design Principles
3 / 33
Outline
1 2 3 4 5 6 7
Principles of OO programming and design Single Responsibility Open/Close principle Liskovs Substitution Principle Interfaces Dependency Inversion Bibliography
OO Design Principles
4 / 33
Single Responsibility Principle was introduced Tom DeMarco in his book Structured Analysis and Systems Specication, 1979. Robert Martin reinterpreted the concept and dened the responsibility as a reason to change.
G. Lipari (Scuola Superiore SantAnna) OO Design Principles October 28, 2011 5 / 33
Example
OO Design Principles
6 / 33
We can create a new interface and class called IContent and Content to split the responsibilities. Having only one responsibility for each class give us a more exible design:
adding a new protocol causes changes only in the Email class. adding a new type of content supported causes changes only in Content class
G. Lipari (Scuola Superiore SantAnna) OO Design Principles October 28, 2011 7 / 33
Separate responsibilities
// single responsibility principle - good example interface IEmail { void setSender(string sender); void setReceiver(string receiver); void setContent(IContent content); }; interface IContent { String getAsString(); // used for serialization }; class Email implements IEmail { public void setSender(string sender) { /* set sender; */ } public void setReceiver(string receiver) { /* set receiver; */ } public void setContent(IContent content) { /* set content; */ } };
OO Design Principles
8 / 33
Outline
1 2 3 4 5 6 7
Principles of OO programming and design Single Responsibility Open/Close principle Liskovs Substitution Principle Interfaces Dependency Inversion Bibliography
OO Design Principles
9 / 33
OO Design Principles
10 / 33
A class should be open for extension, but closed for modication Bertrand Meyer [2] In an ideal world, you should never need to change existing code or classes
Except for bug-xing and maintenance
all new functionality should be added by adding new subclasses and overriding methods, or by reusing existing code through delegation
OO Design Principles
11 / 33
OO Design Principles
12 / 33
The code
class GraphicEditor { public void drawShape(Shape s) { if (s.type==1) drawRectangle(s); else if (s.type==2) drawCircle(s); } public void drawCircle(Circle r) {....} public void drawRectangle(Rectangle r) {....} } class Shape { public int type; }; class Rectangle extends Shape { Rectangle() { type=1; } }; class Circle extends Shape { Circle() { type=2; } };
OO Design Principles
13 / 33
Solution (UML)
OO Design Principles
14 / 33
The Open-Closed principle Key issue: prepare for change Causes for re-design
Dependence on hardware or software platform Dependence on representation or implementation Algorithmic dependence Tight coupling Overuse of inheritance Inability to alter classes easily
OO Design Principles
15 / 33
Outline
1 2 3 4 5 6 7
Principles of OO programming and design Single Responsibility Open/Close principle Liskovs Substitution Principle Interfaces Dependency Inversion Bibliography
OO Design Principles
16 / 33
Functions that use pointers of references to base classes must be able to use objects of derived classes without knowing it. Barbara Liskov, Data Abstraction and Hierarchy, [1]
OO Design Principles
17 / 33
Functions that use pointers of references to base classes must be able to use objects of derived classes without knowing it. Barbara Liskov, Data Abstraction and Hierarchy, [1] The importance of this principle becomes obvious when you consider the consequences of violating it. If there is a function which does not conform to the LSP, then that function uses a pointer or reference to a base class, but must know about all the derivatives of that base class.
OO Design Principles
17 / 33
Clearly the DrawShape function is badly formed. It must know about every possible derivative of the Shape class, and it must be whenever new derivatives of Shape are created. Indeed, G. Lipari changed (Scuola Superiore SantAnna) OO Design Principles October 28, 2011 18 / 33
OO Design Principles
19 / 33
OO Design Principles
19 / 33
Problems?
Square will inherit the SetWidth and SetHeight functions. These functions are utterly inappropriate for a Square!
since the width and height of a square are identical.
This should be a signicant clue that there is a problem with the design.
OO Design Principles
20 / 33
Problems?
Square will inherit the SetWidth and SetHeight functions. These functions are utterly inappropriate for a Square!
since the width and height of a square are identical.
This should be a signicant clue that there is a problem with the design. Suppose we write the code so that when we set the height the with changes as well, and viceversa. We have to do the Rectangle members virtual, otherwise it does not work!
OO Design Principles
20 / 33
{ { { {
The code above was written by a programmer that did not know about squares what happens if you pass it a pointer to a Square object?
the programmer made the (at that time correct) assumption that modifying the height does not change the width of a rectangle.
OO Design Principles
22 / 33
The previous design violates the LSP A Square is not the same as a Rectangle for some pieces of code
from the behavioural point of view, they are not equivalent (one cannot be used in place of the other) The behaviour is what is important in software! See the paper
OO Design Principles
23 / 33
Outline
1 2 3 4 5 6 7
Principles of OO programming and design Single Responsibility Open/Close principle Liskovs Substitution Principle Interfaces Dependency Inversion Bibliography
OO Design Principles
24 / 33
Clients should not be forced to depend upon interfaces that they dont use. This means that when we write our interfaces we should take care to add only methods that should be there. If we add methods that should not be there the classes implementing the interface will have to implement those methods as well.
For example if we create an interface called Worker and add a method lunch break, all the workers will have to implement it. What if the worker is a robot?
OO Design Principles
25 / 33
Bad example
interface IWorker { void work(); void eat(); }; class Worker implements IWorker { public void work() { ... } public void eat() { ... } }; class SuperWorker implements IWorker { public void work() { ... } public void eat() { ... } }; class Manager { IWorker worker; public void setWorker(IWorker w) { worker=w; } public void manage() { worker.work(); } };
OO Design Principles
26 / 33
Pollution
The IWorker interface is polluted, as Manager does not use function eat() Suppose a robot is bought that can work() but does not need to eat at lunch break it could implement interface IWorker, but returning an exception of error code for function eat() This is bad design, because the interface is doing too much The solution is to separate the interfaces for work() and eat()
OO Design Principles
27 / 33
Outline
1 2 3 4 5 6 7
Principles of OO programming and design Single Responsibility Open/Close principle Liskovs Substitution Principle Interfaces Dependency Inversion Bibliography
OO Design Principles
28 / 33
OO Design Principles
29 / 33
Dependency problem
Lets take the classical example of a copy module which read characters from keyboard and write them to the printer device. The high level class containing the logic is the Copy class. The low level classes are KeyboardReader and PrinterWriter. In a bad design the high level class uses directly the low level classes. In this case if we want to change the design to direct the output to a new FileWriter class we have to change the Copy class. Since the high level modules contains the complex logic they should not depend on the low level modules a new abstraction layer should be created to decouple the two levels
OO Design Principles
30 / 33
The solution
According to the Dependency Inversion Principle, the way of designing a class structure is to start from high level modules to the low level modules: High Level Classes Abstraction Layer Low Level Classes
1 2
Write interfaces to low level modules (abstract layer) Make sure the high level classes use only references to the abstract interfaces Use some creational pattern to make the connection (i.e. insert the reference to the right low level class into the high level class)
OO Design Principles
31 / 33
Outline
1 2 3 4 5 6 7
Principles of OO programming and design Single Responsibility Open/Close principle Liskovs Substitution Principle Interfaces Dependency Inversion Bibliography
OO Design Principles
32 / 33
Bibliography
Barbara Liskov. Data abstraction and hierarchy. SIGPLAN Notice, 23(5), 1988. Bertrand Meyer. Object-Oriented Software Construction. Prentice Hall, 1988.
OO Design Principles
33 / 33