设计模式篇——初探装饰器模式
文章目录
1、装饰器模式介绍
2、装饰器模式类图
3、装饰器模式Demo实现(一个小镇的拉面馆)
4、装饰器模式总结
装饰器模式介绍:装饰器模式可以在不修改任何底层代码的情况下,给对象赋予新的职责(程序运行时的扩展,动态的将责任附加到对象上)。属于结构型设计模式。
类图:
我们来看下装饰器模式的类图:
一个简单的Demo(故乡小镇的一个面馆):
在故乡的一个小镇上面,有一家面馆,主营拉面。在这里你可以只点清汤面(SoupNoodle),也可以往里面加佐料,佐料有牛肉(Beef),鱼丸(FishBall)还有菠菜(Spinach)。面馆今天开张了。
这里的所有面条都基于一个抽象类BaseNoodle来实现,这个抽象类有两个抽象方法,获取它的价格(double Price()),获取它的名字(string GetName())。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /// <summary> 2 /// 面条抽象类 3 /// </summary> 4 public abstract class BaseNoodle 5 { 6 /// <summary> 7 /// 价格 8 /// </summary> 9 /// <returns></returns> 10 public abstract double Price(); 11 /// <summary> 12 /// 获取名称 13 /// </summary> 14 /// <returns></returns> 15 public abstract string GetName(); 16 }
然后让我们实现下我们最基础的清汤面(SoupNoodle),清汤面的价格是1块钱(嗯,还蛮实惠的)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /// <summary> 2 /// 清汤面 3 /// </summary> 4 public class SoupNoodle : BaseNoodle 5 { 6 private static double cost = 1; 7 public override string GetName() 8 { 9 return "清汤面"; 10 } 11 public override double Price() 12 { 13 return cost; 14 } 15 }
这时候,我们来了第一位客人(张三),他要一碗带牛肉作料的清汤面
于是乎,我们就实现了这样一个类。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class SoupNoodleWithBeef : BaseNoodle 2 { 3 private static double cost = 1; 4 public override string GetName() 5 { 6 return "清汤面" + ",牛肉"; 7 } 8 /// <summary> 9 /// 假设牛肉一份0.6元 10 /// </summary> 11 /// <returns></returns> 12 public override double Price() 13 { 14 return cost + 0.6; 15 } 16 }
然后第二个客人进来了,是个可爱的小姑娘,她要一份加菠菜的清汤面。于是乎,我们又要实现这样一个类。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class SoupNoodleWithSpinach : BaseNoodle 2 { 3 private static double cost = 1; 4 public override string GetName() 5 { 6 return "清汤面" + ",菠菜"; 7 } 8 /// <summary> 9 /// 假设菠菜一份0.2元 10 /// </summary> 11 /// <returns></returns> 12 public override double Price() 13 { 14 return cost + 0.2; 15 } 16 }
我们一共有三种佐料,假设客人的口味都不同,那样的话我们需要多少个继承自BaseNoodle的子类呢? 没错,应该是A(3,3)个6个子类。这样显然不行,假如我们后期有添加了新的佐料,虾球,那样我们的子类个数就是24个,况且谁又能保证客人只点一份相同的佐料呢?假如点两份牛肉呢?我们的子类个数将呈现指数级别的增长。。。
这时候我们的装饰器模式就登场了。
还是我们的面条基类抽象类,和清汤面(被装饰者)类,在这个的基础之上我们将不再写很多针对细节的子类。我们首先实现一个佐料抽象类(SeasoningDecorator),这个抽象类也要继承自BaseNoodle。它内部有一个实例变量=》BaseNoodle
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /// <summary> 2 /// 基础佐料类 3 /// </summary> 4 public abstract class SeasoningDecorator : BaseNoodle 5 { 6 private BaseNoodle _baseNoodle = null; 7 public SeasoningDecorator(BaseNoodle baseNoodle) 8 { 9 _baseNoodle = baseNoodle; 10 } 11 12 public override string GetName() 13 { 14 return this._baseNoodle.GetName(); 15 } 16 17 public override double Price() 18 { 19 return this._baseNoodle.Price(); 20 } 21 }
此时,我们定义我们的具体佐料类,这些佐料类都继承自SeasoningDecorator,而且内部都存在一个实例变量=》BaseNoodle。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /// <summary> 2 /// 牛肉 3 /// </summary> 4 public class BeefDecorator: SeasoningDecorator 5 { 6 private static double cost = 0.6; 7 private BaseNoodle _baseNoodle = null; 8 public BeefDecorator(BaseNoodle baseNoodle) : base(baseNoodle) 9 { 10 _baseNoodle = baseNoodle; 11 } 12 public override string GetName() 13 { 14 return this._baseNoodle.GetName() + ",牛肉"; 15 } 16 public override double Price() 17 { 18 return this._baseNoodle.Price() + cost; 19 } 20 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /// <summary> 2 /// 鱼丸 3 /// </summary> 4 public class FishBallDecorator : SeasoningDecorator 5 { 6 private static double cost = 0.4; 7 private BaseNoodle _baseNoodle = null; 8 public override string GetName() 9 { 10 return this._baseNoodle.GetName() + ",鱼丸"; 11 } 12 public FishBallDecorator(BaseNoodle baseNoodle) : base(baseNoodle) 13 { 14 _baseNoodle = baseNoodle; 15 } 16 public override double Price() 17 { 18 return this._baseNoodle.Price() + cost; 19 } 20 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /// <summary> 2 /// 菠菜 3 /// </summary> 4 public class Spinach : SeasoningDecorator 5 { 6 private static double cost = 0.2; 7 private BaseNoodle _baseNoodle = null; 8 public override string GetName() 9 { 10 return this._baseNoodle.GetName() + ",菠菜"; 11 } 12 public Spinach(BaseNoodle baseNoodle) : base(baseNoodle) 13 { 14 _baseNoodle = baseNoodle; 15 } 16 public override double Price() 17 { 18 return this._baseNoodle.Price() + cost; 19 } 20 }
这些具体的佐料类就是我们的装饰器。因为它构造函数接收一个BaseNoodle,所以我们可以这样来实现对清汤面(SoupNoodle)的装饰:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //定义清汤面 2 BaseNoodle baseSoupNoodle = new SoupNoodle(); 3 //添加一份牛肉 4 baseSoupNoodle = new BeefDecorator(baseSoupNoodle); 5 //添加一份鱼丸 6 baseSoupNoodle = new FishBallDecorator(baseSoupNoodle); 7 //添加一份菠菜 8 baseSoupNoodle = new Spinach(baseSoupNoodle); 9 //再添加一份牛肉 10 baseSoupNoodle = new BeefDecorator(baseSoupNoodle); 11 Console.WriteLine($"点了一份{baseSoupNoodle.GetName()},价格为{baseSoupNoodle.Price()}"); 12 Console.Read();
装饰器模式总结:
- 装饰器属于结构型设计模式,很好的遵循了开闭原则。
- 装饰器模式的装饰者与被装饰者有相同的超类型。
- 装饰器模式可以在程序运行时,以组合的方式,动态的给对象添加行为(因为有相同的超类型,所以任何需要原始对象的场合,都可以用装饰过的对象去替代它)。
- 装饰器模式会出现很多的小的类型。