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 }
d8a0c731-e8eb-41e6-8768-4473c8b233f3|1|4.0