1. 20++ Years of
Design PatternsDesign Patterns
Improve the Design of your Code
Dr Dimitris Dranidis
2. Dr Dimitris Dranidis
Senior Lecturer in Computer Science Department
Programme director of the
MSc in Software Engineering & Telecommunications
MSc in Business, Management, Technology and Innovation
2
MSc in Business, Management, Technology and Innovation
Leader of the "Software Engineering & Service-Oriented
Technologies" research group, r
Research interests:
service-oriented computing, Cloud computing
model-based testing, formal methods,
software engineering education
http://www.city.academic.gr/csd/dranidis/
3. Experience in Java/Design Patterns
Teaching Design Patterns for >15 years
Advanced Software Development Techniques
MSc in Software Engineering and Telecommunications
Principal Developer of JSXM
Model-based testing tool
http:www.jsxm.org
14 KLOC (JSXM core)
Plenty of Design Patterns applied
3
4. Outline
Quiz problem
Bad smells in design
Open/Closed principle
4
Open/Closed principle
Design Patterns
Examples of Design Patterns
Quiz solution
Key points
6. Current Design
(http://bit.ly/1zwNf4R)
Your current implementation of a class DocumentPrinter is as follows:
class DocumentPrinter {
public void printDocument(Document doc) {
if (doc.getType().equals("PDF")) {
PDFPrinter pdfprinter = new PDFPrinter();PDFPrinter pdfprinter = new PDFPrinter();
pdfprinter.printPDF(doc.getContent());
}
else if (doc.getType().equals("HTML")) {
HTMLPrinter htmlprinter = new HTMLPrinter();
htmlprinter.printHTML(doc.getContent());
}
else
throw new RuntimeException("Wrong type of document");
}
}
6
7. Constraints
Library classes (These classes cannot be modified).
Document, PDFPrinter and HTMLPrinter
You are expecting other types of documents to beYou are expecting other types of documents to be
handled in the future and you would not like to change
your DocumentPrinter class continuously.
Change DocumentPrinter once and for all!
Make it open for extensions and closed to modifications!
7
8. What we wish to avoid:
class DocumentPrinter {
public void printDocument(Document doc) {
if (doc.getType().equals("PDF")) {
PDFPrinter pdfprinter = new PDFPrinter();
pdfprinter.printPDF(doc.getContent());
}}
else if (doc.getType().equals("HTML")) {
HTMLPrinter htmlprinter = new HTMLPrinter();
htmlprinter.printHTML(doc.getContent());
}
else if (doc.getType().equals(“XML")) {
XMLPrinter xmlprinter = new XMLPrinter();
xmlprinter.printXML(doc.getContent());
}
else
throw new RuntimeException("Wrong type of document");
}
}
8
10. Design smells
Rigidity: Hard to change
A change forces many other unforeseen changes
“Changing it was a lot more complicated than I
thought”
Fragility: Easy to break
A change forces the application to break in oftenA change forces the application to break in often
unforeseen ways
Fragile modules: The more you fix them the worst
they get
Immobility: Hard to reuse
Useful parts are hard to separate
Viscosity: Hard to do the right thing
Many ways to deal with change; hacks are easier than
design-preserving ways
Caused by the software or the environment
10
11. Design smells
Needless complexity: Overdesign
Not useful (even unused) design
elements
Dealing with anticipated requirements
Needless repetition: Cut & Paste abuseNeedless repetition: Cut & Paste abuse
Redundant code
Bugs require repetitive and different fixes
Opacity: Difficult to understand
Easy when you write it, difficult when you
read it
11
13. OCP: Open-Closed Principle
A module should be
Open for extension
Closed for modification
How can we extend the client so that it works with
other servers (OPEN) without having to modify the
client (CLOSED)?
Client Server
13
14. OCP: Abstraction is the key!
Client
«interface»
ClientInterface
Bertrand Meyer 97
Server
14
16. What are Design patterns?
Well-known best design
practices
Let’s apply a Factory
Named solutions to design
problems by experienced
developers
Vocabulary of principles to
design software
Let’s apply a Factory
and a Strategy!
16
17. A Pattern Language: Towns,
Buildings, Construction (1977)
Book on architecture, urban
design, and community
livability.
Patterns describe a problem
and then offer a solution.and then offer a solution.
Still one of the best-selling
books on architecture
17
18. Gang of Four (GoF) Design
Patterns (1994)
23 patterns categorized in
Creational
Structural
Behavioral
18
19. Quotes from the Book
Program to an interface, not an
implementation
Favor object composition over class
inheritanceinheritance
Inheritance breaks encapsulation
Implement abstract classes,
do not inherit concrete classes!
19
20. 22 PLoP conferences (1994-today)
Pattern Languages of Programs
Annual conferences
*PLoP*PLoP
20
21. Patterns in core Java
All patterns have been implemented
in Java’s core libraries:
http://stackoverflow.com/questions/167
3841/examples-of-gof-design-patterns-3841/examples-of-gof-design-patterns-
in-javas-core-libraries
or google:
Examples of GoF Design Patterns in
Java
21
24. Shapes example
public class Rectangle {
private double width;
private double height;
public Rectangle ( double w, double h ) {
width = w ;
height = h ;
}
public double getArea() { return width * height ; }
}
24
25. Shapes example
public class AreaPrinter {
private Rectangle shape;
public AreaPrinter (Rectangle s ) { shape = s ; }
public void printArea () {
System.out.println( "Shape area : " + shape.getArea() ) ;System.out.println( "Shape area : " + shape.getArea() ) ;
}
}
public class Main {
public static void main( String arg[] ) {
Rectangle rectangle = new Rectangle(2.0, 5.0);
AreaPrinter ap = new AreaPrinter( rectangle ) ;
ap.printArea () ;
}
}
25
26. Desired extensions
We want our application to work with other types of
shapes: Squares, Triangles, Circles, Polygons, etc.
public class Square {
private double width;private double width;
public Square ( double w ) {
width = w ;
}
public double getArea() { return width * width ; }
}
Do we need another AreaPrinter for Square?
Can we make AreaPrinter work for both Rectangle
and Square?
26
27. Abstraction is the key!
public interface Shape {
public double getArea();
}
public class AreaPrinter {public class AreaPrinter {
private Shape shape;
public AreaPrinter ( Shape s ) { shape = s ; }
public void printArea () {
System.out.println( "Shape area : " + shape.getArea() ) ;
}
}
AreaPrinter is now open to extensions, closed to modifications!
27
28. Concrete classes implement the
desired interface
public class Rectangle implements Shape {
private double width;
private double height;
public Rectangle ( double w, double h ) {
width = w ;
height = h ;height = h ;
}
public double getArea() { return width * height ; }
}
public class Square implements Shape {
private double width;
public Square ( double w ) {
width = w ;
}
public double getArea() { return width * width ; }
}
28
29. Polygons?
Polygons’ area is hard to calculate! We would not like to write
the code ourselves.
Luckily we find a class Polygon which offers the calculation of
its objects’ area.
Source code not available OR
We don’t wish to change the implementation of PolygonWe don’t wish to change the implementation of Polygon
public class Polygon {
......
public Polygon ( List points ) { .... }
public double computeArea () { .... }
......
}
How can we integrate polygons into our Shapes application?
29
30. Adapter Design Pattern
public class PolygonAdapter implements Shape {
private Polygon polygon; // composition
public PolygonAdapter (Polygon c ) {
polygon = c ;
}
public double getArea () {
return polygon.computeArea() ; // indirection
}
}
30
31. Main client
public class Main {
public static void main( String arg[] ) {
Rectangle rectangle = new Rectangle(2.0, 5.0);
AreaPrinter ap = new AreaPrinter( rectangle ) ;
ap.printArea () ;ap.printArea () ;
Square square = new Square (3.0);
AreaPrinter sp = new AreaPrinter( square ) ;
sp.printArea () ;
Polygon polygon = new Polygon (......);
AreaPrinter pp = new AreaPrinter (
new PolygonAdapter(polygon));
pp.printArea () ;
}
}
31
32. Adapter (GoF, Structural Pattern)
Intent
convert the interface of an existing class into another interface
used by clients
Motivation:
want to use an existing class but ...want to use an existing class but ...
interface of class is not compatible to the one you use and
modification of class is out of question because
source code of class is not available or
it is a general-purpose class (you do not want to modify)
resolve incompatible interfaces
provide a stable interface to similar classes with different
interfaces
32
37. Current design
public class Employee {
private double salary ;
public Employee (double s) {
salary = s ;
}}
public double getPayAmount () {
return salary ;
}
}
Assume that some privileged employees receive double salary
Assume that employees can be promoted
How can we extend Employee to protect it from changes?
37
39. Sub-typing solution (??)
public class Manager extends Employee {
public double getPayAmount () {
return 2 * salary ;return 2 * salary ;
}
}
39
40. Sub-typing solution does not work!
Objects cannot dynamically change their type
An Employee object cannot be converted to a
Manager object
More points of variation create class explosion!More points of variation create class explosion!
Different types of employees could have different
promotion eligibility criteria
Leading to a huge
inheritance hierarchy tree.
40
41. Type attribute solution (??)
(Too smelly, violates OCP)
public class Employee {
private int type ;
private double salary ;
public static final int ENGINEER = 1;
public static final int MANAGER = 2;public static final int MANAGER = 2;
public Employee (double s, int t) { salary = s ; type = t ;}
public double getPayAmount () {
if (type == Employee.ENGINEER)
return salary;
if (type == Employee.MANAGER)
return salary * 2;
return salary ;
}
} 41
42. Strategy Pattern solution
public interface PaymentStrategy {
public double getPayAmount (Employee context);
}
public class EngineerPaymentStrategy implements PaymentStrategy {
public double getPayAmount (Employee empl) {
return empl.getSalary() ;
}
}
public class ManagerPaymentStrategy implements PaymentStrategy {
public double getPayAmount (Employee empl) {
return empl.getSalary() * 2 ;
}
} 42
43. Strategy Pattern solution
public class Employee {
private PaymentStrategy paymentStrategy ; // composition
private double salary ;
public Employee (double s, PaymentStrategy strategy) {
salary = s ; paymentStrategy = strategy ;
}
public double getSalary () {return salary ;}
public double getPayAmount () {
return paymentStrategy.getPayAmount(this) ; // indirection
}
}
43
44. Strategy (GoF: Behavioral)
Intent
define a family of related algorithms, encapsulate each one, and
make them interchangeable by providing a common interface
algorithm may vary by object and also may vary over time
Motivation:Motivation:
Many different algorithms exist for fulfilling a responsibility
You need different variants of an algorithm
An algorithm uses data that clients shouldn't know about
A class defines many behaviors, and these appear as multiple
switch statements in the classes operations
Many related classes differ only in their behavior
44
45. Strategy (GoF)
Motivation (cont):
Separate the algorithms from the object which
is using them becauseis using them because
an object may decide to change the algorithm it uses
an object will get complex if you include all the code of the
algorithms
an object will have to use conditionals to decide which algorithm it
will use
each algorithm uses its own algorithm-specific data
it is difficult to add new algorithms if they are hard coded in the
object.
45
48. When should you use patterns
You have to add some new
functionality and you find out
that:
You need to copy and paste codeYou need to copy and paste code
You need to hack the solution
Current design does not allow
easy fit of the new functionality
48
49. Protect your code from changes
Identify the variation points
Isolate the variation points,
separate them from what is stable.
Encapsulate variations in a
method/class
method encapsulation
Template method, Factory method
class encapsulation
Strategy, Abstract Factory
49
50. When NOT to use Design Patterns
Patterns introduce complexity
Simple problems require
simple solutions
Over-conformance to goodOver-conformance to good
design principles leads to
“Needless Complexity”
Apply Design Patterns only
when it is necessary!
Remember:
Not everything is a nail!
50
52. Current Design
(http://bit.ly/1zwNf4R)
class DocumentPrinter {
public void printDocument(Document doc) {
if (doc.getType().equals("PDF")) {
PDFPrinter pdfprinter = new PDFPrinter();
pdfprinter.printPDF(doc.getContent());pdfprinter.printPDF(doc.getContent());
}
else if (doc.getType().equals("HTML")) {
HTMLPrinter htmlprinter = new HTMLPrinter();
htmlprinter.printHTML(doc.getContent());
}
else
throw new RuntimeException("Wrong type of document");
}
}
52
53. Analysis of the problem
Variation points:
Objects created:
PDFPrinter pdfprinter = new PDFPrinter();PDFPrinter pdfprinter = new PDFPrinter();
HTMLPrinter htmlprinter = new HTMLPrinter();
Interface of objects:
pdfprinter.printPDF(doc.getContent());
htmlprinter.printHTML(doc.getContent());
53
54. Adapter
(Providing a stable interface)
public interface Printer {
public void print (String content);
}
public class PDFPrinterAdapter implements Printer {public class PDFPrinterAdapter implements Printer {
public void print (String content) {
PDFPrinter pdfprinter = new PDFPrinter();
pdfprinter.printPDF(content);
}
}
… similar for HTMLPrinterAdapter
54
55. Factory (GoF Creational)
(Provides a stable interface for object creation)
public class PrinterFactory {
Printer makePrinter (String doctype) {
if (doctype.equals("PDF")) {
return new PDFPrinterAdapter();
}
else if (doctype.equals("HTML")) {
return new HTMLPrinterAdapter()
}
else throw new RuntimeException("……");
}
}
55
57. Final DocumentPrinter
(Open for extensions, closed for modifications)
class DocumentPrinter {
public void printDocument(Document doc) {
PrinterFactory printerFactory = PrinterFactory.getInstance();
Printer printer = printerFactory.makePrinter (doc.getType());
printer.print (doc.getContent());
}
}
57
58. … or as one-liner!
class DocumentPrinter {
public void printDocument(Document doc) {
PrinterFactory.getInstance().makePrinter (doc.getType()).
print (doc.getContent());print (doc.getContent());
}
}
58
59. How to learn Design Patterns
Pick a pattern and then find, and read as many
examples as possible
Focus on the problems the patterns solves,
NOT the solution!NOT the solution!
Invent your own
examples/problems
Apply the Design Pattern
… and enjoy!
59
60. How to apply Design Patterns
Follow a problem-directed approach:
Identification of the pattern needed:
understand the problem
find a pattern that addresses the problem (or a similarfind a pattern that addresses the problem (or a similar
problem)
Study the Pattern solution
UML diagrams
code example (C++, Java, …)
Apply the solution to the problem
embed pattern name in class/method names
60