由于blog各种垃圾评论太多,而且本人审核评论周期较长,所以懒得管理评论了,就把评论功能关闭,有问题可以直接qq骚扰我

设计模式—装饰器模式

JAVA 西门飞冰 936℃
[隐藏]

1.定义

装饰模式是⼀种结构型设计模式, 允许你通过将对象放⼊包含⾏为的特殊封装对象中来为原对象绑定新的⾏为。

image-20220926184638373

2.装饰模式结构

  • 部件 (Component) 声明封装器和被封装对象的公⽤接⼝。
  • 具体部件 (Concrete Component) 类是被封装对象所属的类。 它定义了基础⾏为, 但装饰类可以改变这些⾏为
  • 基础装饰 (Base Decorator) 类拥有⼀个指向被封装对象的引⽤成员变量。 该变量的类型应当被声明为通⽤部件接⼝,这样它就可以引⽤具体的部件和装饰。 装饰基类会将所有操作委派给被封装的对象。
  • 具体装饰类 (Concrete Decorators) 定义了可动态添加到部件的额外⾏为。 具体装饰类会重写装饰基类的⽅法, 并在调⽤⽗类⽅法之前或之后进⾏额外的⾏为。
  • 客户端 (Client) 可以使⽤多层装饰来封装部件, 只要它能使⽤通⽤接⼝与所有对象互动即可。

3.代码示例

案例:把我们具体的数据保存到一个指定的文件当中进行写入,写入以后还需要提供读取的功能,写入和读取过程可以选择使用base64进行加密解密,可以选择使用json进行序列化

1、顶层抽象DataSource数据源接口

public interface DataSource {
    void writeData(String data);
    String readData();
}

2、具体实现类

// 利用文件来保存我们写入的数据
public class FileDataSource implements DataSource {
    private String name;
    public FileDataSource(String name) {
        this.name = name;
    }
    @Override
    public void writeData(String data) {
        File file = new File(name);
        try (OutputStream fos = new FileOutputStream(file)) {
            fos.write(data.getBytes(), 0, data.length());
        } catch (IOException ex) {
            System.out.println(ex.getMessage());
        }
    }
    @Override
    public String readData() {
        char[] buffer = null;
        File file = new File(name);
        try (FileReader reader = new FileReader(file)) {
            buffer = new char[(int) file.length()];
            reader.read(buffer);
        } catch (IOException ex) {
            System.out.println(ex.getMessage());
        }
        return new String(buffer);
    }
}

3、BASE64编码装饰器 装饰器Decorator同样实现DataSource接⼝,且内部通过构造⽅法接收某个dataSource对象,这个dataSource可以是最终的DataSource对象,也可以是其他装饰器对象。之后便是对数据⾏为进⾏扩展操作

public class EncryptionDecorator implements DataSource {
    private DataSource datasource;
    //通过构造方法传入原始的数据源
    public EncryptionDecorator(DataSource dataSource) {
        this.datasource = dataSource;
    }
    @Override
    public void writeData(String data) {
        datasource.writeData(encode(data));
    }
    @Override
    public String readData() {
        return decode(datasource.readData());
    }
    // 定义数据加密方法
    private String encode(String data) {
        byte[] result = data.getBytes();
        for (int i = 0; i < result.length; i++) {
            result[i] += (byte) 1;
        }
        return Base64.getEncoder().encodeToString(result);
    }
    // 定义数据解密方法
    private String decode(String data) {
        byte[] result = Base64.getDecoder().decode(data);
        for (int i = 0; i < result.length; i++) {
            result[i] -= (byte) 1;
        }
        return new String(result);
    }
}

4、JSON序列化装饰器

public class JsonDecorator implements DataSource {
    private DataSource dataSource;
    public JsonDecorator(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    @Override
    public void writeData(String data) {
        dataSource.writeData(toJson(data));
    }
    @Override
    public String readData() {
        return fromJson(dataSource.readData());
    }
    // 进行json序列化
    private String toJson(String stringData) {
        Map map = new HashMap();
        map.put("content", stringData);
        return new Gson().toJson(map);
    }
    // 进行json反序列化
    private String fromJson(String json) {
        Map map = new Gson().fromJson(json,new TypeToken<Map<String,String>>(){}.getType());
        return map.get("content").toString();
    }
}

5、客户端使⽤

可以将多个装饰器组合使⽤,执⾏时从外向内依次执⾏

public class Client {
    public static void main(String[] args) {
        String salaryRecords = "Name,Salary / John Smith,100000 / Steven Jobs";
        // 装饰器混用场景
        // 对数据进行二次装饰,顺序是从左到右依次执行,先进行加密,在进行json序列号,最后在存入具体的文件中
        DataSource dataSource = new EncryptionDecorator(new JsonDecorator(new FileDataSource("OutputDemo.txt")));
        dataSource.writeData(salaryRecords);
        DataSource plain = new FileDataSource("OutputDemo.txt");
        // 输出原始数据
        System.out.println("- Data ----------------");
        System.out.println(salaryRecords);
        // 输出加密数据
        System.out.println("- EncodedContent --------------");
        System.out.println(plain.readData());
        // 输出解密数据
        System.out.println("- DecodedContent --------------");
        System.out.println(dataSource.readData());
    }
}

6、输出结果

- Data ----------------
Name,Salary / John Smith,100000 / Steven Jobs
- EncodedContent --------------
{"content":"T2JuZi1UYm1ic3ohMCFLcGlvIVRuanVpLTIxMTExMSEwIVR1ZndmbyFLcGN0"}
- DecodedContent --------------
Name,Salary / John Smith,100000 / Steven Jobs

4.装饰模式适合应⽤场景

  • 如果你希望在⽆需修改代码的情况下即可使⽤对象, 且希望在运⾏时为对象新增额外的⾏为, 可以使⽤装饰模式。
  • 装饰能将业务逻辑组织为层次结构, 你可为各层创建⼀个装饰, 在运⾏时将各种不同逻辑组合成对象。 由于这些对象都遵循通⽤接⼝, 客户端代码能以相同的⽅式使⽤这些对象。
  • 如果⽤继承来扩展对象⾏为的⽅案难以实现或者根本不可⾏, 你可以使⽤该模式。
  • 许多编程语⾔使⽤ final最终关键字来限制对某个类的进⼀步扩展。复⽤最终类已有⾏为的唯⼀⽅法是使⽤装饰模式: ⽤封装器对其进⾏封装。

5.装饰器模式优点

  • 你⽆需创建新⼦类即可扩展对象的⾏为。
  • 你可以在运⾏时添加或删除对象的功能。
  • 你可以⽤多个装饰封装对象来组合⼏种⾏为。
  • 单⼀职责原则。 你可以将实现了许多不同⾏为的⼀个⼤类拆分为多个较⼩的类。

6.装饰器模式缺点

  • 在封装器栈中删除特定封装器⽐较困难。
  • 实现⾏为不受装饰栈顺序影响的装饰⽐较困难。
  • 各层的初始化配置代码看上去可能会很糟糕。

转载请注明:西门飞冰的博客 » 设计模式—装饰器模式

喜欢 (0)or分享 (0)