设计模式 - 装饰者模式
一、需求
有一个人,战斗力只有 2 万最高,需要通过变身,来增强自己的战斗力,变身为:
- 超级赛亚人一,提高到 20 万战斗力
- 超级赛亚人二,提高到 200 万战斗力
- 超级赛亚人三,提高到 2000 万战斗力
public interface Biology {
void feature();
}
public class Human implements Biology{
@Override
public void feature() {
System.out.println("人类特性:战斗力小于 2 万");
}
}
可以有哪些方式来增强?
二、继承增强
2.1 思路
每一个增强类都可以通过继承原有类从而拓展原有类功能
超级塞亚人一增强了人类
public class SuperSaiyanI extends Human{
@Override
public void feature() {
super.feature();
System.out.println("超级赛亚人一特性:战斗力小于 20 万");
}
}
超级赛亚人二增强了超级赛亚人一
public class SuperSaiyanII extends SuperSaiyanI {
@Override
public void feature() {
super.feature();
System.out.println("超级赛亚人二特性:战斗力小于 200 万");
}
}
超级赛亚人三继承了超级赛亚人二
public class SuperSaiyanIII extends SuperSaiyanII {
@Override
public void feature() {
super.feature();
System.out.println("超级赛亚人三特性:战斗力小于 2000 万");
}
}
2.2 结果
测试一下增强效果:
public class Test {
public static void main(String[] args) {
Human human = new Human(); //原始人类
human.feature();
System.out.println();
human = new SuperSaiyanI();//原始人类到超级赛亚人一
human.feature();
System.out.println();
human = new SuperSaiyanII();//原始人类到超级赛亚人二
human.feature();
System.out.println();
human = new SuperSaiyanIII();//原始人类到超级塞亚人三
human.feature();
System.out.println();
}
}
输出:
人类特性:战斗力小于 2 万
人类特性:战斗力小于 2 万
超级赛亚人一特性:战斗力小于 20 万
人类特性:战斗力小于 2 万
超级赛亚人一特性:战斗力小于 20 万
超级赛亚人二特性:战斗力小于 200 万
人类特性:战斗力小于 2 万
超级赛亚人一特性:战斗力小于 20 万
超级赛亚人二特性:战斗力小于 200 万
超级赛亚人三特性:战斗力小于 2000 万
赛亚人一到三增强时,省略过程?无非就是继承的时候不调用 super.feature() 可以在增强类中直接写增强结果
2.3 分析
通过继承可以增强原有类的功能,但过多的增强需求会导致拓展功能类继承关系越来越多层,耦合度增加程度很高,一个类修改,会影响许多类被更改,有个不大官方的词管这种现象叫做 “类爆炸”
所以,长远考虑,这种方式不适合用来增强拓展原有类,因为耦合度太高了
三、装饰增强
3.1 思路
创建一个装饰器(或许代入这里的场景就是所谓的变身器了 XD),将功能拓展类的增强功能 “装饰” 到原有类上,具体做法如下:
装饰器:
public abstract class HumanDecorator implements Biology{
//要装饰的人类
private Biology human;
public HumanDecorator(Biology human) {
this.human = human;
}
@Override
public void feature() {
human.feature();
}
}
人类:
public class Human implements Biology{
@Override
public void feature() {
System.out.println("人类特性:战斗力小于 2 万");
}
}
超级赛亚人一:
public class SuperSaiyanI extends HumanDecorator {
public SuperSaiyanI(Biology human) {
super(human);
}
public void superSaiyanFeature() {
System.out.println("超级赛亚人一特性:战斗力小于 20 万");
}
@Override
public void feature() {
super.feature();
superSaiyanFeature();
}
}
超级赛亚人二:
public class SuperSaiyanII extends HumanDecorator {
public SuperSaiyanII(Biology human) {
super(human);
}
public void superSaiyanIIFeature(){
System.out.println("超级赛亚人二特性:战斗力小于 200 万");
}
@Override
public void feature() {
super.feature();
superSaiyanIIFeature();
}
}
超级赛亚人三:
public class SuperSaiyanIII extends HumanDecorator {
public SuperSaiyanIII(Biology human) {
super(human);
}
public void superSaiyanIIIFeature() {
System.out.println("超级赛亚人三特性:战斗力小于 2000 万");
}
@Override
public void feature() {
super.feature();
superSaiyanIIIFeature();
}
}
3.2 结果
测试一下增强效果:
public class Test {
public static void main(String[] args) {
//初始化普通人
Biology human = new Human();
human.feature();
System.out.println();
//人类变身超级赛亚人一
human = new SuperSaiyanI(human);
human.feature();
System.out.println();
//超级赛亚人一变身超级赛亚人二
human = new SuperSaiyanII(human);
human.feature();
System.out.println();
//超级赛亚人二变身超级赛亚人三
human = new SuperSaiyanIII(human);
human.feature();
}
}
输出:
人类特性:战斗力小于 2 万
人类特性:战斗力小于 2 万
超级赛亚人一特性:战斗力小于 20 万
人类特性:战斗力小于 2 万
超级赛亚人一特性:战斗力小于 20 万
超级赛亚人二特性:战斗力小于 200 万
人类特性:战斗力小于 2 万
超级赛亚人一特性:战斗力小于 20 万
超级赛亚人二特性:战斗力小于 200 万
超级赛亚人三特性:战斗力小于 2000 万
3.3 分析
原本需要一层又一层继承,一层又一层的堆积,才能实现新的功能拓展类
现在,不再有那么多层的继承关系,功能拓展类继承了装饰器,具有 “装饰” 的性质了,建立了新的功能拓展类,将原有类通过构造器传给功能拓展类,可以将旧类的功能装饰给功能拓展类,那么原有对象就有了新的功能
这个过程,原有对象的功能是通过不断 “装饰” 来得到的,功能拓展类间的耦合度大大降低,因为需求实在变动很大,直接删掉,重写一个新的功能类都可以,其他的功能拓展类却可以不变
四、总结
继承类增强的过程其实可以一句话解决:
Human human = new SuperSaiyanIII();
human.feature();
装饰器增强的过程其实也可以一句话解决:
Biology human = new SuperSaiyanIII(new SuperSaiyanII(new SuperSaiyanI(new Human())));
human.feature();
结合分析,无论是继承增强还是装饰器增强,都是要用到多态上转型为根本来操作的
这个过程中:
- 继承说白了就是不断继承,添加新功能..
- 装饰器说白了就是不断更新旧的原有对象,并结合旧的原有对象,添加新功能
五、应用
Java I/O 流:
//例一:控制台读取字符串
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
//例二:读取磁盘文件流
BufferedInputStream bis =
new BufferedInputStream(
new FileInputStream(
new File("/home/user/abc.txt")));