design patterns behavioral patterns. the dependency inversion principle (dip) depend upon...
TRANSCRIPT
Design PatternsDesign Patterns
Behavioral Patterns Behavioral Patterns
The Dependency Inversion Principle (DIP)
The Dependency Inversion Principle (DIP)
Depend upon Abstractions. Do not depend upon concretions.
Depend upon Abstractions. Do not depend upon concretions.
The Dependency Inversion Principle (DIP)The Dependency Inversion Principle (DIP)
• Dependency Inversion is the strategy of depending upon interfaces or abstract functions and classes, rather than upon concrete functions and classes.
• Every dependency in the design should target an interface, or an abstract class. No dependency should target a concrete class.
• Dependency Inversion is the strategy of depending upon interfaces or abstract functions and classes, rather than upon concrete functions and classes.
• Every dependency in the design should target an interface, or an abstract class. No dependency should target a concrete class.
The Dependency Inversion Principle (DIP) ExampleDependency Structure of a Procedural Architecture
The Dependency Inversion Principle (DIP) ExampleDependency Structure of a Procedural Architecture
main
HighLevelFn1 HighLevelFn2 HighLevelFn3
LowLevelFn1 LowLevelFn2 LowLevelFn3 LowLevelFn4
The Dependency Inversion Principle (DIP) ExampleDependency Structure of an Object Oriented Architecture
The Dependency Inversion Principle (DIP) ExampleDependency Structure of an Object Oriented Architecture
HighLevel
AbstractInterface1<<Interface>>
AbstractInterface2<<Interface>>
AbstractInterface3<<Interface>>
DetailImpl1 DetailImpl2 DetailImpl3
The Dependency Inversion Principle (DIP) DiscussionThe Dependency Inversion Principle (DIP) Discussion
• Typically, Concrete things change a lot • abstract things change much less frequently.• One of the most common places that designs depend
upon concrete classes is when those designs create instances.
• By definition, you cannot create instances of abstract classes.
• Solution to this problem: use Abstract Factory
• Typically, Concrete things change a lot • abstract things change much less frequently.• One of the most common places that designs depend
upon concrete classes is when those designs create instances.
• By definition, you cannot create instances of abstract classes.
• Solution to this problem: use Abstract Factory
The Interface Segregation Principle (ISP)
The Interface Segregation Principle (ISP)
Many client specific interfaces are better than one general purpose
interface
Many client specific interfaces are better than one general purpose
interface
The Interface Segregation Principle (ISP)The Interface Segregation Principle (ISP)
• If you have a class that has several clients, rather than loading the class with all the methods that the clients need, create specific interfaces for each type of client and multiply inherit them into the class.
• If you have a class that has several clients, rather than loading the class with all the methods that the clients need, create specific interfaces for each type of client and multiply inherit them into the class.
The Interface Segregation Principle (ISP) ExampleFat Service with Integrated Interfaces
The Interface Segregation Principle (ISP) ExampleFat Service with Integrated Interfaces
ClientA
ClientB
ClientC
Service
clientAMethods()clientBMethods()clientCMethods()
<<Interface>>
The Interface Segregation Principle (ISP) ExampleSegregated Interfaces
The Interface Segregation Principle (ISP) ExampleSegregated Interfaces
ClientA
ClientB
ClientC
ClientAService
clientAMethods()
<<Interface>>
ClientBService
clientBMethods()
<<Interface>>
ClientCService
clientCMethods()
<<Interface>>
ServiceImpl
clientAMethods()clientBMethods()clientCMethods()
The Interface Segregation Principle (ISP) DiscussionThe Interface Segregation Principle (ISP) Discussion
• Without segregation whenever a change is made to one of the methods that ClientA calls, ClientB and ClientC may be affected. It may be necessary to recompile and redeploy them. With segregation if the interface for ClientA needs to change, ClientB and ClientC will remain unaffected.
• The ISP does not recommend that every class that uses a service have its own special interface class that the service must inherit from. Rather, clients should be categorized by their type, and interfaces for each type of client should be created. If two or more different client types need the same method, the method should be added to both of their interfaces.
• Without segregation whenever a change is made to one of the methods that ClientA calls, ClientB and ClientC may be affected. It may be necessary to recompile and redeploy them. With segregation if the interface for ClientA needs to change, ClientB and ClientC will remain unaffected.
• The ISP does not recommend that every class that uses a service have its own special interface class that the service must inherit from. Rather, clients should be categorized by their type, and interfaces for each type of client should be created. If two or more different client types need the same method, the method should be added to both of their interfaces.
The Acyclic Dependencies Principle (ADP)
The Acyclic Dependencies Principle (ADP)
The dependencies betwen packages must not form cycles.
The dependencies betwen packages must not form cycles.
The Acyclic Dependencies Principle (ADP)
The Acyclic Dependencies Principle (ADP)
• Once changes to a package are made, developers can release the packages to the rest of the project. Before they can do this release, however, they must test that the package works. To do that, they must compile and build it with all the packages it depends upon.
• A single cyclic dependency that gets out of control can make the dependency list very long.
• Hence, someone needs to be watching the package dependency structure with regularity, and breaking cycles wherever they appear.
• Once changes to a package are made, developers can release the packages to the rest of the project. Before they can do this release, however, they must test that the package works. To do that, they must compile and build it with all the packages it depends upon.
• A single cyclic dependency that gets out of control can make the dependency list very long.
• Hence, someone needs to be watching the package dependency structure with regularity, and breaking cycles wherever they appear.
The Acyclic Dependencies Principle (ADP) ExampleAcyclic Package Network
The Acyclic Dependencies Principle (ADP) ExampleAcyclic Package Network
gui
comm
modem protocol
comm_error
process
file
The Acyclic Dependencies Principle (ADP) ExampleCyclic Package Network
The Acyclic Dependencies Principle (ADP) ExampleCyclic Package Network
gui
comm
modem protocol
comm_error
process
file
The Acyclic Dependencies Principle (ADP) Discussion
The Acyclic Dependencies Principle (ADP) Discussion
• In the acyclic scenario to release the protocol package, the engineers would have to build it with the latest release of the comm_error package, and run their tests.
• In the cyclic scenario to release protocol, the engineers would have to build it with the latest release of the comm_error, gui, comm, process, modem, file and run their tests.
• Breaking the cycle:– Add new package in between– Add a new Interface
• In the acyclic scenario to release the protocol package, the engineers would have to build it with the latest release of the comm_error package, and run their tests.
• In the cyclic scenario to release protocol, the engineers would have to build it with the latest release of the comm_error, gui, comm, process, modem, file and run their tests.
• Breaking the cycle:– Add new package in between– Add a new Interface
Behavioral patternsBehavioral patterns
• Behavioral patterns are concerned with algorithms and the assignment of responsibilities between objects
• Behavioral patterns describe not just patterns of objects or classes but also the patterns of communication between them.
• Behavioral patterns are concerned with algorithms and the assignment of responsibilities between objects
• Behavioral patterns describe not just patterns of objects or classes but also the patterns of communication between them.
Chain of Responsibility Chain of Responsibility
• Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request.
• Chain the receiving objects and pass the request along the chain until an object handles it
• Use When:– more than on object may handle a request, and the
handler isn't known a priori– When you want to issue a request to one of several
objects without specifying the receiver explicitly – When the set of objects that can handle a request
should be specified dynamically
• Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request.
• Chain the receiving objects and pass the request along the chain until an object handles it
• Use When:– more than on object may handle a request, and the
handler isn't known a priori– When you want to issue a request to one of several
objects without specifying the receiver explicitly – When the set of objects that can handle a request
should be specified dynamically
Chain of Responsibility Chain of Responsibility
HandlerHandler
abstract class Approver{
protected string name;protected Approver successor;
public Approver( string name ){
this.name = name;}
public void SetSuccessor( Approver successor ){
this.successor = successor;}
abstract public void ProcessRequest(PurchaseRequest request );}
abstract class Approver{
protected string name;protected Approver successor;
public Approver( string name ){
this.name = name;}
public void SetSuccessor( Approver successor ){
this.successor = successor;}
abstract public void ProcessRequest(PurchaseRequest request );}
Concrete HandlerConcrete Handler
class Director : Approver{
public Director ( string name ) : base( name ) {}
override public void ProcessRequest( PurchaseRequest request ){
if( request.Amount < 10000.0 )Console.WriteLine( "{0} {1} approved request# {2}",
this, name, request.Number);else
if( successor != null )successor.ProcessRequest( request );
}}
class Director : Approver{
public Director ( string name ) : base( name ) {}
override public void ProcessRequest( PurchaseRequest request ){
if( request.Amount < 10000.0 )Console.WriteLine( "{0} {1} approved request# {2}",
this, name, request.Number);else
if( successor != null )successor.ProcessRequest( request );
}}
class VicePresident : Approver{
public VicePresident ( string name ) : base( name ) {}
override public void ProcessRequest( PurchaseRequest request ){
if( request.Amount < 25000.0 )Console.WriteLine( "{0} {1} approved request# {2}",
this, name, request.Number);else
if( successor != null )successor.ProcessRequest( request );
}}
class VicePresident : Approver{
public VicePresident ( string name ) : base( name ) {}
override public void ProcessRequest( PurchaseRequest request ){
if( request.Amount < 25000.0 )Console.WriteLine( "{0} {1} approved request# {2}",
this, name, request.Number);else
if( successor != null )successor.ProcessRequest( request );
}}
class President : Approver{
public President ( string name ) : base( name ) {}override public void ProcessRequest( PurchaseRequest request ){
if( request.Amount < 100000.0 )Console.WriteLine( "{0} {1} approved request# {2}",
this, name, request.Number);else
Console.WriteLine( "Request# {0} requires " +"an executive meeting!", request.Number );
}}
class President : Approver{
public President ( string name ) : base( name ) {}override public void ProcessRequest( PurchaseRequest request ){
if( request.Amount < 100000.0 )Console.WriteLine( "{0} {1} approved request# {2}",
this, name, request.Number);else
Console.WriteLine( "Request# {0} requires " +"an executive meeting!", request.Number );
}}
ClientClient
Director Larry = new Director( "Larry" );VicePresident Sam = new VicePresident( "Sam" );President Tammy = new President( "Tammy" );Larry.SetSuccessor( Sam );Sam.SetSuccessor( Tammy );
PurchaseRequest rs = new PurchaseRequest(2034, 350.00, "Supplies" );Larry.ProcessRequest( rs );
PurchaseRequest rx = new PurchaseRequest(2035, 32590.10, "Project X" );Larry.ProcessRequest( rx );
PurchaseRequest ry = new PurchaseRequest(2036, 122100.00, "Project Y" );Larry.ProcessRequest( ry );
Director Larry = new Director( "Larry" );VicePresident Sam = new VicePresident( "Sam" );President Tammy = new President( "Tammy" );Larry.SetSuccessor( Sam );Sam.SetSuccessor( Tammy );
PurchaseRequest rs = new PurchaseRequest(2034, 350.00, "Supplies" );Larry.ProcessRequest( rs );
PurchaseRequest rx = new PurchaseRequest(2035, 32590.10, "Project X" );Larry.ProcessRequest( rx );
PurchaseRequest ry = new PurchaseRequest(2036, 122100.00, "Project Y" );Larry.ProcessRequest( ry );
Command Command
• Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations
• Use When you need an action as a parameter• Commands replace callback functions • When you need to specify, queue, and execute
requests at different times• When you need to support undo • When you need to support logging changes
• Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations
• Use When you need an action as a parameter• Commands replace callback functions • When you need to specify, queue, and execute
requests at different times• When you need to support undo • When you need to support logging changes
Command Command
abstract class Command
{
abstract public void Execute();
abstract public void UnExecute();
}
abstract class Command
{
abstract public void Execute();
abstract public void UnExecute();
}
Command ExampleCommand Example
class CalculatorCommand : Command{ char _operator; int operand; Calculator calculator; public CalculatorCommand(Calculator calculator,char operator, int op ) { this.calculator = calculator; this._operator = _operator; this.operand = op; } public char Operator { set{ _operator = value; } } public int Operand { set{ operand = value; } }
class CalculatorCommand : Command{ char _operator; int operand; Calculator calculator; public CalculatorCommand(Calculator calculator,char operator, int op ) { this.calculator = calculator; this._operator = _operator; this.operand = op; } public char Operator { set{ _operator = value; } } public int Operand { set{ operand = value; } }
override public void Execute() { calculator.Operation( _operator, operand ); } override public void UnExecute() { calculator.Operation( Undo( _operator ), operand ); } private char Undo( char _operator ) { char undo = ' '; switch( _operator ) { case '+': undo = '-'; break; case '-': undo = '+'; break; case '*': undo = '/'; break; case '/': undo = '*'; break; } return undo; }}
override public void Execute() { calculator.Operation( _operator, operand ); } override public void UnExecute() { calculator.Operation( Undo( _operator ), operand ); } private char Undo( char _operator ) { char undo = ' '; switch( _operator ) { case '+': undo = '-'; break; case '-': undo = '+'; break; case '*': undo = '/'; break; case '/': undo = '*'; break; } return undo; }}
ReceiverReceiver
class Calculator{ private int total = 0; public void Operation( char _operator, int operand ) { switch( _operator ) { case '+': total += operand; break; case '-': total -= operand; break; case '*': total *= operand; break; case '/': total /= operand; break; } Console.WriteLine( "Total = {0} (following {1} {2})", total, _operator, operand ); }}
class Calculator{ private int total = 0; public void Operation( char _operator, int operand ) { switch( _operator ) { case '+': total += operand; break; case '-': total -= operand; break; case '*': total *= operand; break; case '/': total /= operand; break; } Console.WriteLine( "Total = {0} (following {1} {2})", total, _operator, operand ); }}
InvokerInvoker
class User
{
private Calculator calculator = new Calculator();
private ArrayList commands = new ArrayList();
private int current = 0;
public void Redo( int levels )
{
Console.WriteLine( "---- Redo {0} levels ", levels );
for( int i = 0; i < levels; i++ )
if( current < commands.Count - 1 )
((Command)commands[ current++ ]).Execute();
}
class User
{
private Calculator calculator = new Calculator();
private ArrayList commands = new ArrayList();
private int current = 0;
public void Redo( int levels )
{
Console.WriteLine( "---- Redo {0} levels ", levels );
for( int i = 0; i < levels; i++ )
if( current < commands.Count - 1 )
((Command)commands[ current++ ]).Execute();
}
public void Undo( int levels )
{
Console.WriteLine( "---- Undo {0} levels ", levels );
for( int i = 0; i < levels; i++ )
if( current > 0 )
((Command)commands[ --current ]).UnExecute();
}
public void Compute( char _operator, int operand )
{
Command command = new CalculatorCommand(
calculator, _operator, operand );
command.Execute();
commands.Add( command );
current++;
}
public void Undo( int levels )
{
Console.WriteLine( "---- Undo {0} levels ", levels );
for( int i = 0; i < levels; i++ )
if( current > 0 )
((Command)commands[ --current ]).UnExecute();
}
public void Compute( char _operator, int operand )
{
Command command = new CalculatorCommand(
calculator, _operator, operand );
command.Execute();
commands.Add( command );
current++;
}
ClientClient
User user = new User();
user.Compute( '+', 100 );user.Compute( '-', 50 );user.Compute( '*', 10 );user.Compute( '/', 2 );
user.Undo( 4 );user.Redo( 3 );
User user = new User();
user.Compute( '+', 100 );user.Compute( '-', 50 );user.Compute( '*', 10 );user.Compute( '/', 2 );
user.Undo( 4 );user.Redo( 3 );
Interpreter Interpreter
• Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language
• you create a class for each rule
• The classes can be used to construct a tree that represents elements of the language
• Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language
• you create a class for each rule
• The classes can be used to construct a tree that represents elements of the language
Interpreter Interpreter
class Context{ private string input; private int output; public Context( string input ) { this.input = input; } public string Input { get{ return input; } set{ input = value; } } public int Output { get{ return output; } set{ output = value; } }}
class Context{ private string input; private int output; public Context( string input ) { this.input = input; } public string Input { get{ return input; } set{ input = value; } } public int Output { get{ return output; } set{ output = value; } }}
Abstract ExpressionAbstract Expression
abstract class Expression
{
public abstract string One();
public abstract string Four();
public abstract string Five();
public abstract string Nine();
public abstract int Multiplier();
abstract class Expression
{
public abstract string One();
public abstract string Four();
public abstract string Five();
public abstract string Nine();
public abstract int Multiplier();
public void Interpret( Context context ) { if( context.Input.Length == 0 ) return; if( context.Input.StartsWith( Nine() ) ) { context.Output += 9 * Multiplier(); context.Input = context.Input.Substring(2);
} else if( context.Input.StartsWith( Four() ) ) { context.Output += 4 * Multiplier(); context.Input = context.Input.Substring( 2); } else if( context.Input.StartsWith( Five() ) ) { context.Output += 5 * Multiplier(); context.Input = context.Input.Substring( 1); } while( context.Input.StartsWith( One() ) ) { context.Output += 1 * Multiplier(); context.Input = context.Input.Substring( 1 ); } }
public void Interpret( Context context ) { if( context.Input.Length == 0 ) return; if( context.Input.StartsWith( Nine() ) ) { context.Output += 9 * Multiplier(); context.Input = context.Input.Substring(2);
} else if( context.Input.StartsWith( Four() ) ) { context.Output += 4 * Multiplier(); context.Input = context.Input.Substring( 2); } else if( context.Input.StartsWith( Five() ) ) { context.Output += 5 * Multiplier(); context.Input = context.Input.Substring( 1); } while( context.Input.StartsWith( One() ) ) { context.Output += 1 * Multiplier(); context.Input = context.Input.Substring( 1 ); } }
class ThousandExpression : Expression
{
public override string One() { return "M"; }
public override string Four(){ return " "; }
public override string Five(){ return " "; }
public override string Nine(){ return " "; }
public override int Multiplier() { return 1000; }
}
class HundredExpression : Expression
{
public override string One() { return "C"; }
public override string Four(){ return "CD"; }
public override string Five(){ return "D"; }
public override string Nine(){ return "CM"; }
public override int Multiplier() { return 100; }
}
class ThousandExpression : Expression
{
public override string One() { return "M"; }
public override string Four(){ return " "; }
public override string Five(){ return " "; }
public override string Nine(){ return " "; }
public override int Multiplier() { return 1000; }
}
class HundredExpression : Expression
{
public override string One() { return "C"; }
public override string Four(){ return "CD"; }
public override string Five(){ return "D"; }
public override string Nine(){ return "CM"; }
public override int Multiplier() { return 100; }
}
class TenExpression : Expression
{
public override string One() { return "X"; }
public override string Four(){ return "XL"; }
public override string Five(){ return "L"; }
public override string Nine(){ return "XC"; }
public override int Multiplier() { return 10; }
}
class OneExpression : Expression
{
public override string One() { return "I"; }
public override string Four(){ return "IV"; }
public override string Five(){ return "V"; }
public override string Nine(){ return "IX"; }
public override int Multiplier() { return 1; }
}
class TenExpression : Expression
{
public override string One() { return "X"; }
public override string Four(){ return "XL"; }
public override string Five(){ return "L"; }
public override string Nine(){ return "XC"; }
public override int Multiplier() { return 10; }
}
class OneExpression : Expression
{
public override string One() { return "I"; }
public override string Four(){ return "IV"; }
public override string Five(){ return "V"; }
public override string Nine(){ return "IX"; }
public override int Multiplier() { return 1; }
}
ClientClient
string roman = "MCMXXVIII";
Context context = new Context( roman );
ArrayList parse = new ArrayList();
parse.Add(new ThousandExpression());
parse.Add(new HundredExpression());
parse.Add(new TenExpression());
parse.Add(new OneExpression());
foreach( Expression exp in parse )
exp.Interpret( context );
Console.WriteLine( "{0} = {1}",roman, context.Output );
string roman = "MCMXXVIII";
Context context = new Context( roman );
ArrayList parse = new ArrayList();
parse.Add(new ThousandExpression());
parse.Add(new HundredExpression());
parse.Add(new TenExpression());
parse.Add(new OneExpression());
foreach( Expression exp in parse )
exp.Interpret( context );
Console.WriteLine( "{0} = {1}",roman, context.Output );
Iterator Iterator
• Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation
• methods:– movenext()– getcur()– eof()
• Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation
• methods:– movenext()– getcur()– eof()
Iterator Iterator
class Item
{
string name;
public Item( string name )
{
this.name = name;
}
public string Name
{
get{ return name; }
}
}
class Item
{
string name;
public Item( string name )
{
this.name = name;
}
public string Name
{
get{ return name; }
}
}
abstract class AbstractCollection{ abstract public Iterator CreateIterator();}class Collection : AbstractCollection{ private ArrayList items = new ArrayList(); public override Iterator CreateIterator() { return new Iterator( this ); } public int Count { get{ return items.Count; } } public object this[ int index ] { get{ return items[ index ]; } set{ items.Add( value ); } }}
abstract class AbstractCollection{ abstract public Iterator CreateIterator();}class Collection : AbstractCollection{ private ArrayList items = new ArrayList(); public override Iterator CreateIterator() { return new Iterator( this ); } public int Count { get{ return items.Count; } } public object this[ int index ] { get{ return items[ index ]; } set{ items.Add( value ); } }}
abstract class AbstractIterator{ abstract public Item First(); abstract public Item Next(); abstract public bool IsDone(); abstract public Item CurrentItem();}class Iterator : AbstractIterator{ private Collection collection; private int current = 0; private int step = 1; public Iterator( Collection collection ) { this.collection = collection; } public int Step { get{ return step; } set{ step = value; } }
abstract class AbstractIterator{ abstract public Item First(); abstract public Item Next(); abstract public bool IsDone(); abstract public Item CurrentItem();}class Iterator : AbstractIterator{ private Collection collection; private int current = 0; private int step = 1; public Iterator( Collection collection ) { this.collection = collection; } public int Step { get{ return step; } set{ step = value; } }
override public Item First() { current = 0; return (Item)collection[ current ]; } override public Item Next() { current += step; if( !IsDone() ) return (Item)collection[ current ]; else return null; } override public Item CurrentItem() { return (Item)collection[ current ]; } override public bool IsDone() { return current >= collection.Count ? true : false ; }
override public Item First() { current = 0; return (Item)collection[ current ]; } override public Item Next() { current += step; if( !IsDone() ) return (Item)collection[ current ]; else return null; } override public Item CurrentItem() { return (Item)collection[ current ]; } override public bool IsDone() { return current >= collection.Count ? true : false ; }
ClientClient
Collection collection = new Collection();
collection[0] = new Item( "Item 0" );
collection[1] = new Item( "Item 1" );
:
collection[8] = new Item( "Item 8" );
Iterator iterator = new Iterator( collection );
iterator.Step = 2;
for( Item item = iterator.First(); !iterator.IsDone(); item = iterator.Next() )
{
Console.WriteLine( item.Name );
}
Collection collection = new Collection();
collection[0] = new Item( "Item 0" );
collection[1] = new Item( "Item 1" );
:
collection[8] = new Item( "Item 8" );
Iterator iterator = new Iterator( collection );
iterator.Step = 2;
for( Item item = iterator.First(); !iterator.IsDone(); item = iterator.Next() )
{
Console.WriteLine( item.Name );
}
Mediator Mediator
• Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently
• Defines an interface for communicating with Colleague objects
• Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently
• Defines an interface for communicating with Colleague objects
Mediator Mediator
ExampleExample
interface IChatroom
{
void Register( Participant participant );
void Send( string from, string to, string message );
}
interface IChatroom
{
void Register( Participant participant );
void Send( string from, string to, string message );
}
class Chatroom : IChatroom{ private Hashtable participants = new Hashtable();
public void Register( Participant participant ) { if( participants[ participant.Name ] == null ) participants[ participant.Name ] = participant;
participant.Chatroom = this; }
public void Send( string from, string to, string message ) { Participant pto = (Participant)participants[ to ]; if( pto != null ) pto.Receive( from, message ); }}
class Chatroom : IChatroom{ private Hashtable participants = new Hashtable();
public void Register( Participant participant ) { if( participants[ participant.Name ] == null ) participants[ participant.Name ] = participant;
participant.Chatroom = this; }
public void Send( string from, string to, string message ) { Participant pto = (Participant)participants[ to ]; if( pto != null ) pto.Receive( from, message ); }}
class Participant{ private Chatroom chatroom; private string name; public Participant( string name ) { this.name = name; } public string Name { get{ return name; } } public Chatroom Chatroom { set{ chatroom = value; } get{ return chatroom; } } public void Send( string to, string message ) { chatroom.Send( name, to, message ); } virtual public void Receive(string from, string message ) { Console.WriteLine( "{0} to {1}: '{2}'",from, this.name, message ); }}
class Participant{ private Chatroom chatroom; private string name; public Participant( string name ) { this.name = name; } public string Name { get{ return name; } } public Chatroom Chatroom { set{ chatroom = value; } get{ return chatroom; } } public void Send( string to, string message ) { chatroom.Send( name, to, message ); } virtual public void Receive(string from, string message ) { Console.WriteLine( "{0} to {1}: '{2}'",from, this.name, message ); }}
class BeatleParticipant : Participant{public BeatleParticipant( string name ) : base ( name ) { }override public void Receive( string from, string message ) { Console.Write( "To a Beatle: " ); base.Receive( from, message ); }}class NonBeatleParticipant : Participant{public NonBeatleParticipant( string name ) : base ( name ) { } override public void Receive(string from, string message ) { Console.Write( "To a non-Beatle: " ); base.Receive( from, message ); }}
class BeatleParticipant : Participant{public BeatleParticipant( string name ) : base ( name ) { }override public void Receive( string from, string message ) { Console.Write( "To a Beatle: " ); base.Receive( from, message ); }}class NonBeatleParticipant : Participant{public NonBeatleParticipant( string name ) : base ( name ) { } override public void Receive(string from, string message ) { Console.Write( "To a non-Beatle: " ); base.Receive( from, message ); }}
ClientClient
Chatroom c = new Chatroom();Participant George = new BeatleParticipant("George");Participant Paul = new BeatleParticipant("Paul");Participant Ringo = new BeatleParticipant("Ringo");Participant John = new BeatleParticipant("John") ;Participant Yoko = new NonBeatleParticipant("Yoko");c.Register( George );c.Register( Paul );c.Register( Ringo );c.Register( John );c.Register( Yoko );Yoko.Send( "John", "Hi John!" );Paul.Send( "Ringo", "All you need is love" );Ringo.Send( "George", "My sweet Lord" );Paul.Send( "John", "Can't buy me love" );John.Send( "Yoko", "My sweet love" ) ;
Chatroom c = new Chatroom();Participant George = new BeatleParticipant("George");Participant Paul = new BeatleParticipant("Paul");Participant Ringo = new BeatleParticipant("Ringo");Participant John = new BeatleParticipant("John") ;Participant Yoko = new NonBeatleParticipant("Yoko");c.Register( George );c.Register( Paul );c.Register( Ringo );c.Register( John );c.Register( Yoko );Yoko.Send( "John", "Hi John!" );Paul.Send( "Ringo", "All you need is love" );Ringo.Send( "George", "My sweet Lord" );Paul.Send( "John", "Can't buy me love" );John.Send( "Yoko", "My sweet love" ) ;
Memento Memento
• Without violating encapsulation, capture and externalize an object's internal state so that the object can be returned to this state later
• Allow undos, rollbacks, etc.• But we have Command and Clone, why do we
need another pattern for undo?– Clone can be more expensive than we need
• Only part of the state of an object may change, cloning will copy all the state
• Cloning can be hard to implement • Replacing an object with a clone does not work when other
objects have references to the object to rollback
• Without violating encapsulation, capture and externalize an object's internal state so that the object can be returned to this state later
• Allow undos, rollbacks, etc.• But we have Command and Clone, why do we
need another pattern for undo?– Clone can be more expensive than we need
• Only part of the state of an object may change, cloning will copy all the state
• Cloning can be hard to implement • Replacing an object with a clone does not work when other
objects have references to the object to rollback
Memento Memento
class SalesProspect{ private string name; private string phone; private double budget; public string Name { get{ return name; } set{ name = value; } } public string Phone { get{ return phone; } set{ phone = value; } } public double Budget { get{ return budget; } set{ budget = value; } }
class SalesProspect{ private string name; private string phone; private double budget; public string Name { get{ return name; } set{ name = value; } } public string Phone { get{ return phone; } set{ phone = value; } } public double Budget { get{ return budget; } set{ budget = value; } }
• public Memento SaveMemento()• {• return (new Memento( name, phone, budget ));• }• public void RestoreMemento( Memento memento )• {• this.name = memento.Name;• this.phone = memento.Phone;• this.budget = memento.Budget;• }• public void Show()• {• Console.WriteLine( "\nSales prospect ---- " );• Console.WriteLine( "Name: {0}", this.name );• Console.WriteLine( "Phone: {0}", this.phone );• Console.WriteLine( "Budget: {0:C}", this.budget );• }
• public Memento SaveMemento()• {• return (new Memento( name, phone, budget ));• }• public void RestoreMemento( Memento memento )• {• this.name = memento.Name;• this.phone = memento.Phone;• this.budget = memento.Budget;• }• public void Show()• {• Console.WriteLine( "\nSales prospect ---- " );• Console.WriteLine( "Name: {0}", this.name );• Console.WriteLine( "Phone: {0}", this.phone );• Console.WriteLine( "Budget: {0:C}", this.budget );• }
class Memento
{
private string name;
private string phone;
private double budget;
public Memento(string name,string phone,double budget)
{ this.name = name;
this.phone = phone;
this.budget = budget;
}
public string Name
{
get{ return name; }
set{ name = value; }
}
// also for phone and budget
}
class Memento
{
private string name;
private string phone;
private double budget;
public Memento(string name,string phone,double budget)
{ this.name = name;
this.phone = phone;
this.budget = budget;
}
public string Name
{
get{ return name; }
set{ name = value; }
}
// also for phone and budget
}
class ProspectMemory{ private Memento memento;
public Memento Memento { set{ memento = value; } get{ return memento; } }}
class ProspectMemory{ private Memento memento;
public Memento Memento { set{ memento = value; } get{ return memento; } }}
ClientClientSalesProspect s = new SalesProspect();s.Name = "Noel van Halen";s.Phone = "(412) 256-0990";s.Budget = 25000.0;s.Show();
ProspectMemory m = new ProspectMemory();m.Memento = s.SaveMemento();
s.Name = "Leo Welch";s.Phone = "(310) 209-7111";s.Budget = 1000000.0;s.Show();
s.RestoreMemento( m.Memento );s.Show();
SalesProspect s = new SalesProspect();s.Name = "Noel van Halen";s.Phone = "(412) 256-0990";s.Budget = 25000.0;s.Show();
ProspectMemory m = new ProspectMemory();m.Memento = s.SaveMemento();
s.Name = "Leo Welch";s.Phone = "(310) 209-7111";s.Budget = 1000000.0;s.Show();
s.RestoreMemento( m.Memento );s.Show();
Observer Observer
• Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
• Use the Observer pattern: – When an abstraction has two aspects, one dependent
on the other. – When a change to one object requires changing
others, and you don't how many objects need to be changed
– When an object should be able to notify other objects without making assumptions about who these objects are.
• Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
• Use the Observer pattern: – When an abstraction has two aspects, one dependent
on the other. – When a change to one object requires changing
others, and you don't how many objects need to be changed
– When an object should be able to notify other objects without making assumptions about who these objects are.
Observer Observer
abstract class Stock{ protected string symbol; protected double price; private ArrayList investors = new ArrayList(); public Stock( string symbol, double price ) {
this.symbol = symbol; this.price = price;
} public void Attach( Investor investor ) { investors.Add( investor ); } public void Detach( Investor investor ) { investors.Remove( investor ); }
abstract class Stock{ protected string symbol; protected double price; private ArrayList investors = new ArrayList(); public Stock( string symbol, double price ) {
this.symbol = symbol; this.price = price;
} public void Attach( Investor investor ) { investors.Add( investor ); } public void Detach( Investor investor ) { investors.Remove( investor ); }
public void Notify() { foreach( Investor i in investors ) i.Update( this ); } public double Price { get{ return price; } set{ price = value; Notify(); } } public string Symbol { get{ return symbol; } set{ symbol = value; } }}class IBM : Stock{ public IBM( string symbol, double price ) : base( symbol, price ) {}}
public void Notify() { foreach( Investor i in investors ) i.Update( this ); } public double Price { get{ return price; } set{ price = value; Notify(); } } public string Symbol { get{ return symbol; } set{ symbol = value; } }}class IBM : Stock{ public IBM( string symbol, double price ) : base( symbol, price ) {}}
interface IInvestor{
void Update( Stock stock ); }class Investor : IInvestor{ private string name; private string observerState; private Stock stock; public Investor( string name ) { this.name = name; } public void Update( Stock stock ) { Console.WriteLine( "Notified investor {0} of {1}'s " + change to {2:C}", name, stock.Symbol, stock.Price ); } public Stock Stock { get{ return stock; } set{ stock = value; } }}
interface IInvestor{
void Update( Stock stock ); }class Investor : IInvestor{ private string name; private string observerState; private Stock stock; public Investor( string name ) { this.name = name; } public void Update( Stock stock ) { Console.WriteLine( "Notified investor {0} of {1}'s " + change to {2:C}", name, stock.Symbol, stock.Price ); } public Stock Stock { get{ return stock; } set{ stock = value; } }}
ClientClient
Investor s = new Investor( "Sorros" );Investor b = new Investor( "Berkshire" );IBM ibm = new IBM( "IBM", 120.00 );ibm.Attach( s );ibm.Attach( b );ibm.Price = 120.10;ibm.Price = 121.00;ibm.Price = 120.50;ibm.Price = 120.75;
Investor s = new Investor( "Sorros" );Investor b = new Investor( "Berkshire" );IBM ibm = new IBM( "IBM", 120.00 );ibm.Attach( s );ibm.Attach( b );ibm.Price = 120.10;ibm.Price = 121.00;ibm.Price = 120.50;ibm.Price = 120.75;
State State
• Allow an object to alter its behavior when its internal state changes. The object will appear to change its class
• Use the State pattern in either of the following cases: – An object's behavior depends on its state, and it must
change its behavior at run-time depending on that state.
– Operations have large, multipart conditional statements that depend on the object's state. Often, several operations will contain this same conditional structure
• Allow an object to alter its behavior when its internal state changes. The object will appear to change its class
• Use the State pattern in either of the following cases: – An object's behavior depends on its state, and it must
change its behavior at run-time depending on that state.
– Operations have large, multipart conditional statements that depend on the object's state. Often, several operations will contain this same conditional structure
State State
abstract class State{
protected Account account;protected double balance;protected double interest;protected double lowerLimit;protected double upperLimit;public Account Account{
get{ return account; }set{ account = value; }
}public double Balance{
get{ return balance; }set{ balance = value; }
}abstract public void Initialize();abstract public void Deposit( double amount );abstract public void Withdraw( double amount );abstract public void PayInterest();abstract public void StateChangeCheck();
}
abstract class State{
protected Account account;protected double balance;protected double interest;protected double lowerLimit;protected double upperLimit;public Account Account{
get{ return account; }set{ account = value; }
}public double Balance{
get{ return balance; }set{ balance = value; }
}abstract public void Initialize();abstract public void Deposit( double amount );abstract public void Withdraw( double amount );abstract public void PayInterest();abstract public void StateChangeCheck();
}
class RedState : State{
double serviceFee;public RedState( State state ){
this.balance = state.Balance;this.account = state.Account;Initialize();
}override public void Initialize(){
interest = 0.0;lowerLimit = -100.0;upperLimit = 0.0;serviceFee = 15.00;
}override public void Deposit( double amount ){
balance += amount;StateChangeCheck();
}
class RedState : State{
double serviceFee;public RedState( State state ){
this.balance = state.Balance;this.account = state.Account;Initialize();
}override public void Initialize(){
interest = 0.0;lowerLimit = -100.0;upperLimit = 0.0;serviceFee = 15.00;
}override public void Deposit( double amount ){
balance += amount;StateChangeCheck();
}
override public void Withdraw( double amount ){
amount = amount - serviceFee;Console.WriteLine("No funds available to
withdraw!" );}
override public void PayInterest(){
// No interest is paid}
override public void StateChangeCheck(){
if( balance > upperLimit )account.State = new SilverState( this );
}
override public void Withdraw( double amount ){
amount = amount - serviceFee;Console.WriteLine("No funds available to
withdraw!" );}
override public void PayInterest(){
// No interest is paid}
override public void StateChangeCheck(){
if( balance > upperLimit )account.State = new SilverState( this );
}
class SilverState : State{
public SilverState(double balance, Account account ){
this.balance = balance;this.account = account;Initialize();
}public SilverState( State state ){
this.balance = state.Balance;this.account = state.Account;Initialize();
}override public void Initialize(){
interest = 0.0;lowerLimit = 0.0;upperLimit = 1000.0;
}
class SilverState : State{
public SilverState(double balance, Account account ){
this.balance = balance;this.account = account;Initialize();
}public SilverState( State state ){
this.balance = state.Balance;this.account = state.Account;Initialize();
}override public void Initialize(){
interest = 0.0;lowerLimit = 0.0;upperLimit = 1000.0;
}
override public void Deposit( double amount ){
balance += amount;StateChangeCheck();
}override public void Withdraw( double amount ){
balance -= amount;StateChangeCheck();
}override public void PayInterest(){
balance += interest * balance;StateChangeCheck();
}override public void StateChangeCheck(){
if( balance < lowerLimit )account.State = new RedState( this );
else if( balance > upperLimit )account.State = new GoldState( this );
}
override public void Deposit( double amount ){
balance += amount;StateChangeCheck();
}override public void Withdraw( double amount ){
balance -= amount;StateChangeCheck();
}override public void PayInterest(){
balance += interest * balance;StateChangeCheck();
}override public void StateChangeCheck(){
if( balance < lowerLimit )account.State = new RedState( this );
else if( balance > upperLimit )account.State = new GoldState( this );
}
class GoldState : State{
public GoldState(double balance, Account account ){
this.balance = balance;this.account = account;Initialize();
}public GoldState( State state ){
this.balance = state.Balance;this.account = state.Account;Initialize();
}override public void Initialize(){
// Should come from a databaseinterest = 0.05;lowerLimit = 1000.0;upperLimit = 10000000.0;
}
class GoldState : State{
public GoldState(double balance, Account account ){
this.balance = balance;this.account = account;Initialize();
}public GoldState( State state ){
this.balance = state.Balance;this.account = state.Account;Initialize();
}override public void Initialize(){
// Should come from a databaseinterest = 0.05;lowerLimit = 1000.0;upperLimit = 10000000.0;
}
override public void Deposit( double amount ){
balance += amount;StateChangeCheck();
}override public void Withdraw( double amount ){
balance -= amount;StateChangeCheck();
}override public void PayInterest(){
balance += interest * balance;StateChangeCheck();
}override public void StateChangeCheck(){
if( balance < 0.0 )account.State = new RedState( this );
else if( balance < lowerLimit )account.State = new SilverState( this );
}
override public void Deposit( double amount ){
balance += amount;StateChangeCheck();
}override public void Withdraw( double amount ){
balance -= amount;StateChangeCheck();
}override public void PayInterest(){
balance += interest * balance;StateChangeCheck();
}override public void StateChangeCheck(){
if( balance < 0.0 )account.State = new RedState( this );
else if( balance < lowerLimit )account.State = new SilverState( this );
}
class Account{
private State state;private string owner;public Account( string owner ){ // New accounts are 'Silver' by default
this.owner = owner;state = new SilverState( 0.0, this );
}public double Balance { get{ return state.Balance; }}public State State{
get{ return state; }set{ state = value; }
}public void Deposit( double amount ){
state.Deposit( amount );Console.WriteLine( "Deposited {0:C} --- ", amount);Console.WriteLine( " Balance = {0:C}", this.Balance );Console.WriteLine( " Status = {0}" , this.State );Console.WriteLine( "" );
}
class Account{
private State state;private string owner;public Account( string owner ){ // New accounts are 'Silver' by default
this.owner = owner;state = new SilverState( 0.0, this );
}public double Balance { get{ return state.Balance; }}public State State{
get{ return state; }set{ state = value; }
}public void Deposit( double amount ){
state.Deposit( amount );Console.WriteLine( "Deposited {0:C} --- ", amount);Console.WriteLine( " Balance = {0:C}", this.Balance );Console.WriteLine( " Status = {0}" , this.State );Console.WriteLine( "" );
}
public void Withdraw( double amount ){
state.Withdraw( amount );Console.WriteLine( "Withdrew {0:C} --- ", amount);Console.WriteLine( " Balance = {0:C}", this.Balance );Console.WriteLine( " Status = {0}" , this.State );Console.WriteLine( "" );
}
public void PayInterest(){
state.PayInterest();Console.WriteLine( "Interest Paid --- ");Console.WriteLine( " Balance = {0:C}",this.Balance );Console.WriteLine( " Status = {0}" , this.State );Console.WriteLine( "" );
}
public void Withdraw( double amount ){
state.Withdraw( amount );Console.WriteLine( "Withdrew {0:C} --- ", amount);Console.WriteLine( " Balance = {0:C}", this.Balance );Console.WriteLine( " Status = {0}" , this.State );Console.WriteLine( "" );
}
public void PayInterest(){
state.PayInterest();Console.WriteLine( "Interest Paid --- ");Console.WriteLine( " Balance = {0:C}",this.Balance );Console.WriteLine( " Status = {0}" , this.State );Console.WriteLine( "" );
}
ClientClient
Account account = new Account( "Ana Micola" );
account.Deposit( 500.0 );
account.Deposit( 300.0 );
account.Deposit( 550.0 );
account.PayInterest();
account.Withdraw( 2000.00 );
account.Withdraw( 1100.00 );
Account account = new Account( "Ana Micola" );
account.Deposit( 500.0 );
account.Deposit( 300.0 );
account.Deposit( 550.0 );
account.PayInterest();
account.Withdraw( 2000.00 );
account.Withdraw( 1100.00 );
Strategy Strategy
• Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from the clients that use it
• The Strategy pattern should only be used when the variation in behavior is relevant to clients. If this criteria is not satisfied, the additional abstraction and effort necessary to implement the Strategy pattern is not justified
• Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from the clients that use it
• The Strategy pattern should only be used when the variation in behavior is relevant to clients. If this criteria is not satisfied, the additional abstraction and effort necessary to implement the Strategy pattern is not justified
Strategy Strategy
abstract class SortStrategy{
abstract public void Sort( ArrayList list );}
// "ConcreteStrategy"
class QuickSort : SortStrategy{
public override void Sort(ArrayList list ){
list.Sort(); // Default is QuicksortConsole.WriteLine("QuickSorted list ");
}}
abstract class SortStrategy{
abstract public void Sort( ArrayList list );}
// "ConcreteStrategy"
class QuickSort : SortStrategy{
public override void Sort(ArrayList list ){
list.Sort(); // Default is QuicksortConsole.WriteLine("QuickSorted list ");
}}
class ShellSort : SortStrategy{
public override void Sort(ArrayList list ){
//list.ShellSort();Console.WriteLine("ShellSorted list ");
}}
class MergeSort : SortStrategy{
public override void Sort( ArrayList list ){
//list.MergeSort();Console.WriteLine("MergeSorted list ");
}}
class ShellSort : SortStrategy{
public override void Sort(ArrayList list ){
//list.ShellSort();Console.WriteLine("ShellSorted list ");
}}
class MergeSort : SortStrategy{
public override void Sort( ArrayList list ){
//list.MergeSort();Console.WriteLine("MergeSorted list ");
}}
class SortedList{
private ArrayList list = new ArrayList();private SortStrategy sortstrategy;public void SetSortStrategy( SortStrategy sortstrategy ){
this.sortstrategy = sortstrategy;}public void Sort(){
sortstrategy.Sort( list );}public void Add( string name ){
list.Add( name );}public void Display(){
foreach( string name in list )Console.WriteLine( " " + name );
}}
class SortedList{
private ArrayList list = new ArrayList();private SortStrategy sortstrategy;public void SetSortStrategy( SortStrategy sortstrategy ){
this.sortstrategy = sortstrategy;}public void Sort(){
sortstrategy.Sort( list );}public void Add( string name ){
list.Add( name );}public void Display(){
foreach( string name in list )Console.WriteLine( " " + name );
}}
ClientClient
SortedList studentRecords = new SortedList( );studentRecords.Add( "Samual" );studentRecords.Add( "Jimmy" );studentRecords.Add( "Sandra" );studentRecords.Add( "Anna" );studentRecords.Add( "Vivek" );
studentRecords.SetSortStrategy( new QuickSort() );studentRecords.Sort();studentRecords.Display();
SortedList studentRecords = new SortedList( );studentRecords.Add( "Samual" );studentRecords.Add( "Jimmy" );studentRecords.Add( "Sandra" );studentRecords.Add( "Anna" );studentRecords.Add( "Vivek" );
studentRecords.SetSortStrategy( new QuickSort() );studentRecords.Sort();studentRecords.Display();
Template Method Template Method
• Define the skeleton of an algorithm in an operation, deferring some steps to client subclasses.
• Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.
• Define the skeleton of an algorithm in an operation, deferring some steps to client subclasses.
• Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.
Template Method Template Method
abstract class DataObject{
abstract public void Connect(); abstract public void Select(); abstract public void Process(); abstract public void Disconnect();
// The "Template Method" public void Run() { Connect(); Select(); Process(); Disconnect(); }}
abstract class DataObject{
abstract public void Connect(); abstract public void Select(); abstract public void Process(); abstract public void Disconnect();
// The "Template Method" public void Run() { Connect(); Select(); Process(); Disconnect(); }}
class CustomerDataObject : DataObject{
private string connectionString ="provider=Microsoft.JET.OLEDB.4.0; "+ "data source=c:\\nwind.mdb";
private string commandString;private DataSet dataSet;public override void Connect( ){ // Nothing to do }public override void Select( ){
commandString = "select CompanyName from Customers";OleDbDataAdapter dataAdapter = new OleDbDataAdapter(
commandString, connectionString );dataSet = new DataSet();dataAdapter.Fill( dataSet, "Customers" );
}public override void Process(){
DataTable dataTable = dataSet.Tables["Customers"];foreach( DataRow dataRow in dataTable.Rows )
Console.WriteLine( dataRow[ "CompanyName" ] );}public override void Disconnect(){ // Nothing to do }
}
class CustomerDataObject : DataObject{
private string connectionString ="provider=Microsoft.JET.OLEDB.4.0; "+ "data source=c:\\nwind.mdb";
private string commandString;private DataSet dataSet;public override void Connect( ){ // Nothing to do }public override void Select( ){
commandString = "select CompanyName from Customers";OleDbDataAdapter dataAdapter = new OleDbDataAdapter(
commandString, connectionString );dataSet = new DataSet();dataAdapter.Fill( dataSet, "Customers" );
}public override void Process(){
DataTable dataTable = dataSet.Tables["Customers"];foreach( DataRow dataRow in dataTable.Rows )
Console.WriteLine( dataRow[ "CompanyName" ] );}public override void Disconnect(){ // Nothing to do }
}
ClientClient
public static void Main( string[] args )
{
CustomerDataObject c = new
CustomerDataObject( );
c.Run();
}
public static void Main( string[] args )
{
CustomerDataObject c = new
CustomerDataObject( );
c.Run();
}
Visitor Visitor
• Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates
• Use When an object structure contains many classes of objects with differing interfaces, and you want to perform operations on these objects that depend on their concrete classes
• Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates
• Use When an object structure contains many classes of objects with differing interfaces, and you want to perform operations on these objects that depend on their concrete classes
The ProblemThe Problem
• We have a set of Objects.
• We are writing several algorithms working with these objects.
• We don’t want the objects to be responsible for their part in the algorithm– It does not look good– Several teams are developing with these
classes and we can’t change them easily.
• We have a set of Objects.
• We are writing several algorithms working with these objects.
• We don’t want the objects to be responsible for their part in the algorithm– It does not look good– Several teams are developing with these
classes and we can’t change them easily.
Example - Animal hospitalExample - Animal hospital
• We have many animals
• We want to create a dentist class that can treat any animal’s tooth ache
• Addition of new animals is not so probable
• Addition of new types of doctors is probable.
• We have many animals
• We want to create a dentist class that can treat any animal’s tooth ache
• Addition of new animals is not so probable
• Addition of new types of doctors is probable.
Solution - 1Solution - 1
Let the damn horse treat itself ….
class Horse : public Animal {public:
void CureTeeth() { }}
class Animal {public: void CureTeeth()=0;}
Looks ugly !
ProblemsProblems
• It does not make any sense. – Heeling teeth is not one of the things horses
(or any animals) do to themselves.
• Maybe we can’t change the animal classes– Other groups might be working on it
simultaneously.
• It does not make any sense. – Heeling teeth is not one of the things horses
(or any animals) do to themselves.
• Maybe we can’t change the animal classes– Other groups might be working on it
simultaneously.
Solution - 2Solution - 2
Void foo(Animal x) {if (x instanceof Horse) {
//give a horse teeth treatment} else if (x instanceof Mouse) {
//give a mouse teeth treatment} …
}
ProblemsProblems
• Looks bad!
• Slow
• RTTI usage is not recommended in good design.
• Looks bad!
• Slow
• RTTI usage is not recommended in good design.
A Better Idea - Use VisitorA Better Idea - Use VisitorVisitor
VisitConcreteElementA ( : ConcreteElementA)VisitConcreteElementB ( : ConcreteElementB)
ConcreteVisitor1
VisitConcreteElementA ( : ConcreteElementA)VisitConcreteElementB ( : ConcreteElementB)
ConcreteVisitor2
VisitConcreteElementA ( : ConcreteElementA)VisitConcreteElementB ( : ConcreteElementB)
Element
Accept ( : Visitor)
ConcreteElementB
Accept ( : Visitor)OperationB ()
ConcreteElementA
Accept ( : Visitor)OperationA ()
How does it work ?How does it work ? : SomeObject a : ConcreteElementA v : ConcreteVisitor1b : ConcreteElementB
Accept (v)VisitConcreteElementA (a)
OperationA ( )
Accept (v)VisitConcreteElementB (b)
OperationB ( )
abstract class Visitor{
abstract public void Visit( Element element );}
// "ConcreteVisitor1"
class IncomeVisitor : Visitor{
public override void Visit( Element element ){
Employee employee = ((Employee)element);
// Provide 10% pay raiseemployee.Income *= 1.10;Console.WriteLine( "{0}'s new income: {1:C}",
employee.Name, employee.Income );}
}
abstract class Visitor{
abstract public void Visit( Element element );}
// "ConcreteVisitor1"
class IncomeVisitor : Visitor{
public override void Visit( Element element ){
Employee employee = ((Employee)element);
// Provide 10% pay raiseemployee.Income *= 1.10;Console.WriteLine( "{0}'s new income: {1:C}",
employee.Name, employee.Income );}
}
class VacationVisitor : Visitor{
public override void Visit( Element element ){
Employee employee = ((Employee)element);
// Provide 3 extra vacation daysemployee.VacationDays += 3;Console.WriteLine( "{0}'s new vacation days: {1}",
employee.Name, employee.VacationDays );}
}
class VacationVisitor : Visitor{
public override void Visit( Element element ){
Employee employee = ((Employee)element);
// Provide 3 extra vacation daysemployee.VacationDays += 3;Console.WriteLine( "{0}'s new vacation days: {1}",
employee.Name, employee.VacationDays );}
}
abstract class Element{
abstract public void Accept( Visitor visitor );}class Employee : Element{
string name;double income;int vacationDays;public Employee( string name, double income,int vacationDays ){
this.name = name;this.income = income;this.vacationDays = vacationDays;
}//properties for all fieldspublic override void Accept( Visitor visitor ){
visitor.Visit( this );}
}
abstract class Element{
abstract public void Accept( Visitor visitor );}class Employee : Element{
string name;double income;int vacationDays;public Employee( string name, double income,int vacationDays ){
this.name = name;this.income = income;this.vacationDays = vacationDays;
}//properties for all fieldspublic override void Accept( Visitor visitor ){
visitor.Visit( this );}
}
class Employees{
private ArrayList employees = new ArrayList();
public void Attach( Employee employee ){
employees.Add( employee );}
public void Detach( Employee employee ){
employees.Remove( employee );}
public void Accept( Visitor visitor ){
foreach( Employee e in employees )e.Accept( visitor );
}}
class Employees{
private ArrayList employees = new ArrayList();
public void Attach( Employee employee ){
employees.Add( employee );}
public void Detach( Employee employee ){
employees.Remove( employee );}
public void Accept( Visitor visitor ){
foreach( Employee e in employees )e.Accept( visitor );
}}
ClientClient
Employees e = new Employees();e.Attach( new Employee( "Hank", 25000.0, 14 ) );e.Attach( new Employee( "Elly", 35000.0, 16 ) );e.Attach( new Employee( "Dick", 45000.0, 21 ) );
// Create two visitorsIncomeVisitor v1 = new IncomeVisitor();VacationVisitor v2 = new VacationVisitor();
// Employees are visitede.Accept( v1 );e.Accept( v2 );
Employees e = new Employees();e.Attach( new Employee( "Hank", 25000.0, 14 ) );e.Attach( new Employee( "Elly", 35000.0, 16 ) );e.Attach( new Employee( "Dick", 45000.0, 21 ) );
// Create two visitorsIncomeVisitor v1 = new IncomeVisitor();VacationVisitor v2 = new VacationVisitor();
// Employees are visitede.Accept( v1 );e.Accept( v2 );