610 likes | 784 Views
Design Patterns. Design principles. The Open/Closed Principle (OCP). A module should be open for extension but closed for modification. The open/ closed principle.
E N D
Design Patterns Design principles
The Open/Closed Principle (OCP) A module should be open for extension but closed for modification.
The open/ closed principle • We should write our modules so that they can be extended, without requiring them to be modified. In other words, we want to be able to change what the modules do, without changing the source code of the modules. • How?: Abstraction and Polymorphism
The open/ closed principle (OCP) Discussion • If I need to create a new shape, such as a Triangle, I must modify the ‘drawShape()' function. • In a complex application the switch/case statement above is repeated over and over again for every kind of operation that can be performed on a shape . • Worse, every module that contains such a switch/case statement retains a dependency upon every possible shape that can be drawn, thus, whenever one of the shapes is modified in any way, the modules all need recompilation, and possibly modification
Summery • When the majority of modules in an application conform to the open/closed principle, then new features can be added to the application by adding new code rather than by changing working code. Thus, the working code is not exposed to breakage.
The Liskov Substitution Principle (LSP) Subclasses should be substitutable for their base classes.
The Liskov Substitution Principle (LCP) • A client of a base class should continue to function properly if a derivative of that base class is passed to it. • In other words, if some functiontakes an argument ot type Policy, then it should be legal to pass in an instance of Personal Auto Policy to that provided Personal Auto Policy is directly/ indirectly derived from Policy.
The Liskov Substitution Principle (LCP) Discussion • Is Square a Rectangle ? Mathematically yes, Behaviorally, a Square is not a Rectangle and it is behavior that software is really all about. • It is only when derived types are completely substitutable for their base types that functions which use those base types canbe reused with impunity, and the derived types can be changed with impunity. • Violations of LSP are latent violations of OCP.
Structural Patterns • Structural patterns are concerned with how classes and objects are composed to form larger structures • Structural class patterns use inheritance to compose interfaces or implementations
Adapter • Convert the interface of a class into another interface clients expect • Adapter lets classes work together that couldn't otherwise because of incompatible interfaces • Use the Adapter pattern when: • you want to use an existing class and its interface does not match the one you need • you need to use several existing subclasses, but it's impractical to adapt their interface by subclassing everyone. An object adapter can adapt the interface of its parent class
Demo code public interface ICar { void Drive(); } public class CToyota : ICar { public void Drive() { Console.WriteLine("we're off in our Toyota..."); } }
public class CCessna { public void Fly() { Console.WriteLine("we're off in our C172..."); } } // the adapter class public class CDrivableCessna : CCessna, ICar { public void Drive() { base.Fly(); } }
Client ICar oCar = new CToyota(); oCar.Drive(); oCar = new CDrivableCessna(); oCar.Drive();
Other solution public class CDrivableCessna2 : ICar { private CCessna m_oContained; public CDrivableCessna2() { m_oContained = new CCessna(); } public void Drive() { m_oContained.Fly(); } }
Bridge • Decouple an abstraction from its implementation so that the two can vary independently • Use the Bridge pattern when: • you want run-time binding of the implementation • you want to share an implementation among multiple objects
Example class Stack { private StackImpl impl; public Stack( String s ) { if (s.Equals("array")) impl = new StackArray(); else if (s.Equals("list")) impl = new StackList(); else Console.WriteLine( "Stack: unknown parameter" ); } public Stack() :this( "array" ) { } public virtual void push( int i ) { impl.push( i ); } public virtual int pop() { return impl.pop(); } public int top() { return impl.top(); } public bool isEmpty() { return impl.isEmpty(); } public bool isFull() { return impl.isFull(); } }
class StackHanoi : Stack { private int totalRejected = 0; public StackHanoi():base( "array" ){ } public StackHanoi( String s ):base( s ){ } public int reportRejected() { return totalRejected; } public override void push( int i ) { if ( ! isEmpty() && i > top()) totalRejected++; else base.push( i ); } }
class StackFIFO : Stack { private StackImpl temp = new StackList(); public StackFIFO():base( "array" ){ } public StackFIFO( String s ) : base( s ){ } public override int pop() { while ( ! isEmpty()) temp.push( base.pop() ); int ret = temp.pop(); while ( ! temp.isEmpty()) push( temp.pop() ); return ret; } }
interface StackImpl { void push( int i ); int pop(); int top(); bool isEmpty(); bool isFull();} class StackArray : StackImpl { private int[] items = new int[12]; private int total = -1; public void push( int i ) { if ( ! isFull()) items[++total] = i; } public bool isEmpty() { return total == -1; } public bool isFull() { return total == 11; } public int top() { if (isEmpty()) return -1; return items[total]; } public int pop() { if (isEmpty()) return -1; return items[total--]; } }
class StackList : StackImpl { private Node last; public void push( int i ) { if (last == null) last = new Node( i ); else { last.next = new Node( i ); last.next.prev = last; last = last.next; } } public bool isEmpty() { return last == null; } public bool isFull() { return false; } public int top() { if (isEmpty()) return -1; return last.value; } public int pop() { if (isEmpty()) return -1; int ret = last.value; last = last.prev; return ret; } }
Client Stack[] stacks = { new Stack( "array" ), new Stack( "list" ), new StackFIFO(), new StackHanoi() }; for (int i=1; i < 15; i++) for (int j=0; j < 3; j++) stacks[j].push( i ); Random rn = new Random(); for (int i=1; i < 15; i++) stacks[3].push( rn.Next(20) ); for (int i=0; i < stacks.Length; i++) { while ( ! stacks[i].isEmpty()) Console.Write( stacks[i].pop() + " " ); Console.WriteLine(); } Console.WriteLine( "total rejected is " + ((StackHanoi)stacks[3]).reportRejected() );
Composite • Compose objects into tree structures to represent whole-part hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly • Use this pattern whenever you have "composites that contain components, each of which could be a composite".
Component abstract class DrawingElement { protected string name; public DrawingElement( string name ) { this.name = name; } abstract public void Add( DrawingElement d ); abstract public void Remove( DrawingElement d ); abstract public void Display( int indent ); }
Leaf class PrimitiveElement : DrawingElement { public PrimitiveElement( string name ) : base( name ) {} public override void Add( DrawingElement c ) { Console.WriteLine("Cannot Add"); } public override void Remove( DrawingElement c ) { Console.WriteLine("Cannot Remove"); } public override void Display( int indent ) { Console.WriteLine( new String( '-', indent ) + " draw a {0}", name ); } }
class CompositeElement : DrawingElement { private ArrayList elements = new ArrayList(); public CompositeElement( string name ): base( name ) {} public override void Add( DrawingElement d ) { elements.Add( d ); } public override void Remove( DrawingElement d ) { elements.Remove( d ); } public override void Display( int indent ) { Console.WriteLine( new String( '-', indent ) + "+ " + name ); foreach( DrawingElement c in elements ) c.Display( indent + 2 ); } }
Client CompositeElement root = new CompositeElement( "Picture" ); root.Add( new PrimitiveElement( "Red Line" )); root.Add( new PrimitiveElement( "Blue Circle" )); root.Add( new PrimitiveElement( "Green Box" )); CompositeElement comp = new CompositeElement( "Two Circles" ); comp.Add( new PrimitiveElement( "Black Circle" ) ); comp.Add( new PrimitiveElement( "White Circle" ) ); root.Add( comp ); PrimitiveElement l = new PrimitiveElement( "Yellow Line" ); root.Add( l ); root.Remove( l ); root.Display( 1 );
Decorator • Attach additional responsibilities to an object dynamically • Decorators provide a flexible alternative to subclassing for extending functionality
Problems • Several classes with a similar operation (method), but different behavior. • We want to use many combinations of these behaviors
Example - Streams • Properties: • BufferedStream • CryptoStream • Objects: • FileStream • MemoryStream • NetworkStream
Solution 1 • Make all possible classes: • FileStream • MemoryStream • NetworkStream • BufferedFileStream • CryptoFileStream • BufferedMemoryStream • CryptoMemoryStream • BufferedNetworkStream • CryptoNetworkStream • BufferedCryptoFileStream • BufferedCryptoNetworkStream • …..
Problems with solution-1 • We don’t have multiple inheritance. • Even if we have, it is problematic, and bad design. • 2n possible classes to create before compilation.
Code Example CryptoStream cryptostreamDecr = new CryptoStream(new FileStream( "EncryptedFile.txt“ ,FileMode.Open ,FileAccess.Read), desdecrypt, CryptoStreamMode.Read); Widget aWidget = new BorderDecorator( new HorScrollDecorator( new VerScrollDecorator( new TextWidget( 80, 24 )))); aWidget.draw();
Example interface Widget { void draw(); } class TextField : Widget { private int width, height; public TextField( int w, int h ) { width = w; height = h; } public void draw() { Console.WriteLine("TextField: " + width + ", " + height ); } }
abstract class Decorator : Widget { private Widget wid; public Decorator( Widget w ) { wid = w; } public virtual void draw() { wid.draw(); } } class BorderDecorator : Decorator { public BorderDecorator( Widget w ):base(w) { } public override void draw() { base.draw(); Console.WriteLine( “ BorderDecorator" ); } } class ScrollDecorator : Decorator { public ScrollDecorator( Widget w ) :base(w){ } public override void draw() { base.draw(); Console.WriteLine( " ScrollDecorator" ); } }
Client public static void Main( String[] args ) { Widget aWidget = new BorderDecorator( new BorderDecorator( new ScrollDecorator( new TextField( 80, 24 )))); aWidget.draw(); }
Facade • Provide a unified interface to a set of interfaces in a subsystem • Facade defines a higher-level interface that makes the subsystem easier to use • Create a class that is the interface to the subsystem • Clients interface with the Facade class to deal with the subsystem • It hides the implementation of the subsystem from clients • It promotes weak coupling between the subsystems and its clients • It does not prevent clients from using subsystems class, should it?
class Bank{ public bool SufficientSavings( Customer c ) { Console.WriteLine("Check bank for {0}", c.Name ); return true; } } class Credit{ public bool GoodCredit( int amount, Customer c ) { Console.WriteLine( "Check credit for {0}", c.Name ); return true; } } class Loan{ public bool GoodLoan( Customer c ) { Console.WriteLine( "Check loan for {0}", c.Name ); return true; } }
class MortgageApplication { int amount; private Bank bank = new Bank(); private Loan loan = new Loan(); private Credit credit = new Credit(); public MortgageApplication( int amount ) { this.amount = amount; } public bool IsEligible( Customer c ) { if( !bank.SufficientSavings( c ) ) return false; if( !loan.GoodLoan( c ) ) return false; if( !credit.GoodCredit( amount, c )) return false; return true; } }
Client MortgageApplication mortgage = new MortgageApplication( 125000 ); // Call subsystem through Facade mortgage.IsEligible( new Customer( “Bill Gates" ) );
Flyweight • Use sharing to support large numbers of fine-grained objects efficiently • The pattern can be used when: • The program uses a large number of objects and • Storage cost are high because of the sheer quantity of objects and • The program does not use object identity (==)
Example abstract class Character { protected char symbol; protected int width; protected int height; protected int ascent; protected int descent; protected int pointSize; public abstract void Draw( int pointSize ); }