1、装饰模式概要
装饰者模式(Decorator):动态地为一个对象添加一些额外的职责,若要扩展一个对象的功能,装饰者提供了比继承更有弹性的替代方案。
- 抽象构件类(Component):给出一个抽象的接口,用以规范准备接收附加责任的对象
- 具体构件类(ConcreteComponent):定义一个具体的准备接受附加责任的类,其必须实现Component接口。
- 装饰者类(Decorator):持有一个构件(Conponent)对象的实例,并定义一个和抽象构件一致的接口。
- 具体装饰者类(Concrete Decoratator):定义给构件对象“贴上”附加责任。
装饰者模式具有的一些特征
1,装饰者(decorator)和被装饰(扩展)的对象有着相同的超类(supertype)。
2,我们可以用多个装饰者去装饰一个对象。
3,我们可以用装饰过的对象替换代码中的原对象,而不会出问题(因为他们有相同的超类)。
4,装饰者可以在委托(delegate,即调用被装饰的类的成员完成一些工作)被装饰者的行为完成之前或之后加上他自己的行为。
5,一个对象能在任何时候被装饰,甚至是运行时。
简单来说:装饰模式即为动态的扩展,而不是修改
我们想象一个场景,你是一个卖鸡蛋饼的小贩,鸡蛋饼的原始组成是一个面饼一个鸡蛋,但是有些顾客相加油条(奇葩的想法),或者想加烤肠
在你编写类的时候,如果初始创建了一个鸡蛋饼类里面存在鸡蛋饼的价格,那么加油条的时候,这个类就无法使用了就不得不编写一个新的类
假设原始的鸡蛋饼类为这样
public class Waffle {
private String name = "鸡蛋饼";
private double cost = 3.5;
public void Describe(){
System.out.println("我是" + name);
}
public void Price(){
System.out.println("价格是" + cost);
}
}
有同学说,我可以继承鸡蛋饼这个类,编写一个加油条的方法
那么如果你添加的种类多的话,就会想下面这个图一样
造成类的爆炸
还有同学说我可以在其中编写方法,来添加油条
public class Waffle {
private String name = "鸡蛋饼";
private double cost = 3.5;
private double chinese_Donut = 1;
public void Describe(){
System.out.println("我是" + name);
}
public void Price(){
System.out.println("价格是" + cost);
}
public void addDonut(){
this.name = name + "加了油条";
this.cost = cost + chinese_Donut;
}
}
如果是这样的话,那么问题就来了,我添加油条是一个方法,添加烤肠又是一个方法,如果东西多了的话,这个类需要添加无尽个方法,而且如果有一天不卖油条了,我还得进来删除油条这个方法。
我们所希望达到的情况是动态的扩展,而不是无尽的修改源码
那么这时候引入装饰模式就很有必要了
2、装饰模式案例代码
抽象构建类(Component)
public abstract class Component {
abstract void method(); // 抽象方法
}
具体构建类(Concrete Component)
public class ConcreteComponent extends Component{
@Override
public void method() {
// 实现抽象方法
}
}
装饰者类(Decorator)
public abstract class Decorator extends Component{
public abstract void method();
}
具体装饰者类(Concrete Decoratator)
public class Concrete_Decoratator extends CondimentDecorator{
Concrete_Decoratator concrete_Decoratator
public Concrete_Decoratator(Concrete_Decoratator concrete_Decoratator){
this.Concrete_Decoratator = concrete_Decoratator;
}
@Override
public void method() {
// 实现抽象方法
}
}
3、案例代码
我们现在真的是一个卖鸡蛋饼的小贩,我们手里能添加的东西目前有三个,里脊肉、烤肠、培根
我们希望编写一个类,能够动态的计算出价格和名称
抽象构建类(Component)
public abstract class Pancake {
public String describe = "我是一种煎饼,但我不知道我具体是谁";
public abstract void money();
}
具体构建类(Concrete Component)
public class Waffle extends Pancake {
public Waffle() {
setDescribe("鸡蛋饼");
}
@Override
public void money() {
setCost(3.5);
System.out.println(getCost());
}
}
装饰者类(Decorator)
public abstract class Condiment extends Pancake {
public abstract String getDescribe();
}
具体装饰者类(Concrete Decoratator)
烤肠类
public class Sausage extends Condiment {
public Pancake pancake;
public Sausage(Pancake pancake) {
this.pancake = pancake;
}
@Override
public String getDescribe() {
return pancake.getDescribe() + ",烤肠";
}
@Override
public double money() {
return 2 + pancake.money();
}
}
培根类
public class Baconic extends Condiment {
private Pancake pancake;
public Baconic(Pancake pancake) {
this.pancake = pancake;
}
@Override
public String getDescribe() {
return pancake.getDescribe() + ",培根";
}
@Override
public double money() {
return 2 + pancake.money();
}
}
里脊肉类
public class Tenderloin extends Condiment {
public Pancake pancake;
public Tenderloin(Pancake pancake) {
this.pancake = pancake;
}
@Override
public String getDescribe() {
return pancake.getDescribe() + ",里脊肉";
}
@Override
public double money() {
return 3 + pancake.money();
}
}
测试类
public class Main {
public static void main(String[] args) {
Pancake waffle = new Waffle(); //初始化鸡蛋饼
waffle = new Tenderloin(waffle); // 鸡蛋饼添加里脊肉
waffle = new Baconic(waffle); // 鸡蛋饼添加培根
System.out.println(waffle.getDescribe());
System.out.println(waffle.money());
}
}
控制台输出
这样就完成了动态装饰,其中的原理还需要慢慢体会。尤其是多态这一块。