装饰器模式

简介

装饰器模式(Decorator Pattern)也叫作包装器模式(Wrapper Pattern),指在不改变原有对象的基础上,动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活,属于结构型设计模式。

通用模板

  1. 创建抽象组件接口:可以是一个接口或者抽象类,充当被装饰类的原始对象,规定了被装饰对象的行为。

    java 复制代码
    // 被装饰的组件接口
    public interface IComponent {
        // 被装饰对象的行为
        void describe();
    }
  2. 创建具体组件:实现/继承Component的一个具体对象,即被装饰对象。

    java 复制代码
    // 具体的被装饰类
    public class Component implements IComponent{
        @Override
        public void describe() {
            System.out.println("我是未被装饰的基础组件");
        }
    }
  3. 创建抽象装饰器基类:通用的装饰ConcreteComponent的装饰器,其内部必然有一个属性指向Component;其实现一般是一个抽象类,主要为了让其子类按照其构造形式传入一个Component,这是强制的通用行为。如果系统中装饰逻辑单一,则并不需要实现许多装饰器,可以直接省略该类,而直接实现一个具体装饰器即可。

    java 复制代码
    // 装饰器基类
    public abstract class BaseDecorator implements IComponent {
        private IComponent iComponent;
    
        public BaseDecorator(IComponent iComponent) {
            this.iComponent = iComponent;
        }
    
        @Override
        public void describe() {
            iComponent.describe();
        }
    }
  4. 创建具体装饰器类:理论上,每个ConcreteDecorator都扩展了Component对象的一种功能。

    java 复制代码
    // 具体的装饰器类A
    public class DecoratorA extends BaseDecorator {
        public DecoratorA(IComponent iComponent) {
            super(iComponent);
        }
    
        @Override
        public void describe() {
            super.describe();
            System.out.println("我被装饰器A装饰了");
        }
    }
    java 复制代码
    // 具体的装饰器类B
    public class DecoratorB extends BaseDecorator {
        public DecoratorB(IComponent iComponent) {
            super(iComponent);
        }
    
        @Override
        public void describe() {
            super.describe();
            System.out.println("我被装饰器B装饰了");
        }
    }

模板测试

  1. 代码

    java 复制代码
    public class Client {
        public static void main(String[] args) {
            Component component = new Component();
            // 用装饰器A来装饰组件
            DecoratorA decoratorA = new DecoratorA(component);
    
            // 用装饰器B来装饰已经被装饰器A装饰过后的组件
            DecoratorB decoratorB = new DecoratorB(decoratorA);
            decoratorB.describe();
        }
    }
  2. 结果

    java 复制代码
    我是未被装饰的基础组件
    我被装饰器A装饰了
    我被装饰器B装饰了

应用场景

来看这样一个场景,上班族大多有睡懒觉的习惯,每天早上上班时间都很紧张,于是很多人为了多睡一会儿,就用更方便的方式解决早餐问题,有些人早餐可能会吃煎饼。煎饼中可以加鸡蛋,也可以加香肠,但是不管怎么"加码",都还是一个煎饼。再比如,给蛋糕加上一些水果,给房子装修,都是装饰器模式。 装饰器模式在代码程序中适用于以下应用场景。

(1)用于扩展一个类的功能,或者给一个类添加附加职责。

(2)动态地给一个对象添加功能,这些功能可以再动态地被撤销。

(3)需要为一批平行的兄弟类进行改装或加装功能。

优点

(1)装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态地给一个对象扩展功能,即插即用。

(2)通过使用不同装饰类及这些装饰类的排列组合,可以实现不同效果。

(3)装饰器模式完全遵守开闭原则。

缺点

(1)会出现更多的代码、更多的类,增加程序的复杂性。

(2)动态装饰在多层装饰时会更复杂。

"生搬硬套"实战

场景描述

