61
Design Patterns Design principles

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

Embed Size (px)

Citation preview

Design PatternsDesign Patterns

Design principles Design principles

The Open/Closed Principle (OCP)

The Open/Closed Principle (OCP)

A module should be open for extension but closed for

modification.

A module should be open for extension but closed for

modification.

The open/ closed principleThe 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

• 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) ExampleThe open/ closed principle (OCP) Example

CircleData

radius : floatcenter : Point

SquareData

origin : Pointlength : floatwidth : float

ShapeManipulator

drawShape(shapeData : ShapeData)drawCircle(circle : CircleData)drawSquare(square : SquareData)

ShapeData

shapeType : int

public void drawShape(ShapeData shapeData) { switch (shapeData.shapeType) {

case SQUARE:drawSquare((SquareData)shapeData);break;

case CIRCLE:drawCircle((CircleData)shapeData);break;

} }

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

• 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) ExampleThe open/ closed principle (OCP) Example

ShapeInterface

draw()move()

<<Interface>>

Circle

draw()move()

Square

draw()move()

SmartShapeManipulator

drawShape(shape : ShapeInterface)

public void drawShape(ShapeInterface shape){ shape.draw ();}

SummerySummery

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

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

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 function takes 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.

• 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 function takes 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

Policy

getUnitsOfRisk()getPolicyCoverages()getRateSteps()

AutoPolicy

PersonalAutoPolicy CommercialAutoPolicy

PolicyRateModule

This module should not break regardless of whether a Personal Auto or Commercial Auto or Home Owner Policy is passed to it

HomeOwnerPolicy

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.

• 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

• 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

• 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

Adapter Adapter

Demo codeDemo code

public interface ICar{

void Drive();}

public class CToyota : ICar{

public void Drive(){

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

}

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 classpublic class CDrivableCessna : CCessna, ICar{

public void Drive() { base.Fly(); }}

public class CCessna{

public void Fly(){

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

}

// the adapter classpublic class CDrivableCessna : CCessna, ICar{

public void Drive() { base.Fly(); }}

ClientClient

ICar oCar = new CToyota();

oCar.Drive();

oCar = new CDrivableCessna();

oCar.Drive();

ICar oCar = new CToyota();

oCar.Drive();

oCar = new CDrivableCessna();

oCar.Drive();

Other solutionOther solution

public class CDrivableCessna2 : ICar{

private CCessna m_oContained;

public CDrivableCessna2(){

m_oContained = new CCessna();}

public void Drive(){

m_oContained.Fly();}

}

public class CDrivableCessna2 : ICar{

private CCessna m_oContained;

public CDrivableCessna2(){

m_oContained = new CCessna();}

public void Drive(){

m_oContained.Fly();}

}

BridgeBridge

• 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

• 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

BridgeBridge

ExampleExample

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

} }

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

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

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

ClientClient

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

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

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

Composite Composite

ComponentComponent

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

}

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

}

LeafLeaf

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

}}

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

}}

ClientClient

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

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

• Attach additional responsibilities to an object dynamically

• Decorators provide a flexible alternative to subclassing for extending functionality

ProblemsProblems

• Several classes with a similar operation

(method), but different behavior.

• We want to use many combinations of

these behaviors

• Several classes with a similar operation

(method), but different behavior.

• We want to use many combinations of

these behaviors

Example - StreamsExample - Streams

• Properties:

– BufferedStream

– CryptoStream

• Objects:

– FileStream

– MemoryStream

– NetworkStream

• Properties:

– BufferedStream

– CryptoStream

• Objects:

– FileStream

– MemoryStream

– NetworkStream

Solution 1Solution 1• Make all possible classes:

– FileStream

– MemoryStream

– NetworkStream

– BufferedFileStream

– CryptoFileStream

– BufferedMemoryStream

– CryptoMemoryStream

– BufferedNetworkStream

– CryptoNetworkStream

– BufferedCryptoFileStream

– BufferedCryptoNetworkStream

– …..

• Make all possible classes:

– FileStream

– MemoryStream

– NetworkStream

– BufferedFileStream

– CryptoFileStream

– BufferedMemoryStream

– CryptoMemoryStream

– BufferedNetworkStream

– CryptoNetworkStream

– BufferedCryptoFileStream

– BufferedCryptoNetworkStream

– …..

Problems with solution-1Problems 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.

• 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 DecoratorA Better idea: Use Decorator

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

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

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

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

ExampleExample

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

} }

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

} }

ClientClient

public static void Main( String[] args ) { Widget aWidget =

new BorderDecorator( new BorderDecorator( new ScrollDecorator(

new TextField( 80, 24 ))));

aWidget.draw();}

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?

• 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?

Facade Facade

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

}

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

}

ClientClient

MortgageApplication mortgage =

new MortgageApplication( 125000 );

// Call subsystem through Facade

mortgage.IsEligible(

new Customer( “Bill Gates" ) );

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

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

Flyweight Flyweight

ExampleExample

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

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

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

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

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;

}}

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;

}}

ClientClient

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

}

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

• 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

• 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

• 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

Proxy Proxy

Dynamics Dynamics

ExampleExample

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

}

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

}

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

}

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

}

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

}

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

}

ClientClient

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

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