装饰器模式介绍

目录

一、装饰器模式介绍

1.1 装饰器模式定义

1.2 装饰器模式原理

1.2.1 模式类图

1.2.2 模式角色说明

1.2.3 举例代码

二、装饰器模式的应用

2.1 需求说明

2.2 需求实现

2.2.1 类图

2.2.2 类图说明

2.2.3 具体实现

2.2.3.1 DataLoader类

2.2.3.2 BaseFileDataLoader类

2.2.3.3 DataLoaderDecorator类

2.2.3.4 EncryptionDataDecorator

2.2.3.5 TestDecorator测试类

三、装饰器模式总结

3.1 装饰器模式优点

3.2 装饰器模式缺点

3.3 装饰器模式适用场景


一、装饰器模式介绍

1.1 装饰器模式定义

装饰模式(decorator pattern) 的原始定义是:动态的给一个对象添加一些额外的职责。就扩展功能而言,装饰器模式提供了一种比使用子类更加灵活的替代方案。

假设现在有有一块蛋糕,如果只有涂上奶油那这个蛋糕就是普通的奶油蛋糕,这时如果我们添加上一些蓝莓,那这个蛋糕就是蓝莓蛋糕。如果我们再拿一块黑巧克力 然后写上姓名、插上代表年龄的蜡烛,这就是变成了一块生日蛋糕。

在软件设计中,装饰器模式是一种用于替代继承的技术,它通过一种无须定义子类的方式给对象动态的增加职责,使用对象之间的关联关系取代类之间的继承关系。

1.2 装饰器模式原理

1.2.1 模式类图

1.2.2 模式角色说明

装饰(Decorator)模式中的角色:

  • 抽象构件(Component)角色 :它是具体构件和抽象装饰类的共同父类,声明了在具体构件中实现的业务方法。它引进了可以使客户端以一致的方式处理未被装饰的对象以及装饰之后的对象,实现客户端的透明操作。
  • 具体构件(Concrete Component)角色 :它是抽象构件类的子类,用于定义具体的构建对象,实现了在抽象构建中声明的方法,装饰类可以给它增加额外的职责(方法)。
  • 抽象装饰(Decorator)角色 :它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。它维护了一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。
  • 具体装饰(ConcreteDecorator)角色 : 它是抽象装饰类的子类,负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用于扩充对象的行为。

1.2.3 举例代码

package main.java.cn.test.decorator.V1;

/**
 * @author ningzhaosheng
 * @date 2024/1/12 18:46:19
 * @description 抽象构件类
 */
public abstract class Component {
    //抽象方法
    public abstract void operation();

}

package main.java.cn.test.decorator.V1;

/**
 * @author ningzhaosheng
 * @date 2024/1/12 18:46:54
 * @description 具体构建类
 */
public class ConcreteComponent extends Component{
    @Override
    public void operation() {
        //基础功能实现(复杂功能通过装饰类进行扩展)
    }
}

package main.java.cn.test.decorator.V1;

/**
 * @author ningzhaosheng
 * @date 2024/1/12 18:47:38
 * @description 抽象装饰类-装饰者模式的核心
 */
public class Decorator extends Component {
    //维持一个对抽象构件对象的引用
    private Component component;

    //注入一个抽象构件类型的对象
    public Decorator(Component component) {
        this.component = component;
    }

    @Override
    public void operation() {
        //调用原有业务方法(这里并没有真正实施装饰,而是提供了一个统一的接口,将装饰过程交给子类完成)
        component.operation();
    }
}

package main.java.cn.test.decorator.V1;

/**
 * @author ningzhaosheng
 * @date 2024/1/12 18:48:48
 * @description 具体装饰类
 */
public class ConcreteDecorator extends Decorator {
    public ConcreteDecorator(Component component) {
        super(component);
    }

    @Override
    public void operation() {
        super.operation(); //调用原有业务方法
        addedBehavior(); //调用新增业务方法
    }

    //新增业务方法
    public void addedBehavior() {
        //......
    }

}

二、装饰器模式的应用

2.1 需求说明

我们以一个文件读写器程序为例, 演示一下装饰者模式的使用。

2.2 需求实现

2.2.1 类图

2.2.2 类图说明

  • DataLoader

  抽象的文件读取接口DataLoader

  • BaseFileDataLoader

  具体组件BaseFileDataLoader,重写组件 DataLoader 的读写方法

  • DataLoaderDecorator

  装饰器DataLoaderDecorator,这里要包含一个引用 DataLoader 的对象实例 wrapper,同样是重写 DataLoader 方法,不过这里使用wrapper 来读写,并不进行扩展。

  • EncryptionDataDecorator

  读写时有加解密功能的具体装饰器EncryptionDataDecorator,它继承了装饰器 DataLoaderDecorator 重写读写方法。

