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 }
Currently rated 4.0 by 1 people
- Currently 4/5 Stars.
- 1
- 2
- 3
- 4
- 5