February 15, 2012 Book Reviews, Design Patterns, Success 23 comments
February 15, 2012 Book Reviews, Design Patterns, Success 23 comments
Dear Reader,
I’m glad to let you know, that I released my short free e-book on design patterns.
It is available on web site https://designpatterns.andriybuday.com/
As you many know early 2010 I started writing series of blog post dedicated to GoF design patterns. I tried to keep my examples of patterns very simple and in the way, that person reading example can build associations with real or close to real world situations. I also translated all of the examples into Ukrainian and then decided to assemble small e-book. And now it is available for you.
Here is some introduction in Ukrainian (I’m not translating it to English, since if you cannot read in Ukrainian there is no point).
Опис
Книга «Дизайн патерни — просто, як двері» є безплатною україномовною книгою, що містить унікальні приклади до шаблонів проектування. Завдяки своїй нестандартній подачі матеріалу вона дозволить вам легко опанувати основи розуміння дизайн патернів чи систематично і дуже швидко повторити їх перед інтерв’ю. Спосіб написання книги дозволить провести час, відведений на її прочитання, без нудьги, а подекуди навіть захоплююче.
Освіжіть у своїй пам’яті призабуті дизайн патерни!
У декількох словах
Книгу я написав із добрими намірами. Їх у мене було декілька. Я хотів упевнитися, що сам розумію усі класичні дизайн патерни. Ресурсів для цього є досить багато, але я вирішив реалізувати ці патерни самостійно та придумати власні приклади. Таким чином, починаючи із 16 січня 2010 я писав блог пости, які так чи інакше викликали зацікавлення в читачів. Щоб цей внесок в програмування був більш чітким, в мене виникло бажання випустити невеличку книжку, яка стала б колекцією цих блог постів.
Завітайте на сайт книги: http://designpatterns.andriybuday.com/
Та не забудьте подякувати автору, поширивши посилання.
October 14, 2011 Design Patterns 16 comments
As you may know I’m working on Design Patterns book in Ukrainian and as most of the posts I had on patterns have UML-s, I’m considering having UML diagrams in book as well to be consistent. Thus I can either prepare them in some UML tool or just draw. See yourself.
Also drawn by me, but in advanced tool
I would like to use hand-drawn variant because it will make the book look cheery and will make the difference. It might bring some interest like “So what’s is really on that diagram?”. Also I still don’t position book as an “official” book, so I would like to have some bits of unofficially. Especially taking into account that auditory is mostly young starting developers.
I’m of course hesitating, as this variant in its origin is inaccurate (of course I can try harder). Also maintaining such diagrams is bit more difficult, but I don’t see any problem with this.
[Added later (wasn’t in original post)]
After I posted this friend suggested me to use this online uml generation tool. And it would be great another option to consider, but it generates not really what I want:
But it was extremely nice, that to generate picture above I just used this code:
# Abstract Factory
[Cat]^[WoodenCat], [Cat]^[TeddyCat]
[Bear]^[WoodenBear], [Bear]^[TeddyBear]
[IToyFactory]^[WoodenToysFactory], [IToyFactory]^[TeddyToysFactory]
[TeddyToysFactory]uses-.->[TeddyBear]
[TeddyToysFactory]uses-.->[TeddyCat]
[Client]->[IToyFactory]
[Client]->[Cat]
[Client]->[Bear]
[WoodenToysFactory]uses-.->[WoodenCat]
[WoodenToysFactory]uses-.->[WoodenBear]
Nice tool, indeed, but for extremely simple diagrams.
[Added later (15 Oct, after comments)]
After comment by Satomi Joba I tried community edition of another tool, called Astah. Below is what I was able to draw by it. Two things about it: 1) Drawing in this tool is just fabulous, smooth and easy. For me it was more quick and intuitive drawing than in such matured tools as Enterprise Architect for example. 2) Although I’m not sure I like this bold borders and I wasn’t able to quickly change styling of diagram (maybe because of edition I used?).
May 30, 2011 Design Patterns 4 comments
// Abstract expression public abstract class Goods { public abstract int Interpret(CurrentPricesContext context); } // Nonterminal expression public class GoodsPackage : Goods { public List<Goods> GoodsInside { get; set; } public override int Interpret(CurrentPricesContext context) { var totalSum = 0; foreach (var goods in GoodsInside) { totalSum += goods.Interpret(context); } return totalSum; } } // Terminal expression internal class TV : Goods { public override int Interpret(CurrentPricesContext context) { int price = context.GetPrice("TV"); Console.WriteLine("TV: {0}", price); return price; } } // Here other terminal expressions go (Laptop, Bed)
public void RunInterpreterDemo() { // create syntax tree that represents sentence var truckWithGoods = PrepareTruckWithGoods(); // get latest context var pricesContext = GetRecentPricesContext(); // invoke Interpret var totalPriceForGoods = truckWithGoods.Interpret(pricesContext); Console.WriteLine("Total: {0}", totalPriceForGoods); } private CurrentPricesContext GetRecentPricesContext() { var pricesContext = new CurrentPricesContext(); pricesContext.SetPrice("Bed", 400); pricesContext.SetPrice("TV", 100); pricesContext.SetPrice("Laptop", 500); return pricesContext; } public GoodsPackage PrepareTruckWithGoods() { var truck = new GoodsPackage() { GoodsInside = new List<Goods>() }; var bed = new Bed(); var doubleTriplePackedBed = new GoodsPackage() { GoodsInside = new List<Goods>() { new GoodsPackage() { GoodsInside = new List<Goods>() { bed } } } }; truck.GoodsInside.Add(doubleTriplePackedBed); truck.GoodsInside.Add(new TV()); truck.GoodsInside.Add(new TV()); truck.GoodsInside.Add(new GoodsPackage() { GoodsInside = new List<Goods>() { new Laptop(), new Laptop(), new Laptop() } }); return truck; }
Quickly output:
Bed: 400
TV: 100
TV: 100
Laptop: 500
Laptop: 500
Laptop: 500
Total: 2100
Interpreter is one of the design patterns that you most likely will never use in your life. It is bit cumbersome and has very specific application.
May 8, 2011 Design Patterns No comments
Do you imagine yourself defusing bomb? Scary? Even if you couldn’t imagine this there is such profession as bomb defuser and people owning it are human as well, so I bet they feel scary when near bomb. For our luck there is technology nowadays that allows remotely operate with some kind of robot that can defuse bomb. Using robots also allows to do more complex operations with safely lifting bombs, and manipulating them. I bet human hands would shake while holding bomb, not robot’s.
Connection with robot is wireless of course and you would have special suit that makes you body operate robot remotely as you were it. (Ok on the picture above you see not such advance robot, but lets imagine we are in future.) Robot still has control panel on it that will allow operating it directly. This is needed just in case connection could not be made (say someone is blocking signal).
To summarize, robot is heavy instance and it can be operated directly or you can proxy your requests through special suit.
Proxy is design pattern that surrogates real object and proxies requests to it when needed, it also instantiates real object if it is not yet instantiated.
Talking about example above, object which we use is robot (RobotBombDefuser) it is heavy stuff, which we move using remote controls aka. proxy suit (RobotBombDefuserProxy). As this suit works wirelessly with robot it knows how to connect to real object and how to pass your requests to it. The same is happening when we connect to some remote web server and call methods on it.
Proxy is also often used when you need lazy initialization. In this case instead of connecting to remote resource there is simple check if object is not null and then assigning an instance if needed.
Let’s see what we have in our example. Here is robot itself.
public class RobotBombDefuser { private Random _random = new Random(); private int _robotConfiguredWavelength = 41; private bool _isConnected = false; public void ConnectWireless(int communicationWaveLength) { if(communicationWaveLength == _robotConfiguredWavelength) { _isConnected = IsConnectedImmitatingConnectivitiyIssues(); } } public bool IsConnected() { _isConnected = IsConnectedImmitatingConnectivitiyIssues(); return _isConnected; } private bool IsConnectedImmitatingConnectivitiyIssues() { return _random.Next(0, 10) < 4; // immitates 40% good connection, aka. very bad } public virtual void WalkStraightForward(int steps) { Console.WriteLine("Did {0} steps forward...", steps); } public virtual void TurnRight() { Console.WriteLine("Turned right..."); } public virtual void TurnLeft() { Console.WriteLine("Turned left..."); } public virtual void DefuseBomb() { Console.WriteLine("Cut red or green or blue wire..."); } }
Main methods that do the stuff are WalkStraightForward, TurnRight, TurnLeft, DefuseBomb. Methods to connect wirelessly and IsConnected are just for imitating reality of this app. You could omit how it works internally.
Here we have Proxy implementation. It always has reference to real object and often implements same interface if is not derived from object’s class.
public class RobotBombDefuserProxy : RobotBombDefuser { private RobotBombDefuser _robotBombDefuser; private int _communicationWaveLength; private int _connectionAttempts = 3; public RobotBombDefuserProxy(int communicationWaveLength) { _robotBombDefuser = new RobotBombDefuser(); _communicationWaveLength = communicationWaveLength; } public virtual void WalkStraightForward(int steps) { EnsureConnectedWithRobot(); _robotBombDefuser.WalkStraightForward(steps); } public virtual void TurnRight() { EnsureConnectedWithRobot(); _robotBombDefuser.TurnRight(); } public virtual void TurnLeft() { EnsureConnectedWithRobot(); _robotBombDefuser.TurnLeft(); } public virtual void DefuseBomb() { EnsureConnectedWithRobot(); _robotBombDefuser.DefuseBomb(); } private void EnsureConnectedWithRobot() { if (_robotBombDefuser == null) { _robotBombDefuser = new RobotBombDefuser(); _robotBombDefuser.ConnectWireless(_communicationWaveLength); } for (int i = 0; i < _connectionAttempts; i++) { if (_robotBombDefuser.IsConnected() != true) { _robotBombDefuser.ConnectWireless(_communicationWaveLength); } else { break; } } if(_robotBombDefuser.IsConnected() != true) { throw new BadConnectionException("No connection with remote bomb diffuser robot could be made after few attempts."); } } }
So our proxy simply redirects requests to real object and before doing so it always ensures that connection is established, and if it is not it establishes it with three attempts and if it couldn’t it then throws an exception.
So let’s see usage:
public static void Run() { int opNum = 0; try { var proxy = new RobotBombDefuserProxy(41); proxy.WalkStraightForward(100); opNum++; proxy.TurnRight(); opNum++; proxy.WalkStraightForward(5); opNum++; proxy.DefuseBomb(); opNum++; Console.WriteLine(); } catch (BadConnectionException e) { Console.WriteLine("Exception has been caught with message: ({0}). Decided to have human operate robot there.", e.Message); PlanB(opNum); } } private static void PlanB(int nextOperationNum) { RobotBombDefuser humanOperatingRobotDirectly = new RobotBombDefuser(); if(nextOperationNum == 0) { humanOperatingRobotDirectly.WalkStraightForward(100); nextOperationNum++; } if (nextOperationNum == 1) { humanOperatingRobotDirectly.TurnRight(); nextOperationNum++; } if (nextOperationNum == 2) { humanOperatingRobotDirectly.WalkStraightForward(5); nextOperationNum++; } if (nextOperationNum == 3) { humanOperatingRobotDirectly.DefuseBomb(); } } }
In sample code above you can see usage of the defuser proxy, there is also “plan B”, when direct instance of the defuser is created and used. Code might look bit complex, because of that opNum and nextOperationNum noise. I added it to ensure robot continues it work from where it stopped.
Output:
Did 100 steps forward…
Turned right…
Exception has been caught with message: (No connection with remote bomb diffuser robot could be made after few attempts.). Decided to have human operate robot there.
Did 5 steps forward…
Cut red or green or blue wire…
My implementation of this Design Pattern is slightly different from its original implementation in GoF book where you have two derived classes both from some interface or superclass. But I think in real world implementation with deriving from already existing class is more common. For example such proxy is used always you need to have virtual methods for some framework, say mocking framework or ORM framework.
Here is UML for my example:
February 26, 2011 Design Patterns No comments
// Colleague class BodyPart { private readonly Brain _brain; public BodyPart(Brain brain) { _brain = brain; } public void Changed() { _brain.SomethingHappenedToBodyPart(this); } }
class Ear : BodyPart { private string _sounds = string.Empty; public Ear(Brain brain) : base(brain) { } public void HearSomething() { Console.WriteLine("Enter what you hear:"); _sounds = Console.ReadLine(); Changed(); } public string GetSounds() { return _sounds; } }
So it basically can HearSomething and can provide its status by GetSounds method. Some other body parts can have some reactions, as simple Face implementation below:
class Face : BodyPart { public Face(Brain brain) : base(brain) { } public void Smile() { Console.WriteLine("FACE: Smiling..."); } }
// Mediator class Brain { public Brain() { CreateBodyParts(); } private void CreateBodyParts() { Ear = new Ear(this); Eye = new Eye(this); Face = new Face(this); Hand = new Hand(this); Leg = new Leg(this); } public Ear Ear { get; private set; } public Eye Eye { get; private set; } public Face Face { get; private set; } public Hand Hand { get; private set; } public Leg Leg { get; private set; } public void SomethingHappenedToBodyPart(BodyPart bodyPart) { if (bodyPart is Ear) { string heardSounds = ((Ear)bodyPart).GetSounds(); if (heardSounds.Contains("stupid")) { // attacking offender Leg.StepForward(); Hand.HitPersonNearYou(); Leg.Kick(); } else if (heardSounds.Contains("cool")) { Face.Smile(); } } else if (bodyPart is Eye) { // brain can analyze what you see and // can react appropriately using different body parts } else if (bodyPart is Hand) { var hand = (Hand)bodyPart; bool hurtingFeeling = hand.DoesItHurt(); if (hurtingFeeling) { Leg.StepBack(); } bool itIsNice = hand.IsItNice(); if (itIsNice) { Leg.StepForward(); Hand.Embrace(); } } else if (bodyPart is Leg) { // leg can also feel something if you would like it to } } }
Personally I liked example that I’ve prepared. Hope there is no such example over internet, but who knows.
In addition here below is some UML stuff. You can click on image to see it bigger.
January 11, 2011 Design Patterns No comments
For some reason I have thought about good example for Adapter design pattern for a long period of time. There might be straightforward example with two classes, one of which has OperationA and other has OpA and we need to adapt them, but there is no analogy in that example. In this case it would not worse reading this post. Also I thought about example in which we have complex objects with many properties and methods, where you would need to perform some real conversion. But by the way of thinking there was always electricity adapter in my head circling. In Lviv there is still many apartments where old thin sockets exist, to overcome this people buy small adapters, almost as on the picture on the right, except not that cruel.
So we have our notebook charger that easily fits wide European socket. In your apartment all sockets are new, so you get used to it. Your NewElectricitySystem has method MatchWideSocket. But in your friend’s apartment there is still old one and his OldElectricitySystem has only method MatchThinSocket. Unfortunately you cannot drill sockets in someone else’s flat, you have to buy Adapter, that allows you consume the same electricity, but with old system.
ADAPTER
Adapter – is design pattern that allows us use object, which we cannot change, by providing its functionality thought known interface.
// Adaptee class OldElectricitySystem { public string MatchThinSocket() { return "220V"; } } // known interface in our code interface INewElectricitySystem { string MatchWideSocket(); } class NewElectricitySystem : INewElectricitySystem { public string MatchWideSocket() { return "220V"; } } // Adapter class Adapter : INewElectricitySystem { private readonly OldElectricitySystem _adaptee; public Adapter(OldElectricitySystem adaptee) { _adaptee = adaptee; } // All the magic lives here // in our adapter we translate from // what we (code) cannot use straightway into what we can public string MatchWideSocket() { return _adaptee.MatchThinSocket(); } } class ElectricityConsumer { // Charging device can be used only with new system public static void ChargeNotebook(INewElectricitySystem electricitySystem) { Console.WriteLine(electricitySystem.MatchWideSocket()); } } public class AdapterDemo { public static void Run() { // We can easily operate with our new system var newElectricitySystem = new NewElectricitySystem(); ElectricityConsumer.ChargeNotebook(newElectricitySystem); // We have to adapt to old system using adapter var oldElectricitySystem = new OldElectricitySystem(); var adapter = new Adapter(oldElectricitySystem); ElectricityConsumer.ChargeNotebook(adapter); } }
Yet another name for this pattern is Wrapper. That is because it wraps functionality of some object, representing it in another interface.
Actually we can also implement it slightly differently than I have it here. In the example above we use composition, but our adapter can easily implement two interfaces of the old and new system and then once created by one of the constructors we would use our adapter in both directions.
January 4, 2011 Design Patterns No comments
Facade is design pattern that provides us with one entry point to subsystem, simplifying its usage and understanding.
In our example facade would be terminal-serving station (SkiResortFacade), subsystem would be whole bunch of renting buildings, paydesks, hotels. Of course we can go all around the resort, if we like this, but if we take a look from software development point of view if devs will walk all around and use as they wish, it will not lead to positive consequences. And skiers-newcomers will never know where they should go for the first time.
internal class SkiRent { public int RentBoots(int feetSize, int skierLevel) { return 20; } public int RentSki(int weight, int skierLevel) { return 40; } public int RentPole(int height) { return 5; } } internal class SkiResortTicketSystem { public int BuyOneDayTicket() { return 120; } public int BuyHalfDayTicket() { return 60; } } internal class HotelBookingSystem { public int BookRoom(int roomQuality) { switch (roomQuality) { case 3: return 250; case 4: return 500; case 5: return 900; default: throw new ArgumentException("roomQuality should be in range [3;5]","roomQuality"); } } } public class SkiResortFacade { private SkiRent _skiRent = new SkiRent(); private SkiResortTicketSystem _skiResortTicketSystem = new SkiResortTicketSystem(); private HotelBookingSystem _hotelBookingSystem = new HotelBookingSystem(); public int HaveGoodOneDayRest(int height, int weight, int feetSize, int skierLevel, int roomQuality) { int skiPrice = _skiRent.RentSki(weight, skierLevel); int skiBootsPrice = _skiRent.RentBoots(feetSize, skierLevel); int polePrice = _skiRent.RentPole(height); int oneDayTicketPrice = _skiResortTicketSystem.BuyOneDayTicket(); int hotelPrice = _hotelBookingSystem.BookRoom(roomQuality); return skiPrice + skiBootsPrice + polePrice + oneDayTicketPrice + hotelPrice; } public int HaveRestWithOwnSkis() { int oneDayTicketPrice = _skiResortTicketSystem.BuyOneDayTicket(); return oneDayTicketPrice; } } public class FacadeDemo { public static void Run() { var skiResortFacade = new SkiResortFacade(); int weekendRestPrice = skiResortFacade.HaveGoodOneDayRest(175, 60, 42, 2, 3); Console.WriteLine("Price: {0}", weekendRestPrice); } }
This design pattern in some way represents very important encapsulation principle but just on higher level. On this level we encapsulate whole subsystem instead of class. Big systems usually consist with many subsystems that communicate between each other by using facades. Space station connects to another station using one mechanism, not with gluing together hundreds of wires and tubes. Also an good way to develop software would be to have some kind of facade (it might be set of known interfaces) inside of each assembly, so it can be used easily from other parts of your application.
Thank you.
My design patterns table
December 27, 2010 Design Patterns 2 comments
Ok, let’s imagine that you came into toys shop (playing role of Santa) and you want to buy dozen of toys for kids (not necessary for your kids). One kid likes teddy toys, she often goes to bed with them. Other kid prefers solid toys, like wooden or metal. This kid often breaks toys, so you also would prefer to buy something durable. Both of them definitely want a bear and cat toys, they also might want other toy-animals. Fortunately shop is huge and you bought everything. You put wooden toys into one sack and teddy toys into another sack.
Thus when you came to the girl, who likes soft toys, you started getting teddy bear, then teddy cat and other teddy animals requested by kid. When you came to boy you did the same but with another sack, fetching wooden bear and wooden cat out.
Abstract Factory is design pattern that provides you with interface for creating families of objects without specifying their concrete types. (oh… almost repeated word-by-word GoF guys)
In our example family is toys of some specific type, family consists with Bear and Cat. Abstract Factory is sack. One of the factories returns wooden toys and other teddy toys. Thus when kid asks for cat he/she gets cat appropriately to selected sack.
I hope that analogy example is good. Let’s take a look at some code?
Abstract factory and concrete implementations
Abstract Factory defines interface that returns instances of Bear or Cat (through base class). Concrete factories return concrete implementations of members of the family.
// abstract factory
public interface IToyFactory
{
Bear GetBear();
Cat GetCat();
}
// concrete factory
public class TeddyToysFactory : IToyFactory
{
public Bear GetBear()
{
return new TeddyBear();
}
public Cat GetCat()
{
return new TeddyCat();
}
}
// concrete factory
public class WoodenToysFactory : IToyFactory
{
public Bear GetBear()
{
return new WoodenBear();
}
public Cat GetCat()
{
return new WoodenCat();
}
}
This is quite obvious, that once we have instance of factory we are ready to produce members of the families. Let’s take a look on usage:
// lets start with wooden factory
IToyFactory toyFactory = new WoodenToysFactory();
Bear bear = toyFactory.GetBear();
Cat cat = toyFactory.GetCat();
Console.WriteLine("I've got {0} and {1}", bear.Name, cat.Name);
// Output: [I've got Wooden Bear and Wooden Cat]
/*--------------
somewhere else in the code...
----------------*/
// and now teddy one
IToyFactory toyFactory = new TeddyToysFactory();
Bear bear = toyFactory.GetBear();
Cat cat = toyFactory.GetCat();
Console.WriteLine("I've got {0} and {1}", bear.Name, cat.Name);
// Output: [I've got Teddy Bear and Teddy Cat]
Two snippets of code are almost identical, except of concrete sack.
If you are still interested in realization of animals-toys just take a quick look on this:
public abstract class AnimalToy
{
protected AnimalToy(string name)
{
Name = name;
}
public string Name { get; private set; }
}
public abstract class Cat : AnimalToy
{
protected Cat(string name) : base(name) { }
}
public abstract class Bear : AnimalToy
{
protected Bear(string name) : base(name) { }
}
class WoodenCat : Cat
{
public WoodenCat() : base("Wooden Cat") { }
}
class TeddyCat : Cat
{
public TeddyCat() : base("Teddy Cat") { }
}
class WoodenBear : Bear
{
public WoodenBear() : base("Wooden Bear") { }
}
class TeddyBear : Bear
{
public TeddyBear() : base("Teddy Bear") { }
}
Abstract factory is very widely used design pattern. Extremely good example would be ADO.NET DbProviderFactory class, i.e. abstract factory, which defines interface for getting DbCommand, DbConnection, DbParameter and so on. Concrete factory SqlClientFactory returns appropriately SqlCommand, SqlConnection…
Thank you for reading this post till the end.
December 14, 2010 Design Patterns 2 comments
public void GoOutside() { var weather = Weather.GetWeather(); string clothes = GetClothes(weather); string accessories = GetAccessories(weather); Console.WriteLine("Today I wore {0} and took {1}", clothes, accessories); } private string GetAccessories(string weather) { string accessories; switch (weather) { case "sun": accessories = "sunglasses"; break; case "rain": accessories = "umbrella"; break; default: accessories = "nothing"; break; } return accessories; } private string GetClothes(string weather) { string clothes; switch (weather) { case "sun": clothes = "T-Shirt"; break; case "rain": clothes = "Coat"; break; default: clothes = "Shirt"; break; } return clothes; }
internal class Myself { private IWearingStrategy _wearingStrategy = new DefaultWearingStrategy(); public void ChangeStrategy(IWearingStrategy wearingStrategy) { _wearingStrategy = wearingStrategy; } public void GoOutside() { var clothes = _wearingStrategy.GetClothes(); var accessories = _wearingStrategy.GetAccessories(); Console.WriteLine("Today I wore {0} and took {1}", clothes, accessories); } }
public interface IWearingStrategy { string GetClothes(); string GetAccessories(); } class SunshineWearingStrategy : IWearingStrategy { public string GetClothes() { return "T-Shirt"; } public string GetAccessories() { return "sunglasses"; } }
All left is to correctly chose strategy and set it for Myself instance. Hm! Anyway someone have to take a look on weather and put right strategy (wife, who woke up in the morning). But we can do this in one different place.
Output: “Today I wore Coat and took umbrella”
December 7, 2010 Design Patterns 2 comments
Imagine that you went to cafe with your friends. Cafe is somewhat weird, there is not enough room, and you have to pass food to other person if it is not for you. Your best friend took place the most closed to end of the table, so he is first who gets order. Cause he slept not enough in the night he dies for at least one cap of something with coffee and a lot of meat, since he did not eat during the day. Next to your friend is you and then your girlfriend near the wall. You want soup and she wants cappuccino. She have none to pass food to.
I hope that whole mechanics of the design pattern Chain Of Responsibility is understandable. We have some set or chin of handlers (visitors of cafe), that can handle command (food in our example). If handler cannot process command it passes it to its successor if it exists.
Handler
In our example general interface for handler could be following abstract base class:
public abstract class WierdCafeVisitor
{
public WierdCafeVisitor CafeVisitor { get; private set; }
protected WierdCafeVisitor(WierdCafeVisitor cafeVisitor)
{
CafeVisitor = cafeVisitor;
}
public virtual void HandleFood(Food food)
{
// If I cannot handle other food, passing it to my successor
if (CafeVisitor != null)
{
CafeVisitor.HandleFood(food);
}
}
}
As we can see by default food is passed to next cafe visitor in chain, if it exists.
Lets now take a look on realization that is more suitable for your persnickety friend:
public class BestFriend : WierdCafeVisitor
{
public List<Food> CoffeeContainingFood { get; private set; }
public BestFriend(WierdCafeVisitor cafeVisitor) : base(cafeVisitor)
{
CoffeeContainingFood = new List<Food>();
}
public override void HandleFood(Food food)
{
if(food.Ingradients.Contains("Meat"))
{
Console.WriteLine("BestFriend: I just ate {0}. It was testy.", food.Name);
return;
}
if (food.Ingradients.Contains("Coffee") && CoffeeContainingFood.Count < 1)
{
CoffeeContainingFood.Add(food);
Console.WriteLine("BestFriend: I have to take something with coffee. {0} looks fine.", food.Name);
return;
}
base.HandleFood(food);
}
}
Implementations of two other handlers – Me and GirlFriend probably are understandable, but anyway will show realization of GirlFriend visitor:
public class GirlFriend : WierdCafeVisitor
{
public GirlFriend(WierdCafeVisitor cafeVisitor) : base(cafeVisitor)
{
}
public override void HandleFood(Food food)
{
if(food.Name == "Cappuccino")
{
Console.WriteLine("GirlFriend: My lovely cappuccino!!!");
return;
}
base.HandleFood(food);
}
}
That’s simple: girl wants cappuccino, but since she is last in chain she will not get it before friend will not have his cap of something with coffee.
Usage
Now lets take a look on usage. We create two cappuccinos, two soups and one meat. Then we pass those one by one into BestFriend hands:
var cappuccino1 = new Food("Cappuccino", new List<string> {"Coffee", "Milk", "Sugar"});
var cappuccino2 = new Food("Cappuccino", new List<string> {"Coffee", "Milk"});
var soup1 = new Food("Soup with meat", new List<string> {"Meat", "Water", "Potato"});
var soup2 = new Food("Soup with potato", new List<string> {"Water", "Potato"});
var meat = new Food("Meat", new List<string> {"Meat"});
var girlFriend = new GirlFriend(null);
var me = new Me(girlFriend);
var bestFriend = new BestFriend(me);
bestFriend.HandleFood(cappuccino1);
bestFriend.HandleFood(cappuccino2);
bestFriend.HandleFood(soup1);
bestFriend.HandleFood(soup2);
bestFriend.HandleFood(meat);
Output:
BestFriend: I have to take something with coffee. Cappuccino looks fine.
GirlFriend: My lovely cappuccino!!!
BestFriend: I just ate Soup with meat. It was testy.
Me: I like Soup. It went well.
BestFriend: I just ate Meat. It was testy.
As we can see from output girl got only second cappuccino and you have been forced to eat soup without meat :)
What is also interesting is that we can add another one handler after girlfriend, say bag for dog, and everything that none likes will go there.