Design Patterns
Motivation, Best Practice
Patterns in an industrial scenario
Goal
cooperation of engineers from different diciplines with different vocabularies and tools
managing complex systems
Complex system
-
Magnitude
Num of Elements, possible states in the system
num of possible - num of useful solutions
-
Diversity
heterogeneity of elements, Connectivity, structural complexity
Managing complexity
- Abstraction – simplification
- Decoupling – separation of components that should not depend on each other
- Decomposition – focus on components that are easier to understand
- Classification – similar parts
- Standardization
- Modeling
- Transformation – transformation of the given problem to a domain with proven solution approach
- Experience – use documented experiences from experienced contributors
Complexity-drivers in industrial scenario
Types of heterogenity
– Technical "Engineering Polynesia" – Semantic "Engineering Babylon" – Process "Engineering Chaos"
-
Magnitude
About Patterns
Definition
„Pattern“ has been defined as „an idea that has been useful in one practical context and will probably be useful in others.“
Elements of a pattern
- Name - Aliases, classifications
- Motivation and problem statement
- Context
-
Solution
Structure
Participants
Collaboration
Consequences
Implementation
Examples
Usage in Software Development
Advantages
Common vocabulary → helps manage complex systems, better communication
Improves documentation
Capture knowledge and make it more widely available
Combination of patterns
Reusability, adaptability, extendability → Minimizes development time and costs
Disadvantages
Patterns need to be adapted to the code context
Only indirect code reuse
deceptively simple
Teams may suffer from pattern overload
Classification of Patterns
-
Architectural Patterns
– Structure of software systems – Subsystems, dependencies, communication
-
Design Patterns
– level of classes – Snippets focus on low-level details, Programming language specific
-
ProtoPatterns
– Particular case
-
Antipatterns
– Commonly used but should be avoided
When to use
Similar problems that recur with variations in different contexts
Patterns can be an overkill if solution is simple linear set of instructions
Pattern Types: Architectural Patterns (Overview)
- Fundamental Patterns
essential concepts of software architecture
- Interface Pattern
- Delegation Pattern
- Immutable Pattern
- Creational Patterns
initializing and configuring classes and objects
- Singleton
- Factory
- Abstract Factory
- Builder (Factory for building complex objects in different variants)
- Prototype (Factory for cloning new instances from a prototypical instance)
- Structural Patterns
decoupling "interfaceimplementation" of classes and objects
- Facade / Facet
- Adapter
- Proxy
- Bridge (Abstraction for dynamically binding one of many implementations)
- Composite (Treats objects and compositions uniformly)
- Flyweight (Many fine-grained objects shared efficiently)
- Behavioral Patterns
dynamic interactions among objects
- Observer
- Decorator
- State
- Strategy
- Chain of Responsibility
- Iterator (Aggregate elements are accessed sequentially)
- Command (Object represents all the information needed to call a method at a later time)
- Mediator (Mediator coordinates interactions between its associates)
- Memento (Snapshot captures and restores object states)
Fundamental Patterns
○ Interface
Seperating description and implementation
Issue
Separate description and concrete implementation
defines the signature operations of an entity
implementations can be added / changed easily afterwards
should be stable - in comparison to implementation
○ Delegation
Extension of functionality without inheritance
Issue
Class needs additional functionality
Solution
Outsource functionality into third class and use its instance via delegation
Example 1
Solution without inheritance:
Notification-Service gets an instance of the object that should get called.
Example 2
class A { void foo() { print("a.foo"); } }class B { private delegate A a; // delegation linkpublic B(A a) { this.a = a; }void foo() { a.foo(); // call foo() on the a-instance } }
a = new A(); b = new B(a); // establish delegation between two objects
○ Immutable
Providing unchangeable object after initialization
Issue
Object instance should be immutable
Because several threads are accessing same object but object properties should be configurable
Solution
- Initialize variables in constructor
-
Provide only read access with Getter-Methods
getName(), getDate()
Creational Patterns
○ Singleton
Provision of a single instance only
Issue
Only one object instance should exist:
- Database access
- Id generator
- Logger
- Communication with hardware
Solution
Example
public class Person { private final Logger LOG = LoggerFactory.getLogger(Person.class); Logger getLogger() { return LOG; } }
In this case
LOG
is the same object even without static due to the way the LoggerFactory works.
○ Factory
Method in a derived class creates associates
Issue
Object creation & configuration is complex
Initialization of additional sub-instances required
Solution
Example
interface Product { float getPrice(); }public class Milk implements Product { final float price; public Milk(final float price) { this.price = price; } //constructor public float getPrice() { return price; } //getter }public class Sugar implements Product { final float price; public Sugar(final float price) { this.price = price; } //constructor public float getPrice () { return price; } //getter }public class Product Factory { public static Product createProduct(String what){ //When sugar is requested, we return sugar: if (what.equals("Sugar")) { return new Sugar(1.49F) ; } // When milk is needed, we return milk: else if (what.equals("Milk")) { return new Milk(0.99F); } // Otherwise return at least sugar else { return new Sugar(1.49F); } } }
○ Abstract Factory
Factory for building related objects without specifying their concrete classes
Issue
Achieving higher abstraction by grouping individual factories with a common theme
Solution
A group of individual factories that have a common theme
Two hierarchies → abstract AbstractFactory class provides interface
Client only knows abstract interface → Family may grow independently of the client
Structural Patterns
○ Facade / Facet
Simplifies the interface for a subsystem
Issue
Need simplified access to a complex subsystem
Provides an abstracted interface of a subsystem
Solution
Example
public class SimpleMail { public static int sendMail(String address, String subject, String body) { int status = 0; ... //Complex mail sending operation return status; } }
○ Adapter / Translator
Adapts a server interface for a client
Issue
Need to integrate incompatible external functionality
Solution
Translates (data transformation) into a compatible interface
○ Proxy
One object approximates another
Issue
Need to integrate further actions before intended method call
Solution
Extends concept of the delegation pattern
Implements interface and acts as a representative of the „original“ implementation
Used in: security, logging, caching
○ Inversion of Control IoC
Die Verantwortung für das Erzeugen und Initialisieren von Objekten wird an eine zentrale Stelle (z.B. eine Klasse) delegiert.
Von der zentralen Stelle kann man die Abhängigkeiten zwischen den Objekten leichter überblicken und steuern.
Detailierter: Siehe Block 3 - Werkzeuge
Behavioral Patterns
○ Observer
Dependents update automatically when a subject changes
Issue
Need to react to state changes in an object
Solution
in case of changes of the instance‘s state execute specific action(s) – e.g., notification of instances interested in change
○ Decorator / Wrapper
extends an object
Issue
Need to extend object functionality during runtime (inheritance not possible during runtime)
Solution
Dynamically add new functionality to an existing object
Example
Cake cake = new ChocolateCake();NutsInCake nic = new NutsInCake(cake) ; nic.setAmount(15) ; nic.setType("hazelnut") ;CandleCake cc = new CandleCake (nic) ; cc.setCandles(13); cake = (Cake) cc; cake.bake();
VisualComponent vc = new ScrollBar( new Border( new TextEditor() )); vc.draw();
Stream s = new FileStream(filename); Stream s = new CompressingStream( new BufferedStream(new FileStream(filename) ));
○ State
Object whose behavior depends on its state
Issue
Need to change object behavior based on current state
Solution
Allow an object to update its behavior when its internal state changes
(internal state gets changed with a
setState()
method)
○ Strategy
Vary algorithms independently
Solution
Dynamically add new algorithms → context chooses algorithm to use
Example
class NotificationManager { public enum NotificationMethod {SMS, EMAIL}; private List<Person> members; public void addMember(Person person, NotificationMethod notificationPref){ members.add(person); INotification notification; if (notificationPref == NotificationMethod.SMS) notification = new Sms(); else notification = new Email(); notification.setPerson(person) ; person.setNotificationPreference(notification); }public void sendNotifications (String message) for (Person p : members) p.sendMessage(message); } }
○ Chain of Responsibility
Request delegated to the responsible service provider
Issue
Improve loose coupling between a series of processing logic
Solution