让我们通过一个给房子装修的场景来演示装饰器模式。假设我们有一栋房子,需要逐步对其进行装修,包括刷墙漆、安装窗帘、挂画等。

  1. 创建抽象组件(这里指房子)接口

    java 复制代码
    public interface House {
        void describe();
    }
  2. 创建具体组件(这里指空房子)

    java 复制代码
    public class EmptyHouse implements House {
        @Override
        public void describe() {
            System.out.println("This is an empty house.");
        }
    }
  3. 创建抽象装饰器(这里指装修)基类

    java 复制代码
    public abstract class HouseDecorator implements House {
        protected House house;
    
        public HouseDecorator(House house) {
            this.house = house;
        }
    
        @Override
        public void describe() {
            house.describe();
        }
    }
  4. 创建具体装饰器(这里指刷墙漆、安装窗帘、挂艺术作品)类

    java 复制代码
    public class PaintDecorator extends HouseDecorator {
        private String color;
    
        public PaintDecorator(House house, String color) {
            super(house);
            this.color = color;
        }
    
        @Override
        public void describe() {
            super.describe();
            System.out.println("The walls have been painted " + color + ".");
        }
    }
    java 复制代码
    public class CurtainDecorator extends HouseDecorator {
        private String material;
    
        public CurtainDecorator(House house, String material) {
            super(house);
            this.material = material;
        }
    
        @Override
        public void describe() {
            super.describe();
            System.out.println("There are " + material + " curtains installed.");
        }
    }
    java 复制代码
    public class ArtworkDecorator extends HouseDecorator {
        private int numberOfPieces;
    
        public ArtworkDecorator(House house, int numberOfPieces) {
            super(house);
            this.numberOfPieces = numberOfPieces;
        }
    
        @Override
        public void describe() {
            super.describe();
            System.out.println("There are " + numberOfPieces + " pieces of art hanging on the walls.");
        }
    }

至此,我们就通过"生搬硬套"装饰器模式的模板设计出一套装修房子的代码,接下来我们进行测试:

  • 测试代码

    java 复制代码
    public class Main {
        public static void main(String[] args) {
            // 创建一个空房子
            House emptyHouse = new EmptyHouse();
            
            // 装修:刷墙漆
            House paintedHouse = new PaintDecorator(emptyHouse, "blue");
            
            // 装修:安装窗帘
            House curtainedHouse = new CurtainDecorator(paintedHouse, "silk");
            
            // 装修:挂艺术作品
            House decoratedHouse = new ArtworkDecorator(curtainedHouse, 5);
            
            // 描述最终的装修结果
            decoratedHouse.describe();
        }
    }
  • 结果

    java 复制代码
    This is an empty house.
    The walls have been painted blue.
    There are silk curtains installed.
    There are 5 pieces of art hanging on the walls.

装饰器模式与代理模式的区别

这两种模式都是扩展某个类的功能,但是这两种设计模式所面向的功能扩展面是不一样的。

  • 装饰器模式强调自身功能的扩展
  • 代理模式强调对代理过程的控制

简单来讲,假设现在小明想租房,那么势必会有一些事务发生:房源搜索、联系房东谈价格等。 假设按照代理模式进行思考,那么小明只需找到一个房产中介,让他去做房源搜索、联系房东谈价格这些事情,小明只需等待通知然后付中介费就行了。 而如果采用装饰器模式进行思考,因为装饰器模式强调的是自身功能扩展,也就是说,如果要找房子,小明自身就要增加房源搜索能力扩展、联系房东谈价格能力扩展,通过相应的装饰器,提升自身能力,一个人做完所有的事情。

那么我们从写代码的层面来看:

装饰者模式是使用的调用者从外部传进来的被装饰对象,调用者只想让你把他给你的对象加强一下,装饰一下. 代理模式使用的是代理对象在自己的构造方法里面new的一个被代理类的对象,不是调用者传入的,调用者不知道你找了其他人,他也不关心这些事,只要你把事情做好就行了.

那么也就是说使用代理的时候是不需要传入被代理的对象。

总结

装饰器模式提供了比继承更有弹性的替代方案(扩展原有对象的功能)将功能附加到对象上。因此,装饰器模式的核心是功能扩展。使用装饰器模式可以透明且动态地扩展类的功能。

相关推荐
BIGSHU09233 小时前
GPT带我学-设计模式17-装饰器模式
gpt·设计模式·装饰器模式
我是回頭呀3 天前
设计模式之装饰器模式
设计模式·装饰器模式
bobostudio19954 天前
TypeScript 设计模式之【装饰模式】
前端·javascript·设计模式·typescript·装饰器模式
多喝热水-多读书4 天前
Qt C++设计模式->装饰器模式
c++·qt·设计模式·装饰器模式
赤橙红的黄7 天前
装饰器模式
装饰器模式
丶白泽17 天前
重修设计模式-结构型-装饰器模式
java·设计模式·装饰器模式
心之语歌18 天前
设计模式 装饰模式(Decorator Pattern)
java·设计模式·装饰器模式
Aloha_up18 天前
装饰器模式decorator
装饰器模式
weixin_4177599919 天前
77-java 装饰器模式和适配器模式区别
java·适配器模式·装饰器模式