design patterns and principles of object oriented application development “design patterns are...
TRANSCRIPT
Design Patterns and Principles of Object Oriented Application
Development
“design patterns are proven techniques used by experienced developers to tackle recurring design problems
without resorting to first principles”
Design Patterns: Elements of Reusable Object-Oriented SoftwareGamma, Helm, Johnson and Vlissides, 1995
Introduction
Software Quality
Principles of Object Oriented Design Methods to improve on software quality
Design Patterns Principles applied
Software Quality
External Factors
Visible to customers
Examples:
Ease of Use Extendability Robustness Correctness
Internal Factors
Visible to developers
Examples:
Information Hiding Consistency Programming Style
External Factors
The software performs according to the client's specification
Correctness
External Factors
The software reacts reasonably to actions and events not covered by the specification
Robustness
External Factors
A system is reliable if it is correct
& robust
Reliability
Software Quality
How is Software Quality achieved?
Software Principles Design Patterns
Software principles + design patterns=
Improved Internal Factors
Why Object Oriented Design?
Object decomposition More intuitive mapping of the problem domain to
objects What types of object are manipulated by the
system? Objects as generalised type Specific behaviour given to subtypes
Object Oriented DesignBenefits
Reusability Operations usually dependent on data Objects bind data to operations
class is the unit of reuse DRY – Don't Repeat Yourself
Extendibility By building on existing functionality Object heirarchies are more stable over time as
system is modified
Modularity
Decomposition large problems into smaller manageable
problems large or complex code base composed of
smaller, simpler subunits Subunits provide specific feature or functionality
Composition Build new software from existing smaller
components (DRY)
ModularitySupporting Principles
The Open-Closed Principle
Information Hiding
Uniform Access Principle
Acyclic Dependency Principle
Single Responsibility Principle
Dependency Inversion Principle
Liskov Substitution Principle
Interface Segregation Principle
Composite Reuse Principle
Principle of Least Knowledge (Law of Demeter)
The Open-Closed Principle
Modules are open for extension
Modules are coupled across abstract interfaces. The interfaces should provide sufficient access to be
extended by newer code.
Modules are closed for modification
new features are added without modifying the system's existing code.
Only bug fixes are applied to existing implementation. New features are added by new classes.
The Open-Closed Principle
Advocates always writing to interfaces or abstract classes
Reduces the coupling between system components to the abstract layer only
The Open-Closed PrincipleExample
Information Hiding Principle
Each module has a defined public interface through which all external access must occur The module designer selects a subset of a
module's properties or methods to act as this interface to client modules.
A module's internal structure should be protected from external access Implementation details remain hidden Easier to ensure system is in a consistent/legal
state if all access to data is secured against external access.
Uniform Access Principle
"All services offered by a module should be available through a uniform notation,
which does not betray whether they are implemented through
storage or through computation."
Bertrand Meyer
Uniform Access PrincipleRuby Example
class UAP attr_reader :value def initialize(value) @value = value end def value_squared return @value*@value endend uap = UAP.new(10)puts uap.valueputs uap.value_squared
Uniform Access PrinciplePython Example
class UAP(object): def __init__(self, value): self.setValue(value) def getValue(self): return self.__value def setValue(self, value): self.__value = value def getValueSquared(self): return self.__value*self.__value value = property(getValue, doc="getter for value") valueSquared = property(getValueSquared, doc="getter for
value squared") uap = UAP(2)print uap.valueprint uap.valueSquared
Acyclic Dependency Principle
Package dependencies must not form cycles. Example: Package A imports Package B and
Package B imports Package A
Causes the tight coupling between packages. Package A must be deployed everywhere
Package B is (and vice versa)
How to break dependency cycles?
Acyclic Dependency Principle
Factor out code that causes the dependency into separate package
Single Responsibility Principle
A class should have one responsibility.
Each responsibility can lead to modifications to the class on changing requirements.
For maintainable code, if a class has multiple responsibilities, break up the class into multiple cooperating classes.
Dependency Inversion Principle
High level classes should depend upon low level abstractions.
High level class contains reference to abstract type to perform the function.
Dependency Inversion PrincipleExample
Gauge takes a string to avoid dependency on concrete GaugeType subclass.
Prone to errors like typos
(See Factory Pattern)
Liskov Substitution Principle
Extends OCP
A base class can be replaced by a derived class without changing the functionality of the module.
Ensure derived classes do not change the behaviour of parent classes.
Liskov Substitution Principle(Contd)
Design by contract (from Eiffel) Preconditions and postconditions of super
class are maintained by subclass. If preconditions not met, don't invoke
method. If postconditions not met, don't return.
Liskov Substitution PrincipleExample
class Rectangle attr_accessor :width, :height def area @width * @height endend
class Square < Rectangle def width=(new_width) @width=@height=new_width end def height=(new_height) @width=@height=new_height endend
def printArea(shape) shape.width=5 shape.height=10 if shape.is_a? Rectangle puts "Rectangle of 5x10 should have area of 50," else puts "Not a Rectangle, so don't know the area" end puts "Shape is a #{shape.class} of #{shape.width}”
+ “x#{shape.height}, area is #{shape.area}"end
r = Rectangle.news = Square.new
printArea(r)getsprintArea(s)
Integration Segregation Principle
Many specific interfaces are better than a large generic interface. Classes should not be forced to implement an
interface they do not use.
Is large interface really representative of most general type? Null operation or default methods?
Reduces amount of information shared between modules.
Composite Reuse Principle
Don't define default behaviour in base classes
Prefer polymorphic composition of objects over inheritance Instance variables are references to abstract type
of concrete classes providing functionality
See Strategy, Composite and Visitor Patterns
Principle of Least Knowledge(aka Law of Demeter)
When calling method M of object O, M may only access methods on:
O Parameters of M Objects created by M O's contained instance objects M cannot call through to methods exposed
by objects outside of it's control: Example:
object.field.method()
Design Patterns
Proven 'best practices'
High level building blocks for system architecture
Resulted from the experiences of their implementation across a number of scenarios.
Common Design Patterns
Creational
Singleton Factory Builder
Behavioural
Strategy Visitor Observer
Structural
Decorator Facade Composite
Singleton
When only a single instance of a class should be created for the entire application.
class Singleton { private static Singleton instance; private Singleton() { ... } public static synchronized Singleton getInstance() { if (instance == null) instance = new Singleton(); return instance; }
public void doSomething() { ... }}
Builder
Create complex objects while hiding the complexity of the objects' creation behind an interface.
The Builder pattern is indicated when the complex object can have different representations/implementations. client is shielded from these differences in
construction
BuilderExample
Factory
Allows clients to create objects of abstract types without depending on concrete subclasses.
Two types:
Abstract Factory
Factory Method
Abstract Factory
Provides an interface to create families of objects without the client specifying the concrete type
System can use of a multiple families of products e.g. wxWidgets or Qt window toolkits
Clients rely on interfaces, not concrete implementations
Abstract FactoryExample
Factory Method
Expose an interface for creating an object
Subclasses determine the concrete type to instantiate
May delegate responsibility to helper classes to localise the knowledge of object's construction
Factory MethodExample
Factory versus Builder
Factory Method creates concrete object Akin to a virtual constructor Returns different subtypes depending on input
Abstract Factory creates families of objects Factory class may contain Factory Method
pattern
Builder assembles complex object from constituent subcomponents Client instructs Builder how to create object and
asks for result
Decorator
Attach additional responsibilities to an object dynamically
Provides a flexible alternative to subclassing for extending functionality
DecoratorExample
void doSomething() {
// Preprocess...
// Delegate to decorated objectdecorated.doSomething()
// Postprocess...
}
DecoratorConcrete Example
InputStream is = new LineNumberInputStream(new BufferedInputStream(new FileInputStream(file)))
From Java's IO Stream:
Facade
Execute the business logic from one use case as one transaction over a single method invocation utilising functionality from an existing use case.
Considered as a single interface to features of a subsystem specified as a single call to the Facade object.
Facade object exists in addition to the public interface it wraps.
FacadeExample
Composite
Represents part-whole hierarchies
Define a data structure as a linked set of nodes
Client can ignore whether node is a leaf or a container
CompositeDiagram
From GoF
Visitor
Separates operations from the data structures on which they operate
New operations can be defined with new Visitor implementations adheres to the Open-Closed Principle
Traversal order can be specified by: the data structure the Visitor class an external operation (an iterator)
VisitorDiagram
Strategy
Allows a family of algorithms to be used interchangeably by client classes
Allows for “pluggability” Algorithms can be selected at run time
StrategyExample
Observer
Defines a one-to-many dependency between objects dependent on state through abstract interfaces
Enforces OCP and DIP
Allows a change to an object's state to be propagated to interested objects such that the number and types of Observer are immaterial to the subject
ObserverExample
Further InformationBooks
Design Patterns: Elements of Reusable Object-Oriented Software
Gamma, Helm, Johnson and Vlissides, 1995(aka Gang of Four book)
Refactoring: Improving the Design of Existing Code.Martin Fowler, 1999
Patterns of Enterprise Application ArchitectureMartin Fowler, 2002
Plenty more in the library
Further InformationElectronic Resources
Hillside.net Online Pattern Catalog(http://hillside.net/patterns/onlinepatterncatalog.htm)
Refactoring.com
MartinFowler.com
Search the Web