一、前言
有时候不想动脑子,就懒得看源码又不像浪费时间所以会看看书,但是又记不住,所以决定开始写"抄书"系列。本系列大部分内容都是来源于《 图解设计模式》(【日】结城浩 著)。该系列文章可随意转载。
二、Composite 模式(组合模式)
Composite 模式 :容器与内容的一致性
1. 介绍
能够使容器与内容具有一致性,创造出具有递归结构的模式就是 Composite 模式(组合模式)。
Composite 模式中登场的角色
- Leaf (树叶):表示“内容”的角色,在该角色中不能放入其他角色。
- Composite(复合物):表示容器的角色,可以在其中放入 Leaf 角色和 Composite 角色。
- Component :使用 Leaf 和 Composite 角色具有一致性的角色。Component 角色是 Leaf 角色和 Composite角色的父类。
- Client :使用 Composite 模式的角色。
类图如下:
Demo如下:
// 条目,父级接口
public interface Entry {
/**
* 获取文件名
*
* @return
*/
String getName();
/**
* 获取文件大小
*
* @return
*/
int getSize();
/**
* 添加目录,默认抛出异常
*
* @param entry
* @return
*/
default Entry addEntry(Entry entry){
throw new RuntimeException();
}
/**
* 打印目录
*/
default void printList() {
printList("");
}
/**
* 打印目录
*
* @param prefix
*/
default void printList(String prefix){
System.out.println(prefix + "/" + thisPath());
}
default String thisPath() {
return getName() + "(" + getSize() + ")";
}
}
// 文件类型
public class File implements Entry {
private String name;
private int size;
public File(String name, int size) {
this.name = name;
this.size = size;
}
@Override
public String getName() {
return name;
}
@Override
public int getSize() {
return size;
}
@Override
public void printList(String prefix) {
System.out.println(prefix + "/" + thisPath());
}
}
// 文件夹类型
public class Directory implements Entry {
private String name;
private List<Entry> entries = Lists.newArrayList();
public Directory(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public int getSize() {
return entries.stream()
.mapToInt(Entry::getSize)
.sum();
}
@Override
public Entry addEntry(Entry entry) {
entries.add(entry);
return this;
}
@Override
public void printList(String prefix) {
System.out.println(prefix + "/" + thisPath());
entries.forEach(entry -> entry.printList(prefix + "/" + name));
}
}
// main 方法:输出每个文件夹下的目录及大小
public class CompositeDemoMain {
public static void main(String[] args) {
Entry rootDir = new Directory("root");
Entry binDir = new Directory("bin");
Entry tmpDir = new Directory("tmp");
Entry usrDir = new Directory("usr");
Entry hanakoDir = new Directory("hanako");
usrDir.addEntry(hanakoDir);
rootDir.addEntry(binDir);
rootDir.addEntry(tmpDir);
rootDir.addEntry(usrDir);
hanakoDir.addEntry(new File("memo.tex", 10));
binDir.addEntry(new File("vi", 1000));
binDir.addEntry(new File("latex", 2000));
rootDir.printList();
}
}
输出如下:
以上面的例子为例, Entry 可能是 File 的实现,也可能是 Directory 的实现,但无论是哪种实现,都可以通过 getSize 得到他的大小。这就是 Composite 模式的特征:容器与内容的一致性。
2. 应用
暂时没想到
个人使用:该部分内容是写给自己看的,帮助自身理解,因此就不交代项目背景了,读者请自行忽略(◐ˍ◑):
- 项目A中,需要对敏感词构建出一个树状图,以此进行脱敏处理。构建出的敏感词数据结构即是一个树状图结构。具体逻辑这里不再写出,类似下图(详参 https://mp.weixin.qq.com/s/7Rm87J7PJcA8KKwM8m1yVQ):
3. 总结
通常来说,树结构的数据结构都适用于 Composite 模式。
扩展思路:
- 多个和单个的一致性 :使用 Composite 模式可以使容器与内容具有一致性,也可以称其为多个和单个的一致性,即将多个对象结合在一起,当做一个对象进行处理。通常来说树结构的数据结构都适用于 Composite 模式。如
相关模式:
- Command 模式:使用 Command 模式编写宏命令时使用了 Composite 模式
- Visitor 模式:可以使用 Visitor 模式访问 Composite 模式中的递归结构
- Decorator 模式:Composite 模式通过 Component 角色使容器(Composite 角色) 和 内容 (Leaf 角色)具有一致性。Decorator 模式使装饰框和内容具有一致性。
三、Decorator 模式(装饰器模式)
Decorator 模式 :装饰边框与被装饰物的一致性
1. 介绍
Decorator 模式即为对象添加装饰的设计模式。如一个蛋糕,只涂上奶油就是奶油蛋糕,如果再加上草莓就是草莓奶油蛋糕,不过不管加了什么,其核心都是蛋糕。装饰器模式与之类似,不断地为对象添加装饰的模式即为装饰器模式。
登场的角色:
- Component :增加功能时的核心角色。
- ConcreteComponent :该角色是实现了 Component 角色所定义的接口的具体角色。
- Decorator (装饰物):该角色具有与 Component 角色相同的接口,在它内部保存了被装饰的对象(Component 角色)。Decorator 角色知道自己要装饰的角色。
- ConcreteDecorator (具体的装饰物):该角色是具体的 Decorator 角色。
类图如下:
2. 应用
-
Dubbo 在进行服务间调用时会通过 Invoker 来调用,如下图,从 Invoker 的调用过程也可以看出 Invoker 是通过装饰器模式修饰的。
-
Java IO 包中IO类,如下可以自由组合出很多IO操作。
Reader reader = new FileReader("/demo.txt"); Reader reader = new BufferedReader(new FileReader("/demo.txt")); Reader reader = new LineNumberReader(new BufferedReader(new FileReader("/demo.txt")));
个人使用:该部分内容是写给自己看的,帮助自身理解,因此就不交代项目背景了,读者请自行忽略(◐ˍ◑):
-
项目 A 中,需要对外提供脱敏后的数据,但是程序内部使用未脱敏的数据,则可以通过装饰器模式,构建一个脱敏的实现来对外提供数据,如下:FullDataHolder 是程序内部使用未脱敏的数据,DesensitizationDataHolder 则对 FullDataHolder 进行了装饰,对外提供脱敏后的数据。
// 数据持有接口 public interface DataHolder { /** * 获取数据 * @return */ String getData(); } // 全量数据持有者 public class FullDataHolder implements DataHolder { @Override public String getData() { return "全量敏感数据"; } } // 脱敏数据持有者 public class DesensitizationDataHolder implements DataHolder{ /** * 数据持有者 */ private DataHolder dataHolder; public DesensitizationDataHolder(DataHolder dataHolder) { this.dataHolder = dataHolder; } @Override public String getData() { return dataHolder.getData().replace("敏感", "**"); } } public class DemoMain { public static void main(String[] args) { DataHolder dataHolder = new FullDataHolder(); System.out.println(dataHolder.getData()); // 使用 DesensitizationDataHolder 对 FullDataHolder进行装饰,输出脱敏后的数据 DataHolder desensitizationDataHolder = new DesensitizationDataHolder(dataHolder); System.out.println(desensitizationDataHolder.getData()); } }
输出如下:
3. 总结
扩展思路:
- 接口的透明性 : 装饰边框与被装饰物具有一致性,因为被装饰物的接口并不会因为被装饰而被隐藏起来。
- 在不改变被装饰物的前提下增加功能 :由于装饰边框和被装饰物的暴露接口相同,我们可以进行多次装饰,越装饰功能则越多,并且不需要对被装饰的类做任何处理。即实现了不修改被装饰的类即可增加功能。
- 可以动态地增加功能 :Decorator 模式中使用了委托,使得类之间形成了弱关联关系,因此在不改变框架代码的情况下就可以生成一个与其他对象具有不同关系的新对象。
- 只需要一些装饰物即可添加许多功能 :装饰边框可以提供多个,即使每个装饰边框增加的功能很简单,但是可以通过多个装饰边框的嵌套来增加更复杂的功能。
装饰器模式的缺点在于:如果装饰器的增加的功能较小,可能会导致程序中增加许多功能类似的很小的类。
相关设计模式:
- Adapter 模式 :Decorator 模式可以在不改变被装饰物的接口的前提下,为被装饰物添加边框。Adapter 模式适用于两个不同的接口。
- Strategy 模式 :Decorator 模式可以像改变装饰物的边框或是为被装饰物添加多重边框那样,来增加类的功能。Strategy 模式通过整体地替换算法来改变类的功能。
参考文章
https://mp.weixin.qq.com/s/7Rm87J7PJcA8KKwM8m1yVQ