-
Notifications
You must be signed in to change notification settings - Fork 19
装饰者模式:玩了把坦克大战居然彻底搞懂了装饰者模式!
一直都有看到“包装者模式“ 出现在一些文章,甚至书中。它们被应用在装饰者模式和适配器模式中,这个原因笔者猜测源自 GOF 最早在书中给模式命名的时候提到了这两个模式的别名 wrapper同时还有适配器也被成为 wrapper, 所以有人将这几个名称混来混去。后来 GOF 在结尾讲书的简史的时候有提到一些模式的名称变化,其中 glue 改成了 facade, wrapper 改为 decorator ,walker 变成了 visitor 。
前阵子出于自己学习使用的原因开发了一个 chrome extension ,这样我的 chrome 变得比以前更强了,我赋予了它一个可以保存某个页面的某个片段的位置,后续通过点击这个记录可以快速的回到并高亮当时浏览的记录。
我给 chrome 简单的装饰了一下(加了一个插件),它就变强了
插件开源,可以作为基础进行二次开发,想要开发 chrome 插件但是不知道如何开始的可以参考。关注
星尘的一个朋友公众号,回复源码获取
动态地给一个对象添加一些额外的职责
上面两个举出的例子在实际过程中只要你想,你可以无限的装饰它,所以装饰者的类,可以一直嵌套下去。就像
InputStream in = new DataInputStream(new FileInputStream(new File("filePath")));
如果IO不熟,看得不理解,那再看看这个
List<Object> list = new ArrayList<>();
list = Collections.synchronizedList(list);
其实,当我写到这里的时候已经很清楚装饰者的概念了,它就是通过“套娃”变强了😂!
让我继续深入看看它的结构图👀。
一个浏览器的接口,一个 Chrome 实现类,一个 ChromeExtension 插件的实现类(用以装饰 Chrome),扩展中的构造函数为 浏览器类型,在插件类中会对浏览器 Brower 的 browse 方法进行一层装饰(增强,或减弱),在不改变对象的情况下,对对象行为进行动态的改变。
上面的类图在抽象一层的话就变成了了这个样子
为了示意,名称使用了中文,当然如果能够让人清楚你在干什么,无所谓你怎么表达。
通过对象的组合来实现类的增强要比继承更加的灵活。这也是软件设计原则中的组合复用原则的一种体现,优先使用组合,然后考虑继承。
关注公众号:星尘的一个朋友 回复:
源码
,获取全部代码和类图
代码演示通过一个游戏获取道具的方式来理解装饰者模式的具体实现;
想来想去,我没有选择超级玛丽,图难找,代码不好表达🤣
所以我选择了它 Tank Battle 😂
很多回忆都在这里
而且好表达😁
在这游戏中,我们吃到一个星星⭐的时候,就会变强,可以发射两发子弹,同时样子也会发生改变。
吃了这个星(装饰),我变得更强 ==>> ==>>
- 被装饰者接口
- 具体的装饰者类
- 抽象装饰者
- 具体的装饰者
因为星星强化一共有 3 个级别
-
移动、子弹加速
-
连发
-
打掉白色方块
所以我们应该有 3 个装饰者。类角色。
- 被装饰者接口 -> 坦克 Tank
- 具体的被装饰者 -> 玩家坦克 PlayerTank
- 抽象装饰者 -> 用来抽象定义装饰者基本信息,如构造函数等 TankDecorator
- 具体的装饰者 -> OneStarTankDecorator -> TwoStarTankDecorator -> ThreeStarTankDecorator
部分代码
关注公众号:
星尘的一个朋友
回复:源码
,获取全部代码和类图
/**
* 玩家坦克
*
* @author [email protected]
* @date 2020/10/25 11:40
* @since 1.0.0
*/
public class PlayerTank extends Tank {
/**
* 玩家默认坦克数据,移动,设计速度 1
*/
public PlayerTank() {
super(1, 1);
}
}
/**
* 定义坦克抽象装饰者
*
* @author [email protected]
* @date 2020/10/25 11:40
* @since 1.0.0
*/
public abstract class TankDecorator extends Tank{
protected Tank tank;
public TankDecorator(Tank tank) {
this.tank = tank;
}
@Override
protected void move() {
tank.move();
}
@Override
protected void fire() {
tank.fire();
}
}
测试结果
坦克增强的过程是一颗星一颗星获取的一个过程,一直在动态的增强。这个案例中只是一个维度,坦克吃星星。如果在增加一些额外的功能时,比如坦克变身,进化等等,不断的增加装饰时,就可以体会到装饰者模式组合的可扩展性。当然使用继承来实现的话,如果是单一不变的多种状况是很好的,比如说我的玩家坦克的选择不同的外观,可通过不同的子类来确定下来,但如果动态的想要增加一个类的时候,继承就显得非常的困难。
装饰者模式在不改变原对象的情况下,动态的增强具有较好的可扩展性。这也体现了开闭原则
。但我们发现,如果你不合理的使用装饰者模式,类的数量会变的更多,且多重装饰使一个对象的维护变的更加复杂。所以,就像前面说的,具体的特性就完全可以用继承来实现而非装饰者模式,装饰者模式一定是使用在想要动态的给对象增加一些功能的时候使用。
-
比如 JDK 中对 IO 的操作有一个 read() 操作,对它进行装饰之后就变成了 readLine().
-
再比如 JDK 中的 Collections 工具类,通过对集合类的装饰,使其变得线程安全,而对象本身却没有发生改变
仅仅是对原来的方法前面都加了 synchronized
关键字来对原对象做了增强
而 List 本身仍有更多的子类。Collections 工具类提供的就是对 List 对象做增强。
当我们明白了一件事物的本质之后,再去看表象会变的轻而易举。而这最关键的是要去亲自的操作它,看着再简单不过的东西,你第一次动手都会有很大的收获。这也让我想起了初中物理课本最常见的一句话 “动手动脑学物理”
任何情况下,看会和听懂都不是掌握。再不济语文课也学过“书读百遍其义自见”也是要动动嘴的🗣。加油!
MI
2. 原型模式:啥?盗图、盗文章的人居然用的是一种设计模式!原型模式?
8. 桥接模式:这个不常用的设计模式居然被我学的最透,草率了!
9. 适配器模式:今天轻松点,就说说什么是“榫”,什么是“卯”,什么是“榫卯”!
10. 外观模式:书生的家书是谁送的?书童到底是个什么角色?
11. 享元模式:如果让你开发英雄联盟的兵线,你会怎么设计?
14. 模板方法模式:你知道AQS它是干什么的吧,那这个框架是怎么设计的呢?
16. 责任链模式:“张三为了纪念王二请假的悲催经历想出来的一种设计模式”
19. 命令模式:如果把请求变成一个对象,在一些场景更好用!
20. 状态模式:从工作状态,再到订单状态一点点深入学习状态模式
21. 中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。
23. 解释器(Interpreter)模式:提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。