装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。

上面引用来自百度百科,装饰模式,通过一个装饰类来包裹真实对象,先关注几个点:

  1. 不改变原类文件

  2. 不使用继承

  3. 动态扩展

下面这张类图也是来自百度百科,我先写装饰模式,后面再扩展一些东西。
Component:待装饰的接口
ConcreteComponent:待装饰的原始类,实现了待装饰接口
Decorator:装饰抽象父类,继承待装饰接口
ConcreteDecorator:装饰子类,实现装饰功能
装饰模式 - 图1
最近在看手机,所以我打算用手机这个例子来实现一下装饰模式

  1. //待装饰接口
  2. public interface Phone {
  3. //买手机方法
  4. void buy();
  5. }
  1. //待装饰原始类,实现了待装饰接口
  2. public class MiPhone implements Phone {
  3. @Override
  4. public void buy() {
  5. System.out.println("买了一部小米");
  6. }
  7. }
  1. //抽象装饰类
  2. public abstract class Decorator implements Phone {
  3. private Phone phone;
  4. //构造方法,传入待装饰原始类
  5. public Decorator(Phone phone) {
  6. this.phone = phone;
  7. }
  8. @Override
  9. public void buy() {
  10. phone.buy();
  11. }
  12. }
  1. //装饰子类-手机壳(类)
  2. public class KeConcreteDecorator extends Decorator {
  3. public KeConcreteDecorator(Phone phone) {
  4. super(phone);
  5. }
  6. public void buy() {
  7. super.buy();
  8. System.out.println("买了一个漂亮的手机壳给手机套上");
  9. }
  10. }
  1. //装饰子类-手机膜(类)
  2. public class MoConcreteDecorator extends Decorator {
  3. public MoConcreteDecorator(Phone phone) {
  4. super(phone);
  5. }
  6. public void buy() {
  7. super.buy();
  8. System.out.println("买了张钢化膜给手机贴上");
  9. }
  10. }

下面是客户端调用

  1. //客户端
  2. public class DecoratorDemo {
  3. public static void main(String[] args) {
  4. //创建小米手机实例
  5. Phone phone = new MiPhone();
  6. //1.只买了手机壳
  7. Decorator ke = new KeConcreteDecorator(phone);
  8. ke.buy();
  9. System.out.println("================================");
  10. //2.只买了手机膜
  11. Decorator mo = new MoConcreteDecorator(phone);
  12. mo.buy();
  13. System.out.println("================================");
  14. //3.同时买了手机壳和手机膜
  15. Decorator mo2 = new MoConcreteDecorator(ke);
  16. mo2.buy();
  17. }
  18. }

这里演示了三种情况
装饰模式 - 图2


装饰模式和静态代理模式

初初看装饰模式的时候,瞬间冒出一个想法,这模式跟代理模式很像哦。

  1. 跟原始类解耦
    无论是装饰类或者是代理类,发现都是在不改动原始类的基础上,附加新的功能

  2. 客户端调用方式很像

装饰模式 - 图3

装饰模式 - 图4
示例中代理类和装饰类的时候,都是传入了那个具体的对象。

下面来解答上面两个问题
是解耦应该没错,在不改动原始类的基础上,实现同一个(待装饰或被代理)接口。但是思想观念上面有一个很大区别。

装饰模式:

  1. 强调的是增强

  2. 客户端主动行为
    客户端主动的行为。像上面的例子,客户端买到了一个小米手机,是一个裸机,打算花点钱买手机壳或手机膜装饰一下手机,可以根据客户端的想法选择装饰。但这是在程序在运行的时候,才增强的功能(装饰)。

静态代理模式:

  1. 强调的是限制

  2. 客户端被动行为

(上图创建userManager的时候,其实是不严谨的,因为我将需要代理的具体对象暴露了出来。创建的具体实例应该在代理类的构造方法中完成)使用静态代理模式模式的时候,客户端是无感的(被动),即客户端并不知道是哪个真实对象(被代理类)。所以在编译阶段,代理类已经指定了使用哪个被代理类(限制)。


现在又回到最上方那句引用有3个关注点
第一点就很清楚了。
第二点,“不使用继承”。

  1. public class KeDemo extends MiPhone {
  2. public void buy() {
  3. super.buy();
  4. System.out.println("买了一个漂亮的手机壳给手机套上");
  5. }
  6. }
  7. public class MoDemo extends MiPhone{
  8. public void buy() {
  9. super.buy();
  10. System.out.println("买了张钢化膜给手机贴上");
  11. }
  12. }
  13. public class ClientDemo {
  14. public static void main(String[] args) {
  15. MoDemo md = new MoDemo();
  16. md.buy();
  17. System.out.println("====================");
  18. KeDemo kd = new KeDemo();
  19. kd.buy();
  20. }
  21. }

装饰模式 - 图5
第二点是不能使用这种继承方式,虽然能实现部分的功能,但是就不能达到第三点的要求了,“动态扩展”。这个动态扩展怎么理解?像上面装饰模式的客户端中第三种使用方法,可以将另外一种装饰子类作为构造方法的参数传递(由于他们的顶级父类都是phone),可以同时实现多种装饰,这就是动态扩展了。


装饰模式就介绍到这里了,谢谢大家。