Who is Ali Özgür?

RecentComments

Comment RSS

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
003    public abstract class Vehicle
004
005    {
006
007        public abstract string Description { get; }
008
009
010
011    }
012
013    
014
015    //Concrete implementation
016
017    public sealed class Car : Vehicle
018
019    {
020
021        public override string Description
022
023        {
024
025            get { return "Car"; }
026
027        }
028
029    }
030
031
032
033    //Concrete implementation
034
035    public sealed class Helicopter : Vehicle
036
037    {
038
039        public override string Description
040
041        {
042
043            get { return "Helicopter"; }
044
045        }
046
047    }
048
049
050
051    //Concrete implementation
052
053    public sealed class Jet : Vehicle
054
055    {
056
057        public override string Description
058
059        {
060
061            get { return "Jet"; }
062
063        }
064
065    }

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
003    public abstract class Vehicle
004
005    {
006
007        public virtual DateTime FromDate { get; set; }
008
009        public virtual DateTime ToDate { get; set; }
010
011        public virtual string CustomerName { get; set; }
012
013
014
015        public abstract string Description { get; }
016
017
018
019        protected string RentalInfo
020
021        {
022
023            get 
024
025            {
026
027                StringBuilder sb = new StringBuilder();
028
029                sb.AppendLine("I am rented by");
030
031                sb.AppendLine(String.Format("Customer   : {0}", _toCustomer));
032
033                sb.AppendLine(String.Format("From Date     :{0}", _fromDate));
034
035                sb.AppendLine(String.Format("To Date       :{0}", _toDate));
036
037                return sb.ToString();
038
039            }
040
041        }
042
043    }
044
045    
046
047    //Concrete implementation
048
049    public sealed class Car : Vehicle
050
051    {
052
053        public override string Description
054
055        {
056
057            get { return "I'm a car" + base.RentalInfo; }
058
059        }
060
061    }
062
063
064
065    //Concrete implementation
066
067    public sealed class Helicopter : Vehicle
068
069    {
070
071        public override string Description
072
073        {
074
075            get { return "I'm a helicopter" + base.RentalInfo; }
076
077        }
078
079    }
080
081
082
083    //Concrete implementation
084
085    public sealed class Jet : Vehicle
086
087    {
088
089        public override string Description
090
091        {
092
093            get { return "I'm a jet" + base.RentalInfo; }
094
095        }
096
097    }

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    {
004
005        protected Vehicle _decorate = null;
006
007        public VehicleDecorator(Vehicle decorate)
008
009        {
010
011            _decorate = decorate;
012
013        }
014
015    }
 

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    {
004
005        private DateTime _fromDate;
006
007        private DateTime _toDate;
008
009        private string _toCustomer;
010
011
012
013        public RentableVehicle(Vehicle rent, DateTime from, DateTime to, string customer)
014
015            : base(rent)
016
017        {
018
019            this._fromDate = from;
020
021            this._toDate = to;
022
023            this._toCustomer = customer;
024
025        }
026
027
028
029        public override string Description
030
031        {
032
033            get 
034
035            {
036
037                StringBuilder sb = new StringBuilder();
038
039                
040
041                sb.AppendLine("Rentable " + base._decorate.Description);
042
043                sb.AppendLine("I am rented by");
044
045                sb.AppendLine(String.Format("Customer   : {0}", _toCustomer));
046
047                sb.AppendLine(String.Format("From Date     :{0}", _fromDate));
048
049                sb.AppendLine(String.Format("To Date       :{0}", _toDate));
050
051                
052
053                return sb.ToString();
054
055            }
056
057        } 
 

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    {
004
005        static void Main(string[] args)
006
007        {
008
009            Vehicle car = new Car();
010
011
012
013            RentableVehicle rentableCar = new RentableVehicle(car, DateTime.Now, DateTime.Now.AddDays(5), "Ali Özgür");
014
015            Console.WriteLine(rentableCar.Description);
016
017
018
019            Console.WriteLine(String.Empty);
020
021
022
023            Vehicle jet = new Jet();
024
025
026
027            RentableVehicle rentableJet = new RentableVehicle(jet, DateTime.Now.AddDays(1), DateTime.Now.AddDays(3), "Seniha Özgür");
028
029            Console.WriteLine(rentableJet.Description);
030
031
032
033            Console.Read();
034
035        }
036
037    } 

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    {
004
005        public CustomizableVehicle(Vehicle vehicle)
006
007            :base(vehicle)
008
009        {
010
011        
012
013        }
014
015
016
017        public string CustomProperties { get;set; }
018
019
020
021        public override string Description
022
023        {
024
025            get 
026
027            {
028
029                return CustomProperties + " " + _decorate.Description;
030
031            }
032
033        }
034
035    }

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    {
004
005        static void Main(string[] args)
006
007        {
008
009            Vehicle jet = new Jet();
010
011
012
013            RentableVehicle rentableJet = new RentableVehicle(jet, DateTime.Now.AddDays(1), DateTime.Now.AddDays(3), "Seniha Özgür");
014
015            Console.WriteLine(rentableJet.Description);
016
017
018
019            CustomizableVehicle cVehicle = new CustomizableVehicle(rentableJet);
020
021            cVehicle.CustomProperties = "Red";
022
023            Console.WriteLine(cVehicle.Description);
024
025
026
027            Console.Read();
028
029
030
031        }
032
033    }


Posted in: C# , GoF Patterns  Tags:

Comments


 Schneider
June 4. 2008 20: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 11: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 19: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 09: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 19: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 08: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 12: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 13:53
pingback
Pingback from sehajpal.com

Ashish Sehajpal

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


Turkey tolga kurkcuoglu
October 9. 2008 23: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 10: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/


May 20. 2010 22:52
pingback
Pingback from 302.1fh.org

Volvo 940 Window Repair Timing Belt, Chippa 940 Turbo

   http://302.1fh.org/post/146051/

Comments are closed