源文来自:https://zhuanlan.zhihu.com/p/342111312

image.pngimage.png
image.png
image.png
image.pngimage.pngimage.pngimage.pngimage.pngimage.pngimage.pngimage.png
image.pngimage.png
image.png

装饰器模式都包含哪些核心角色呢?

1. Component接口

在我们上面的例子中,Component接口相当于汽车接口,所有的被包装类、包装类,都继承于这个接口。

2. ConcreteComponent类

ConcreteComponent类是被包装的实现类。在例子中,奔驰汽车、宝马汽车、特斯拉汽车都属于这个角色。

3. Decorator抽象类

所有的包装类,都继承自Decorator抽象类,而Decorator类又实现了Component接口,这么做是为了实现多层嵌套包装。

4. ConcreteDecorator类

具体的包装类,用于扩充被包装类的功能,比如例子中的自动驾驶功能、飞行功能扩展。
image.png

首先是汽车接口,也就是Component这个角色,里面定义了run这个行为:

  1. public interface Car {
  2. void run();
  3. }

接下来是各种汽车的实现类,也就是ConcreteComponent角色,不同的汽车对于run行为有着不同的实现:

  1. public class BenzCar implements Car{
  2. @Override
  3. public void run() {
  4. System.out.println("奔驰开车了!");
  5. }
  6. }
  7. public class BmwCar implements Car{
  8. @Override
  9. public void run() {
  10. System.out.println("宝马开车了!");
  11. }
  12. }
  13. public class TeslaCar implements Car{
  14. @Override
  15. public void run() {
  16. System.out.println("特斯拉开车了!");
  17. }
  18. }

下面是装饰器的抽象类,也就是Decorator角色,这个角色包含了被装饰的成员对象(关联),同时还要实现Component接口,因为装饰器是在原有功能上添加更多功能:

  1. public class CarDecorator implements Car {
  2. protected Car decoratedCar;
  3. public CarDecorator(Car decoratedCar){
  4. this.decoratedCar = decoratedCar;
  5. }
  6. public void run(){
  7. decoratedCar.run();
  8. }
  9. }

包装的方式就是把Car对象(也即实现Car接口的具体车型)作为参数,传入到外层装饰器的构造函数当中,然后在该装饰器中就关联了该Car对象,同时还可以在原有Car的功能上,增加新的功能。

接下来是具体的装饰器实现类,也就是ConcreteDecorator角色。这些装饰器同样实现了run的行为,一方面会调用被包装对象的run方法,一方面会进行某些扩展操作(比如自动驾驶、飞行):

  1. public class AutoCarDecorator extends CarDecorator {
  2. //将具体车型进行关联
  3. public AutoCarDecorator(Car decoratedCar){
  4. super(decoratedCar);
  5. }
  6. @Override
  7. public void run(){
  8. decoratedCar.run();
  9. //增加新功能
  10. autoRun();
  11. }
  12. private void autoRun(){
  13. System.out.println("开启自动驾驶");
  14. }
  15. }
  16. public class FlyCarDecorator extends CarDecorator {
  17. public FlyCarDecorator(Car decoratedCar){
  18. super(decoratedCar);
  19. }
  20. @Override
  21. public void run(){
  22. decoratedCar.run();
  23. fly();
  24. }
  25. private void fly(){
  26. System.out.println("开启飞行汽车模式");
  27. }
  28. }

最后,是我们的客户端类。客户端类负责创建被包装对象和装饰者,并决定如何进行包装和执行:

  1. public class Client {
  2. public static void main(String[] args) {
  3. //创建Car对象
  4. Car benzCar = new BenzCar();
  5. Car bmwCar = new BmwCar();
  6. Car teslaCar = new TeslaCar();
  7. //创建自动驾驶的奔驰汽车
  8. CarDecorator autoBenzCar = new AutoCarDecorator(benzCar);
  9. //创建飞行的、自动驾驶的宝马汽车
  10. CarDecorator flyAutoBmwCar = new FlyCarDecorator(new AutoCarDecorator(bmwCar));
  11. benzCar.run();
  12. bmwCar.run();
  13. teslaCar.run();
  14. autoBenzCar.run();
  15. flyAutoBmwCar.run();
  16. }
  17. }

关键:装饰器继承自Car接口,可以让每一个装饰器本身也可以被更外层的装饰器所包装。因为继承后,每一个经过装饰器改造后的车还是属于Car对象,而装饰器本身可以关联Car对象(改造后的),然后对原有进行功能添加。

在实际环境的应用:

image.png
以输入流为例,为了满足不同输入场景,JDK设计了多种多样的输入流,包括ByteArrayInputStream、FileInputStream等等。
这些输入流都继承自共同的抽象类:InputStream。

与此同时,为了给这些输入流带来功能上的扩展,JDK设计了一个装饰器类,FilterInputStream。该类继承自InputStream,并且“组合”了InputStream成员对象。

从FilterInputStream类派生出了许多装饰器子类,包括BufferedInputStream,DataInputStream等等,分别提供了输入流缓冲,以及从输入流读取Java基本数据类型等额外功能。