Pragmatic Developer

Ali Özgür

Bookmark Blog

Add to Technorati Favorites

Google Talk

Chat with Ali Özgür

Purchase PragmaSQL from

Calendar

«  December 2008  »
MoTuWeThFrSaSu
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234
View posts in large calendar

Tag Cloud

Don't show

    Authors

    Recent Comments

    Banners




    Download Source(23,52 kb)

    We have different kind of vehicle implementations inherited from an abstract Vehicle class. Our code looks like this

    001    //Abstract Vehicle class
    002    public abstract class Vehicle
    003    {
    004        public abstract string Description { get; }
    005
    006    }
    007    
    008    //Concrete implementation
    009    public sealed class Car : Vehicle
    010    {
    011        public override string Description
    012        {
    013            get { return "Car"; }
    014        }
    015    }
    016
    017    //Concrete implementation
    018    public sealed class Helicopter : Vehicle
    019    {
    020        public override string Description
    021        {
    022            get { return "Helicopter"; }
    023        }
    024    }
    025
    026    //Concrete implementation
    027    public sealed class Jet : Vehicle
    028    {
    029        public override string Description
    030        {
    031            get { return "Jet"; }
    032        }
    033    }

    Suppose that our client wants to rent these different kind of vehicles and print vehicle information plus rental specific info such as FromDate, ToDate and the customer's name. We could implement this requirement by adding those new properties to our base Vehicle class in order to meet our customer's need. We also have to modify our concrete Vehicle classes so thet they can provide rental information through their Description property.

    Here is our modified Vehicle class

    001    //Abstract Vehicle class
    002    public abstract class Vehicle
    003    {
    004        public virtual DateTime FromDate { get; set; }
    005        public virtual DateTime ToDate { get; set; }
    006        public virtual string CustomerName { get; set; }
    007
    008        public abstract string Description { get; }
    009
    010        protected string RentalInfo
    011        {
    012            get 
    013            {
    014                StringBuilder sb = new StringBuilder();
    015                sb.AppendLine("I am rented by");
    016                sb.AppendLine(String.Format("Customer   : {0}", _toCustomer));
    017                sb.AppendLine(String.Format("From Date     :{0}", _fromDate));
    018                sb.AppendLine(String.Format("To Date       :{0}", _toDate));
    019                return sb.ToString();
    020            }
    021        }
    022    }
    023    
    024    //Concrete implementation
    025    public sealed class Car : Vehicle
    026    {
    027        public override string Description
    028        {
    029            get { return "I'm a car" + base.RentalInfo; }
    030        }
    031    }
    032
    033    //Concrete implementation
    034    public sealed class Helicopter : Vehicle
    035    {
    036        public override string Description
    037        {
    038            get { return "I'm a helicopter" + base.RentalInfo; }
    039        }
    040    }
    041
    042    //Concrete implementation
    043    public sealed class Jet : Vehicle
    044    {
    045        public override string Description
    046        {
    047            get { return "I'm a jet" + base.RentalInfo; }
    048        }
    049    }

    You can see the problem here we had to made changes both to our base class and concrete classes in order to meet the changing requirement. Another problem with our approach is that we would see empty rental information for the vehicles which were not rented, which means we have to add some code to check if rental information is valid in order to return correct information.  Yet another problem is that is our Vehicle class really have to be responsable for keeping the rental information or shall that be behaviour of another class. What if some time later our customer needed to make the vehicles customazable and rentable at the same time, we would change our classes all over again.

    When you find yourself applying lots of changes to your classes remeber the principle saying that "Keep your code closed for modifications but open for extensions". And then re-analyze the change requirement to see if you can apply the Decorator Pattern. As stated in the formal definition of Decorator Pattern in the GoF book "We can attach additional responsibilities to our objects dynamically without changing our objects. Decorators provide a flexible alternative to subclassing."

    Now lets apply decorator pattern for the same scenario. First of all we design an abstract VehicleDecorator class which will be used as the superclass of our concrete decortaor classes. Here is our abstract decorator

    001    public abstract class VehicleDecorator:Vehicle
    002    {
    003        protected Vehicle _decorate = null;
    004        public VehicleDecorator(Vehicle decorate)
    005        {
    006            _decorate = decorate;
    007        }
    008    }
     

    Then we create a concrete decorator class specially designed to decorate our vehicles with some rental information. Here is our RentableVehicle concrete decorator

    001    public class RentableVehicle : VehicleDecorator
    002    {
    003        private DateTime _fromDate;
    004        private DateTime _toDate;
    005        private string _toCustomer;
    006
    007        public RentableVehicle(Vehicle rent, DateTime from, DateTime to, string customer)
    008            : base(rent)
    009        {
    010            this._fromDate = from;
    011            this._toDate = to;
    012            this._toCustomer = customer;
    013        }
    014
    015        public override string Description
    016        {
    017            get 
    018            {
    019                StringBuilder sb = new StringBuilder();
    020                
    021                sb.AppendLine("Rentable " + base._decorate.Description);
    022                sb.AppendLine("I am rented by");
    023                sb.AppendLine(String.Format("Customer   : {0}", _toCustomer));
    024                sb.AppendLine(String.Format("From Date     :{0}", _fromDate));
    025                sb.AppendLine(String.Format("To Date       :{0}", _toDate));
    026                
    027                return sb.ToString();
    028            }
    029        } 
     

    As you can see we added additional rental related properties to our decorator and overriden the Description property so that we can return vehicle specific plus rental specific information.

    001    class Program
    002    {
    003        static void Main(string[] args)
    004        {
    005            Vehicle car = new Car();
    006
    007            RentableVehicle rentableCar = new RentableVehicle(car, DateTime.Now, DateTime.Now.AddDays(5), "Ali Özgür");
    008            Console.WriteLine(rentableCar.Description);
    009
    010            Console.WriteLine(String.Empty);
    011
    012            Vehicle jet = new Jet();
    013
    014            RentableVehicle rentableJet = new RentableVehicle(jet, DateTime.Now.AddDays(1), DateTime.Now.AddDays(3), "Seniha Özgür");
    015            Console.WriteLine(rentableJet.Description);
    016
    017            Console.Read();
    018        }
    019    } 

    When we want to extend our vehicles so that they can be customized we just add another concrete decorator names CustomizableVehicle.

    001   public class CustomizableVehicle:VehicleDecorator
    002    {
    003        public CustomizableVehicle(Vehicle vehicle)
    004            :base(vehicle)
    005        {
    006        
    007        }
    008
    009        public string CustomProperties { get;set; }
    010
    011        public override string Description
    012        {
    013            get 
    014            {
    015                return CustomProperties + " " + _decorate.Description;
    016            }
    017        }
    018    }

    And here is the test how we decorated our vehicles to be customizable and rentable at the same time without changing our abstract Vehicle classor concrete implementations of this class.

    001    class Program
    002    {
    003        static void Main(string[] args)
    004        {
    005            Vehicle jet = new Jet();
    006
    007            RentableVehicle rentableJet = new RentableVehicle(jet, DateTime.Now.AddDays(1), DateTime.Now.AddDays(3), "Seniha Özgür");
    008            Console.WriteLine(rentableJet.Description);
    009
    010            CustomizableVehicle cVehicle = new CustomizableVehicle(rentableJet);
    011            cVehicle.CustomProperties = "Red";
    012            Console.WriteLine(cVehicle.Description);
    013
    014            Console.Read();
    015
    016        }
    017    }


    Posted in: C# , GoF Patterns  Tags:

    Currently rated 4.0 by 1 people

    • Currently 4/5 Stars.
    • 1
    • 2
    • 3
    • 4
    • 5

    Comments


     Schneider
    June 4. 2008 22:30
    Schneider
    I would implement a IVehicleRental or IVehicleCustomizer interfaces instead.

    I don't like the decorator pattern myself. After a while, it will be hard to see in the code what ties to the Rental logic or Vehicle logic...


    no site


    June 5. 2008 13:37
    Ali Özgür
    Thank you for the comment.
    There is no common rule about how to make the right choice to favor interfaces over abstract classes or vice versa. It is possible for this demonstration to replace the abstract class with some interface but for sake of conformity with the rest of the design pattern vocabulary I've chosen to use an abstract class instead.

    My own experience is that if I want to impose some default implementation besides the contract I would choose to use abstract classes, if I want to define just a contract I would use choose to use interfaces. However as i said in this blog post I just wanted to conform to rest of the design patterns vocabularySmile

    You also say "After a while, it will be hard to see in the code what ties to the Rental logic or Vehicle logic..." I did not actually understand what you mean by this, I would be pleased if you give an example.

    http://www.pragmasql.com/


     Schneider
    June 5. 2008 21:31
    Schneider

    The explantaion of the patttern was good. I agree with your points. But part of learning patterns is when and why and why not to use them, and also possible other methods to use.

    In this case you also modify results for anyone who calls Description. Where you could provide a RentalDescription as part of the new rental functionality. This would have less impact on the existing codebase.

    The code will tend to reference both the Vehicle and the Decorator types as one new type, which make it more difficult to see where your code uses the Vehicle functionality or uses the new extended decorator functionality.

    no site


    June 6. 2008 11:58
    Ali Özgür
    Well,

    "modify results for anyone who calls Description" that is not true for the sample code I provided. Concrete Vehicle implementattions
    still provide their Description as expected and RentableVehicle only extends or provide extra behaviour, in our case Description plus
    rental info. The important point here is to keep our code "closed for modification but open for extension".

    Suppose that you purchased a library that provides some OCR (Optical Character Recognition) functionality. But you are not satisfied
    with the behaviour of the OCREngine class. For example you need some tabu words be removed from the result in some special cases. What would you do in
    such a case, you do not have the source code for OCREngine class and Recognize method of this class is not virtual (that is you can not override the method).
    You can not create your own MyOcrEngine class inherited from OCREngine. In this sample case you would create an Adapter class
    for the OCREngine, say that is OcrEngineAdapter, and then create a decorator class which removes tabu words from the recognized words collection.

    My own experience shows that even if you do not know any design patterns we use them all the time. Because
    1) I believe that design patterns are very intuitive and reflect some sort of inherent solutions to common problems.
    2) Development platforms and languages, for example .NET foundation classes, already implement most of the design patterns and we use
    them most of the time

    If you are an object oriented guy it's likely that you would be able to choose a right pattern most of the time. In our sample code
    , leaving out all that change management story, providing rental information is not actually a responsibility of our Vehicle class
    or its concrete implementations. To provide that functionality we need another object collaborating with our Vehicle class, if you can make
    this distinction next step would probabily be implementing some sort of decorator even if you do not know patterns.


    "reference both the Vehicle and the Decorator types as one new type" this can sometimes be a desired functionality. Suppose that you are the
    boss of the car rental company and you want to be able to see all operations through a monitor application. That is you want to see when a new car is
    purchased or rented. As the developer you could implement this requirement this way


    public class Monitor
    {
    public void Print(Vehicle vehicle)
    {
    StringBuilder sb = new StringBuilder();
    sb.AppendLine(vehicle.Description);

    if(!String.IsNullOrEmpty(vehicle.RentalInfo))
    sb.AppendLine(vehicle.Description);

    if(!String.IsNullOrEmpty(vehicle.CustomProperites))
    sb.AppendLine(vehicle.CustomProperties);

    Console.Write(sb.ToString());
    }
    }

    As you can see in the code above we added some sort of complexity to our Monitor class's Print method. If later on we decided to add
    some sort of Maintainance cost management behaviour to our vehicles, we would have to change our Vehicle class to include that information
    plus Print method of our Monitor class in order to print this info too. But if we used Decorator pattern our Print Method would be
    something like this

    public class Monitor
    {
    public void Print(Vehicle vehicle)
    {
    Console.Write(vehicle.Description);
    }
    }


    And we would simply create a new decorator class to support Maintainance cost management behaviour and without no changes to our Print
    method we would also be able to print this new mainainance information for our vehicles.

    http://www.pragmasql.com/


     schneider
    June 6. 2008 21:12
    schneider
    One note: I seem to get a lot of duplicate emails from your site. For the notifications.

    I agree a close API points (OCR lib), that is the case I think this pattern was intended.
    Other wise I would use composition or a interface.

    I agree using patterns is better than none.

    I agree sometimes:
    "reference both the Vehicle and the Decorator types as one new type"
    Just in general I like to isolate code exposure. Example pass in only parameters needed.

    If you reference an Rental type, and I later to change the functionality, it makes it easier to find all references that may need review.


    no site


    June 9. 2008 10:59
    Ali Özgür
    I modified my comment by deleting many times after publishing and that caused lots of e-mails to be sent, I'm sorry for the inconvenience.

    I partly agree with your last sentence. It can be hard to find all references in such a scenario but I think if you have unit tests you would not have to find references manually. Your test should fail if something goes wrong and then you must review your changes.

    As you said "using patterns is better than none". And I believe that patterns can be used as a powerful tool in order to build a common vocabulary that all team members share.

    http://www.pragmasql.com/


    August 28. 2008 14:21
    busby seo challenge
    I partly agree with your last sentence. It can be hard to find all references in such a scenario but I think if you have unit tests you would not have to find references manually. Your test should fail if something goes wrong and then you must review your changes.

    http://alaminos.net

    http://pinayspeak.com/


    October 3. 2008 15:53
    pingback
    Pingback from sehajpal.com

    Ashish Sehajpal

    http://www.sehajpal.com/blog/?p=91


    tr tolga kurkcuoglu
    October 10. 2008 01:40
    tolga kurkcuoglu
    it is an encouraged practice that you do not just declare an interface and build concrete classes on them but instead you build an abstract class on the interface and derive your concrete classes from that abstract class. that looked weird to me in the first sight but there is an overlooked advantage.

    let A and D be an abstract class implementing interface ISomeInterface
    let B be some class extending A
    let C be some class extending D

    then (ISomeInterface) B = (ISomeInterface) C but typeof(B) <> typeof(C)

    therefore, i gain the ability to further discriminate the type of B from type of C at runtime, as A and D. if it wasn't for the abstract classes in between the interface and the concrete classes, the concrete classes would be indistinguishable in this point of view. this fact might serve some practical OOP advantage.



    no site


    October 11. 2008 12:39
    aliozgur
    Good point tolga. Main purpose of abstract classes are hovewer implementing default behaviour for your interface and let consumers of your interfaces inherit from that abstract class and get the default behaviour.

    http://blog.pragmasql.com/

    Add comment


    (Will show your Gravatar icon)  

      Country flag

    [b][/b] - [i][/i] - [u][/u]- [quote][/quote]



    Live preview


     
    December 5. 2008 04:35

    no site