2.2.3 具体实现

2.2.3.1 DataLoader类
package main.java.cn.test.decorator.V2;

/**
 * @author ningzhaosheng
 * @date 2024/1/12 18:28:43
 * @description 抽象的文件读取接口DataLoader
 */
public interface DataLoader {
    String read();

    void write(String data);
}

2.2.3.2 BaseFileDataLoader类
package main.java.cn.test.decorator.V2;

import java.io.File;
import java.io.IOException;

/**
 * @author ningzhaosheng
 * @date 2024/1/12 18:29:10
 * @description 具体实现组件, 重写读写方法
 */
public class BaseFileDataLoader implements DataLoader {
    private String filePath;

    public BaseFileDataLoader(String filePath) {
        this.filePath = filePath;
    }

    @Override
    public String read() {
        try {
            String result = FileUtils.readFileToString(new
                    File(filePath), "utf-8");
            return result;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public void write(String data) {
        try {
            FileUtils.writeStringToFile(new File(filePath),
                    data, "utf-8");
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

2.2.3.3 DataLoaderDecorator类
package main.java.cn.test.decorator.V2;

/**
 * @author ningzhaosheng
 * @date 2024/1/12 18:33:23
 * @description 装抽象饰者类
 */
public class DataLoaderDecorator implements DataLoader {
    private DataLoader wrapper;

    public DataLoaderDecorator(DataLoader wrapper) {
        this.wrapper = wrapper;
    }

    @Override
    public String read() {
        return wrapper.read();
    }

    @Override
    public void write(String data) {
        wrapper.write(data);
    }
}

2.2.3.4 EncryptionDataDecorator
package main.java.cn.test.decorator.V2;

import java.util.Base64;

/**
 * @author ningzhaosheng
 * @date 2024/1/12 18:34:28
 * @description 具体装饰者-对文件内容进行加密和解密
 */
public class EncryptionDataDecorator extends DataLoaderDecorator {
    public EncryptionDataDecorator(DataLoader wrapper) {
        super(wrapper);
    }

    @Override
    public String read() {
        return decode(super.read());
    }

    @Override
    public void write(String data) {
        super.write(encode(data));
    }

    //加密操作
    private String encode(String data) {
        try {

            Base64.Encoder encoder = Base64.getEncoder();
            byte[] bytes = data.getBytes("UTF-8");
            String result = encoder.encodeToString(bytes);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }

    //解密
    private String decode(String data) {
        try {
            Base64.Decoder decoder = Base64.getDecoder();
            String result = new String(decoder.decode(data),
                    "UTF-8");
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

2.2.3.5 TestDecorator测试类
package main.java.cn.test.decorator.V2;

/**
 * @author ningzhaosheng
 * @date 2024/1/12 18:36:41
 * @description 测试类
 */
public class TestDecorator {
    public static void main(String[] args) {
        String info = "name:tom,age:15";
        DataLoaderDecorator decorator = new
                EncryptionDataDecorator(new BaseFileDataLoader("demo.txt"));
        decorator.write(info);
        String data = decorator.read();
        System.out.println(data);
    }
}

三、装饰器模式总结

3.1 装饰器模式优点

  1. 对于扩展一个对象的功能,装饰模式比继承更加灵活,不会导致类的个数急剧增加。
  2. 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的具体装饰类,从而实现不同的行为。
  3. 可以对一个对象进行多次装饰,通过使用不同的具体装饰类以及这些装饰类的排列组合可以创造出很多不同行为的组合,得到更加强大的对象。
  4. 具体构建类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构建类和具体装饰类,原有类库代码无序改变,符合开闭原则。

3.2 装饰器模式缺点

  1. 在使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值不同,大量的小对象的产生势必会占用更多的系统资源,在一定程度上影响程序的性能。
  2. 装饰器模式提供了一种比继承更加灵活、机动的解决方案,但同时也意味着比继承更加易于出错,排错也更加困难,对于多次装饰的对象,在调试寻找错误时可能需要逐级排查,较为烦琐。

3.3 装饰器模式适用场景

  1. 快速动态扩展和撤销一个类的功能场景。 比如,有的场景下对 API 接口的安全性要求较高,那么就可以使用装饰模式对传输的字符串数据进行压缩或加密。如果安全性要求不高,则可以不使用。
  2. 不支持继承扩展类的场景。 比如,使用 final 关键字的类,或者系统中存在大量通过继承产生的子类。

好了,本次分享就到这里,欢迎大家继续阅读《设计模式》专栏其他设计模式内容,如果有帮助到大家,欢迎大家点赞+关注+收藏,有疑问也欢迎大家评论留言!

  • 22
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值