
装饰模式是用来替代继承的一种设计模式。它通过一种无须定义子类的方式来给对象动态增加职责,使用对象之间的关联关系取代类之间的继承关系。降低了系统的耦合,可以动态的增加或者删除对象的职责。
装饰模式:动态地给一个对象增加一些额外的职责。就扩张功能而言,装饰模式提供了一种比使用子类更加灵活的替代方案。
装饰器模式作用是针对目标方法进行增强,提供新的功能或者额外的功能。
不同于适配器模式和桥接模式,装饰器模式涉及的是单方,和代理模式相同,而且目标必须是抽象的!!。
而实际上,装饰器模式和代理模式的实现方式基本一致,只在目标的存在上有些差别。
装饰模式的UML图不想画,借用一下
抽象构建类Component
package 装饰器;
public interface Component {
public void display();
}
具体构件类ConcreteComponent,实现接口Component
package 装饰器;
public class ConcreteComponent implements Component {
@Override
public void display() {
System.out.println("ConcreteComponent");
}
}
抽象装饰类Decorator,实现接口Component
package 装饰器;
public class Decorator implements Component{
public Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void display() {
component.display();
System.out.println(component + "+++" + "Decorator");
}
}
具体装饰类ConcreteDecoratorA,继承Decorator
package 装饰器;
public class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
public void display() {
super.display();
System.out.println("ConcreteDecoratorA");
}
}
具体装饰类ConcreteDecoratorB,继承Decorator
package 装饰器;
public class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB(Component component) {
super(component);
}
public void display() {
super.display();
System.out.println("ConcreteDecoratorB");
}
}
调用类Client
package 装饰器;
public class Client {
public static void main(String[] args) {
Component component = new ConcreteComponent();
component.display();
System.out.println("-------");
Component component1 = new ConcreteDecoratorA(component);
component1.display();
System.out.println("-------");
Component component2 = new ConcreteDecoratorB(component1);
component2.display();
System.out.println("-------");
}
}
运行结果:
ConcreteComponent ------- ConcreteComponent 装饰器.ConcreteComponent@60e53b93+++Decorator ConcreteDecoratorA ------- ConcreteComponent 装饰器.ConcreteComponent@60e53b93+++Decorator ConcreteDecoratorA 装饰器.ConcreteDecoratorA@5e2de80c+++Decorator ConcreteDecoratorB -------
分析结果:
Component component = new ConcreteComponent(); 实例化一个ConcreteComponent对象,执行component.display();,实际调用类ConcreteComponent的方法display(),打印”ConcreteComponent“。
构造过程:Component component1 = new ConcreteDecoratorA(component); 先调用ConcreteDecoratorA构造方法,执行 super(component); 调用父类Decorator 的构造方法,执行 this.component = component;,将传入参数 component 赋值给属性component。
调用过程:执行 component1.display(); 时,其实是执行ConcreteDecoratorA的方法display(),而其方法第一步super.display();执行父类Decorator的方法display()。而父类Decorator的方法display()第一步 component.display(); 执行上述传参的component 的方法display()。故依次打印 ConcreteComponent --> 装饰器.ConcreteComponent@60e53b93+++Decorator --> ConcreteDecoratorA。
构造过程:Component component2 = new ConcreteDecoratorB(component1); 先调用ConcreteDecoratorB构造方法,其构造方法 super(component); 调用父类Decorator 的构造方法 this.component = component;,将传入参 component1 赋值给属性component。
调用过程:执行 component2.display(); 时,其实是执行ConcreteDecoratorB的方法display(),而其方法第一步 super.display(); 执行父类Decorator的方法display()。而父类Decorator的方法display()第一步 component.display(); 执行上述传参的 component1 的方法display()。这一步流程就是上面 “执行component1.display(); ”的流程。 即打印ConcreteComponent --> 装饰器.ConcreteComponent@60e53b93+++Decorator --> ConcreteDecoratorA。然后再打印 装饰器.ConcreteDecoratorA@5e2de80c+++Decorator ,再打印ConcreteDecoratorB。
注:装饰的调用有点类似递归,形成一个调用链。
需求:如果想复用 具体装饰类ConcreteDecoratorA的既有功能,怎么处理? (该需求可以参见《SpringMVC-快速入门(6.2)- 自定义过滤器》里复用HttpServletRequestWrapper原有功能,然后自定义修改逻辑)
解决方案:编写装饰类继承ConcreteDecoratorA
package 装饰器;
public class ConcreteDecoratorD extends ConcreteDecoratorA {
public ConcreteDecoratord(Component component) {
super(component);
}
public void display() {
super.display();
System.out.println("ConcreteDecoratorD");
}
}
新增ConcreteDecoratorD继承ConcreteDecoratorA。
调用类Client:
package 装饰器;
public class Client {
public static void main(String[] args) {
Component component = new ConcreteComponent();
component.display();
System.out.println("-------");
Component component1 = new ConcreteDecoratorA(component);
component1.display();
System.out.println("-------");
Component component3 = new ConcreteDecoratord(component);
component3.display();
System.out.println("-------");
Component component4 = new ConcreteDecoratord(component1);
component4.display();
System.out.println("-------");
}
}
运行结果:
ConcreteComponent ------- ConcreteComponent ConcreteDecoratorA ------- ConcreteComponent ConcreteDecoratorA ConcreteDecoratorD ------- ConcreteComponent ConcreteDecoratorA ConcreteDecoratorA ConcreteDecoratorD -------
结果可以看出,ConcreteDecoratorD.display()执行之前均会先执行其父类ConcreteDecoratorA.display()。
优点装饰类的代码编写和代理很类似,具体区别如下:
快餐店有炒面、炒饭这些快餐,可以额外附加鸡蛋、火腿、培根这些配菜,加配菜需要额外加钱,并且每个配菜的价钱不一样,计算快餐价格如何实现?
继承的方式:这是继承的方式,但是横向扩展性不好:如果要再加一种配料(火腿肠),我们就会发现需要给FriedRice和FriedNoodles分别定义一个子类。如果要新增一个快餐品类(炒河粉)的话,就需要定义更多的子类,会出现类爆炸的问题。
装饰者模式: 注:图片网上找的,大致意思符合,和下面代码不对应。囧。。。 代码实现抽象构件FastFood:
package 装饰器;
public interface FastFood {
public float getCountPrice();
public String getDesc();
}
具体构件RiceFried(炒饭):
package 装饰器;
public class RiceFried implements FastFood {
public float price = 10.0f;
public String desc = "炒饭";
@Override
public float getCountPrice() {
return price;
}
@Override
public String getDesc() {
return desc;
}
}
抽象装饰:
package 装饰器;
public class FoodDecorator implements FastFood {
public FastFood fastFood;
public FoodDecorator(FastFood fastFood) {
this.fastFood = fastFood;
}
@Override
public float getCountPrice() {
float countPrice = fastFood.getCountPrice();
return countPrice;
}
@Override
public String getDesc() {
String desc = fastFood.getDesc();
return desc;
}
}
具体装饰EggDecrator(鸡蛋)
package 装饰器;
public class EggDecrator extends FoodDecorator {
public float price = 2.0f;
public String desc = "鸡蛋";
public EggDecrator(FastFood fastFood) {
super(fastFood);
}
public float getCountPrice() {
float countPrice = super.getCountPrice();
countPrice += price;
return countPrice;
}
public String getDesc() {
String desc = super.getDesc();
desc = this.desc + desc;
return desc ;
}
}
具体装饰BaconDecrator(培根)
package 装饰器;
public class BaconDecrator extends FoodDecorator{
public float price = 3.0f;
public String desc = "培根";
public BaconDecrator(FastFood fastFood) {
super(fastFood);
}
public float getCountPrice() {
float countPrice = super.getCountPrice();
countPrice += price;
return countPrice;
}
@Override
public String getDesc() {
String desc = super.getDesc();
desc = this.desc +desc ;
return desc ;
}
}
调用:
package 装饰器;
public class Shopping {
public static void main(String[] args) {
FastFood RiceFried = new RiceFried();
System.out.println(RiceFried.getDesc() + ":" + RiceFried.getCountPrice());
FastFood eggDecrator = new EggDecrator(RiceFried);
System.out.println(eggDecrator.getDesc() + ":" + eggDecrator.getCountPrice());
BaconDecrator baconDecrator = new BaconDecrator(RiceFried);
System.out.println(baconDecrator.getDesc() + ":" + baconDecrator.getCountPrice());
BaconDecrator baconDecrator2 = new BaconDecrator(eggDecrator);
System.out.println(baconDecrator2.getDesc() + ":" + baconDecrator2.getCountPrice());
}
}
运行结果:
炒饭:10.0 鸡蛋炒饭:12.0 培根炒饭:13.0 培根鸡蛋炒饭:15.0
如果要再加一种配料(火腿肠),我们只需要定义一个修饰类。
如果要新增一个快餐品类(炒河粉)的话,我们只需要定义一个具体构件类。