Design patterns
This presentation is the property of its rightful owner.
Sponsored Links
1 / 61

Design Patterns PowerPoint PPT Presentation


  • 32 Views
  • Uploaded on
  • Presentation posted in: General

Design Patterns. Design principles. The Open/Closed Principle (OCP). A module should be open for extension but closed for modification. The open/ closed principle.

Download Presentation

Design Patterns

An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -

Presentation Transcript


Design patterns

Design Patterns

Design principles


The open closed principle ocp

The Open/Closed Principle (OCP)

A module should be open for extension but closed for modification.


The open closed principle

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 example

The open/ closed principle (OCP) Example


The open closed principle ocp discussion

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


The open closed principle ocp example1

The open/ closed principle (OCP) Example


Summery

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

The Liskov Substitution Principle (LSP)

Subclasses should be substitutable for their base classes.


The liskov substitution principle lcp

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 example

The Liskov Substitution Principle (LCP) Example


The liskov substitution principle lcp discussion

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

  • 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

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


Adapter1

Adapter


Demo code

Demo code

public interface ICar

{

voidDrive();

}

public classCToyota : ICar

{

public voidDrive()

{

Console.WriteLine("we're off in our Toyota...");

}

}


Design patterns

public classCCessna

{

public voidFly()

{

Console.WriteLine("we're off in our C172...");

}

}

// the adapter class

public class CDrivableCessna : CCessna, ICar

{

public voidDrive(){base.Fly();}

}


Client

Client

ICar oCar = new CToyota();

oCar.Drive();

oCar = new CDrivableCessna();

oCar.Drive();


Other solution

Other solution

public classCDrivableCessna2 : ICar

{

private CCessnam_oContained;

public CDrivableCessna2()

{

m_oContained = new CCessna();

}

public voidDrive()

{

m_oContained.Fly();

}

}


Bridge

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


Bridge1

Bridge


Example

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(); }

}


Design patterns

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 );

}

}


Design patterns

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;

}

}


Design patterns

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--];

} }


Design patterns

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;

} }


Client1

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

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".


Composite1

Composite


Component

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 );

}


Design patterns

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 );

}

}


Design patterns

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 );

}

}


Client2

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

Decorator

  • Attach additional responsibilities to an object dynamically

  • Decorators provide a flexible alternative to subclassing for extending functionality


Problems

Problems

  • Several classes with a similar operation (method), but different behavior.

  • We want to use many combinations of these behaviors


Example streams

Example - Streams

  • Properties:

    • BufferedStream

    • CryptoStream

  • Objects:

    • FileStream

    • MemoryStream

    • NetworkStream


Solution 1

Solution 1

  • Make all possible classes:

    • FileStream

    • MemoryStream

    • NetworkStream

    • BufferedFileStream

    • CryptoFileStream

    • BufferedMemoryStream

    • CryptoMemoryStream

    • BufferedNetworkStream

    • CryptoNetworkStream

    • BufferedCryptoFileStream

    • BufferedCryptoNetworkStream

    • …..


Problems with solution 1

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.


A better idea use decorator

A Better idea: Use Decorator


Code example

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();


Example1

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 );

}

}


Design patterns

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" );

}

}


Client3

Client

public static void Main( String[] args )

{

Widget aWidget =

new BorderDecorator(

new BorderDecorator(

new ScrollDecorator(

new TextField( 80, 24 ))));

aWidget.draw();

}


Facade

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?


Facade1

Facade


Design patterns

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;

}

}


Design patterns

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;

}

}


Client4

Client

MortgageApplication mortgage =

new MortgageApplication( 125000 );

// Call subsystem through Facade

mortgage.IsEligible(

new Customer( “Bill Gates" ) );


Flyweight

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 (==)


Flyweight1

Flyweight


Example2

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 );

}


Design patterns

class CharacterA : Character

{

public CharacterA( )

{

this.symbol = 'A';

this.height = 100;

this.width = 120;

this.ascent = 70;

this.descent = 0;

}

public override void Draw( int pointSize )

{

this.pointSize = pointSize;

Console.Write( this.symbol );

}

}

// repeat for all characters


Design patterns

class CharacterFactory

{

private Hashtable characters = new Hashtable();

public Character GetCharacter( char key )

{

Character character = (Character)characters[ key ];

if( character == null )

{

switch( key )

{

case 'A': character = new CharacterA(); break;

case 'B': character = new CharacterB(); break;

//...

case 'Z': character = new CharacterZ(); break;

}

characters.Add( key, character );

}

return character;

}

}


Client5

Client

char[] document = {'A','B','Z','Z','A','A'};

CharacterFactory f = new CharacterFactory();

int pointSize = 12;

foreach( char c in document )

{

Character character = f.GetCharacter( c );

character.Draw( pointSize );

}


Proxy

Proxy

  • Provide a surrogate or placeholder for another object to control access to it.

  • The proxy has the same interface as the original object

  • Virtual Proxy:

    • Creates/accesses expensive objects on demand

    • You may wish to delay creating an expensive object until it is really accessed

    • It may be too expensive to keep entire state of the object in memory at one time


Design patterns

  • Protection Proxy

    • Provides different objects different level of access to original object

  • Cache Proxy (Server Proxy)

    • Multiple local clients can share results from expensive operations: remote accesses or long computations

  • Firewall Proxy

    • Protect local clients from outside world


Proxy1

Proxy


Dynamics

Dynamics


Example3

Example

public interface IMath

{

double Add( double x, double y );

double Sub( double x, double y );

double Mul( double x, double y );

double Div( double x, double y );

}

class Math : MarshalByRefObject, IMath

{

public double Add( double x, double y ) { return x + y; }

public double Sub( double x, double y ) { return x - y; }

public double Mul( double x, double y ){ return x * y; }

public double Div( double x, double y ){ return x / y; }

}


Design patterns

class MathProxy : IMath

{

Math math;

public MathProxy()

{

AppDomain ad = System.AppDomain.CreateDomain( "MathDomain",null, null );

ObjectHandle o = ad.CreateInstance("Proxy","Math",false, System.Reflection.BindingFlags.CreateInstance, null, null, null,null,null );

math = (Math) o.Unwrap();

}


Design patterns

public double Add( double x, double y )

{

return math.Add(x,y);

}

public double Sub( double x, double y )

{

return math.Sub(x,y);

}

public double Mul( double x, double y )

{

return math.Mul(x,y);

}

public double Div( double x, double y )

{

return math.Div(x,y);

}

}


Client6

Client

MathProxy p = new MathProxy();

Console.WriteLine( "4 + 2 = {0}", p.Add( 4, 2 ) );

Console.WriteLine( "4 - 2 = {0}", p.Sub( 4, 2 ) );

Console.WriteLine( "4 * 2 = {0}", p.Mul( 4, 2 ) );

Console.WriteLine( "4 / 2 = {0}", p.Div( 4, 2 ) );


  • Login