问题
装饰者模式的问题,我想从桥接模式引入,对桥接模式不了解的可以参考设计模式之桥接模式。桥接模式解决的问题是我卖的笔记本电脑的组成例如cpu或cache有很多种类型或大小,如果使用继承的方式,会生成非常多的类,难以维护。所以使用桥接模式,将cpu或cache抽象化,在生产电脑时,再将特定类型或大小的组成部分组装到笔记本电脑中。完美!
但是呢,现在又一个问题出现了,客户对电脑要求越来越高,希望可以扩展插入SSD和内存条。嗯,听到这个问题,我觉得很简单呀,在电脑中继续扩展两个组件SSD和cache。这时候发现有一个问题就是原来电脑就有cache的组件了,将cache扩展成list即可,另一种方式就是引入本章的主题——装饰者模式
简介
为什么上面的问题可以用装饰者模式来解决呢?首先,我们从字面上来理解一下,装饰者模式就是给人或物品增加装饰,比如人穿上衣服,化妆或者给房子增加家具等。可以这么理解装饰者模式:
某对象有一个核心功能,现在有一个其衍生对象,扩展了若干功能,但是原对象也有使用场景。在此场景下,可对衍生对象用装饰者模式。
再来上面的问题,为什么我用装饰者模式呢?因为电脑的核心或者必备组件我们之前都已经定义过了,而新增的SSD和cache都是非必须的,添加了的话,电脑性能会更好,没有呢,也可以正常使用电脑,在这种场景下使用装饰者模式,我觉得是相对比较合理的。在考虑使用场景时,可以通过考虑此组件或属性是否可以动态撤销来决定是否使用装饰者模式。
实现
先看Computer(有一些其他设计模式的代码在其中,请忽略)
public class Computer implements Cloneable {
private String screen;
private String cpu;
private String keyboard;
private String disk;
/**
* 主机
*/
private String engine;
private String cache;
/**
* U盘
*/
private ArrayList<String> usbFlashDisk;
public Computer() {
}
public Computer(String screen, String cpu, String keyboard, String disk, String engine,
String cache) {
this.screen = screen;
this.cpu = cpu;
this.keyboard = keyboard;
this.disk = disk;
this.engine = engine;
this.cache = cache;
}
public String getScreen() {
return screen;
}
public void setScreen(String screen) {
this.screen = screen;
}
public String getCpu() {
return cpu;
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
public String getKeyboard() {
return keyboard;
}
public void setKeyboard(String keyboard) {
this.keyboard = keyboard;
}
public String getDisk() {
return disk;
}
public void setDisk(String disk) {
this.disk = disk;
}
public String getEngine() {
return engine;
}
public void setEngine(String engine) {
this.engine = engine;
}
public String getCache() {
return cache;
}
public void setCache(String cache) {
this.cache = cache;
}
public ArrayList<String> getUsbFlashDisk() {
return usbFlashDisk;
}
public void setUsbFlashDisk(ArrayList<String> usbFlashDisk) {
this.usbFlashDisk = usbFlashDisk;
}
@Override
public String toString() {
System.out.println("computer is combined by : ---" + getCpu() + "--" + getCache() + "---" + getDisk()
+ "----" + getEngine() + "----" + getKeyboard() + "----" + getScreen());
return super.toString();
}
@Override
public Object clone() throws CloneNotSupportedException {
Computer computer = (Computer) super.clone();
ArrayList<String> usbFlashDisk = (ArrayList<String>) getUsbFlashDisk().clone();
computer.setUsbFlashDisk(usbFlashDisk);
return computer;
}
}
装饰者的抽象类(为了可能会出现其他装饰而设计的抽象类):
public abstract class ComputerDecorator {
protected Computer computer;
public ComputerDecorator(Computer computer) {
this.computer = computer;
}
}
装饰者的实现类:
public class CommonComputerDirector extends ComputerDecorator {
private List<SSD> ssdList;
private List<Cache> cacheList;
public CommonComputerDirector(Computer computer) {
super(computer);
cacheList = new ArrayList<>();
ssdList = new ArrayList<>();
}
public void addSsd(SSD ssd) {
ssdList.add(ssd);
}
public void addCache(Cache cache) {
cacheList.add(cache);
}
public void play() {
System.out.println("play with computer with extra devices: ssd size: " + ssdList.size() +
" cache size: " + cacheList.size());
}
}
执行代码:
public static void main(String[] args) {
Computer computer = new Computer("base screen", "base cpu", "base keyboard",
"base disk", "base engine", "base cache");
computer.toString();
CommonComputerDirector commonComputerDirector = new CommonComputerDirector(computer);
commonComputerDirector.addCache(new EightCache());
commonComputerDirector.addSsd(new SSD());
commonComputerDirector.play();
}
执行结果:
computer is combined by : ---base cpu--base cache---base disk----base engine----base keyboard----base screen
play with computer with extra devices: ssd size: 1 cache size: 1
从结果即可看到,我们通过装饰者模式添加了额外的ssd和cache,可能你会在看代码或者自己写代码的过程中想到这个其实是可以用建造者模式来实现。解释一下:因为我这次装饰者模式添加的都是配件,而这些组件确实都是可以用建造者模式来实现,但是呢,装饰者模式不仅仅可以添加配件,还可以在添加配件之后,衍生出一些能力出来,这是建造者模式无法做到的。例如,我添加ssd和cache之后,就可以带的动更大的游戏,例如使命召唤。我就可以在装饰者中添加playGame的能力。
总结
应用场景:对已拥有必备组件的对象进行扩展高阶组件或属性。
优点:
- 无需修改现有对象,也无需创建对象的子类,即可扩展对象的功能。
- 可以多扩展功能进行动态添加或删除
- 可以扩展不同的装饰者来解决扩展不同功能的问题
缺点:多层装饰会比较复杂,无法控制装饰的顺序