装饰模式(学习笔记)
1. 意图
动态的给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活
2. 动机
在某些情况下,我们可能会“过度的使用继承来扩展对象的功能”。继承是静态的,无法在运行时更改已有对象的行为,只能使用由不同子类创建的对象来替代当前的整个对象;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致子类的膨胀
3. 适用性
- 在不影响其他对象的情况下,以动态的、透明的方式给单个对象添加指责
 - 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况是,类定义被隐藏,或类定义不能用于生成子类
 - 处理一些可以撤销的职责
 
4. 结构
 
5. 效果
1) 比静态继承更加灵活
2) 可以在运行时,添加和删除对象功能
3) 避免在层次结构高层的类有太多的特征
4) 有很多小对象。 对了解系统的人来说,很容易对它们进行定制,但是很难学习这些系统,排错也很困难
6. 代码实现
decorators/DataSource.java: 定义了读取和写入操作的通用数据接口
package decorator.decorators; /** * @author GaoMing * @date 2021/7/12 - 16:48 */ public interface DataSource { void writeData(String data); String readData(); }
decorators/FileDataSource.java: 简单数据读写器
package decorator.decorators; import java.io.*; /** * @author GaoMing * @date 2021/7/12 - 16:49 */ 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); } }
decorators/DataSourceDecorator.java: 抽象基础装饰
package decorator.decorators; /** * @author GaoMing * @date 2021/7/12 - 16:51 */ public class DataSourceDecorator implements DataSource{ private DataSource wrappee; DataSourceDecorator(DataSource source) { this.wrappee = source; } @Override public void writeData(String data) { wrappee.writeData(data); } @Override public String readData() { return wrappee.readData(); } }
decorators/EncryptionDecorator.java: 加密装饰
package decorator.decorators; import java.util.Base64; /** * @author GaoMing * @date 2021/7/12 - 16:53 */ public class EncryptionDecorator extends DataSourceDecorator{ public EncryptionDecorator(DataSource source) { super(source); } @Override public void writeData(String data) { super.writeData(encode(data)); } @Override public String readData() { return decode(super.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); } }
decorators/CompressionDecorator.java: 压缩装饰
package decorator.decorators; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Base64; import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; import java.util.zip.InflaterInputStream; /** * @author GaoMing * @date 2021/7/12 - 16:54 */ public class CompressionDecorator extends DataSourceDecorator{ private int compLevel = 6; public CompressionDecorator(DataSource source) { super(source); } public int getCompressionLevel() { return compLevel; } public void setCompressionLevel(int value) { compLevel = value; } @Override public void writeData(String data) { super.writeData(compress(data)); } @Override public String readData() { return decompress(super.readData()); } private String compress(String stringData) { byte[] data = stringData.getBytes(); try { ByteArrayOutputStream bout = new ByteArrayOutputStream(512); DeflaterOutputStream dos = new DeflaterOutputStream(bout, new Deflater(compLevel)); dos.write(data); dos.close(); bout.close(); return Base64.getEncoder().encodeToString(bout.toByteArray()); } catch (IOException ex) { return null; } } private String decompress(String stringData) { byte[] data = Base64.getDecoder().decode(stringData); try { InputStream in = new ByteArrayInputStream(data); InflaterInputStream iin = new InflaterInputStream(in); ByteArrayOutputStream bout = new ByteArrayOutputStream(512); int b; while ((b = iin.read()) != -1) { bout.write(b); } in.close(); iin.close(); bout.close(); return new String(bout.toByteArray()); } catch (IOException ex) { return null; } } }
Demo.java: 客户端代码
package decorator; import decorator.decorators.*; /** * @author GaoMing * @date 2021/7/12 - 17:03 */ public class Demo { public static void main(String[] args) { String salaryRecords = "Name,Salary\nJohn Smith,100000\nSteven Jobs,912000"; DataSourceDecorator encoded = new CompressionDecorator( new EncryptionDecorator( new FileDataSource("out/OutputDemo.txt"))); encoded.writeData(salaryRecords); DataSource plain = new FileDataSource("out/OutputDemo.txt"); System.out.println("- Input ----------------"); System.out.println(salaryRecords); System.out.println("- Encoded --------------"); System.out.println(plain.readData()); System.out.println("- Decoded --------------"); System.out.println(encoded.readData()); } }
运行结果
- Input ---------------- Name,Salary John Smith,100000 Steven Jobs,912000 - Encoded -------------- Zkt7e1Q5eU8yUm1Qe0ZsdHJ2VXp6dDBKVnhrUHtUe0sxRUYxQkJIdjVLTVZ0dVI5Q2IwOXFISmVUMU5rcENCQmdxRlByaD4+ - Decoded -------------- Name,Salary John Smith,100000 Steven Jobs,912000
7. 与其他模式的关系
- 适配器模式可以对已有对象的接口进行修改,装饰模式则能在不改变对象接口的前提下强化对象功能。此外,装饰还支持递归组合,适配器则无法实现
 - 适配器能为被封装对象提供不同的接口,代理模式能为对象提供相同的接口,装饰则能为对象提供加强的接口
 - 装饰可让你更改对象的外表,策略模式则让你能够改变其本质
 - 装饰和代理有着相似的结构,但是其意图却非常不同。这两个模式的构建都基于组合原则,也就是说一个对象应该将部分工作委派给另一个对象。两者之间的不同之处在于代理通常自行管理其服务对象的生命周期,而装饰的生成则总是由客户端进行控制
 
8. 已知应用
- java.io.InputStream、OutputStream、Reader 和 Writer 的所有代码都有以自身类型的对象作为参数的构造函数
 
 (转载) 
- java.util.Collections;checkedXXX()、synchronizedXXX() 和 unmodifiableXXX() 方法
 - javax.servlet.http.HttpServletRequestWrapper 和 HttpServletResponseWrapper
 
识别方法: 装饰可通过以当前类或对象为参数的创建方法或构造函数来识别

                
            
        
浙公网安备 33010602011771号