来一局紧张刺激的吃鸡——浅谈装饰者模式
装饰者模式
声明:本文为原创,如有转载请注明转载与原作者并提供原文链接,仅为学习交流,本人才识短浅,如有错误,敬请指正
想看本系列前作的朋友可以点击下面链接哦。
一起去开心的购物吧——浅谈观察者模式
记一场精彩的篮球比赛——浅谈策略模式
大家好,又跟大家见面啦,今天呢,我与大家分享一个新的Java设计模式——装饰者模式。
首先,不能免俗的我们来看一下官方给的定义:
装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
通过这个官方定义,我们也许还理不清这是一个怎么样的设计模式,但是大家肯定都会觉得这绝对是一个非常灵活的适合后期维护的设计模式,为什么呢,短短的一句话中,我们发现了动态,扩展功能,更有弹性这些令人激动的词语(至少对维护一群“屎山”代码的程序员来说足够让人激动),同时,这个设计模式还直接diss了Java中一个非常普遍的概念,继承,这时候也许就会有支持“继承”的拥趸出来呐喊:凭什么这么说我家爱豆,她发高烧50度还在坚持构建对象,实在是太伟大了。OKOK,稍后我们会说明原因滴,此处我们先表下不提。
最近有一款游戏呢非常的火爆,他就是风暴英。。咳咳(风暴要火),他就是APEX,作为一款吃鸡类型的游戏,她承接了从H1Z1到绝地求生到各种吃鸡手游的热度,可以说目前是非常的火爆,对于这种类型的游戏,相信各位看官不会感到陌生,不少人估计更是忠实玩家,在这种开局一个人,装备全靠捡的游戏中,拥有一把好枪无疑是胜利的最佳保障(伏地魔除外,苟就完事了),而一把好枪,除了枪本身之外,还有众多的组件可供搭配,从消音器到瞄准镜再到握把,将不同的组件组装到一把枪中,最终就可以拥有一把威力强大的好枪。
装饰者模式便是实现了这样的功能。
在装饰者模式中,有着两个最重要的角色,一个称为装饰者,另一个称为被装饰者,被装饰者通过装饰者“装饰”自己,而装饰者可以在被装饰者的行为之前或之后加上自己的行为,已达到特定的行为目的。
那么现在是时候开始我们的吃鸡之旅了。在游戏中,被装饰者就是“枪”。我们首先定义一个被装饰者接口,用于定义被装饰者的行为。
public interface Gun { String getName(); String showPower(); }
第一个方法用于返回枪的名称,随着组件的增加,枪的名称会发生变化。第二个方法用于展示当前枪的威力,当然我们是用一句话来形容而不是真的开一枪。
接下来我们定义一个装饰者接口,即各种组件的接口
public interface Unit extends Gun{ @Override String getName(); }
此处,我们发现,组件接口继承了枪接口,并且会重写Gun中的getName方法,这里我们可以得出结论,在装饰者模式中,装饰者和被装饰者对象有相同的超类型!
然后我们该定义几个具体被装饰者了,在这里,我们会定义M16与98K两种型号的枪充当具体被装饰者
public class _98K implements Gun { private String power = "一把威力巨大的98K"; private String name = "98K"; @Override public String getName() { return name; } @Override public String showPower() { return power; } }
public class M16 implements Gun { private String power = "一把射速很快的M16"; private String name = "M16"; @Override public String getName() { return name; } @Override public String showPower() { return power; } }
两个具体被装饰者大同小异,不再赘述。
然后是我们的几个具体组件对象,此处呢,我们定义了三种组件以供搭配——八倍镜,扩容弹夹,握把。
public class Grip implements Unit { private Gun gun; private String function = ",加装握把,提高射击稳定性"; private String name = "握把"; public Grip(Gun gun){ this.gun = gun; } @Override public String getName() { return gun.getName() + "+" + name; } @Override public String showPower() { return gun.showPower() + function; } }
我们仔细来看这个握把的具体实现类,我们发现,它维护了一个被装饰者的引用,并在构造方法中将其初始化,随后实现接口定义的方法,在调用被装饰者的方法时,我们还夹带了一点私货,还记得上面的话吗:装饰者可以在被装饰者的行为之前或之后加上自己的行为,已达到特定的行为目的!在这里,我们真正实现了“装饰”这一步。
同样得,还有八倍镜组件以及扩容弹夹组件
public class SightTelescope implements Unit { private Gun gun; private String name = "八倍镜"; private String function = ",加上八倍镜,射击更加精准"; public SightTelescope(Gun gun){ this.gun = gun; } @Override public String getName() { return gun.getName() + "+" + name; } @Override public String showPower() { return gun.showPower() + function; } }
public class ExpansionCartridgeClip implements Unit { private Gun gun; private String name = "扩容弹夹"; private String function = ",换装扩容弹夹,绝对火力压制"; public ExpansionCartridgeClip(Gun gun){ this.gun = gun; } @Override public String getName() { return gun.getName() + "+" + name; } @Override public String showPower() { return gun.showPower() + function; } }
至此,我们已经成功定义了我们所有的装饰者与被装饰者,赶快开始这一局紧张刺激的吃鸡吧。我们的装饰者模式将在游戏中经受考验。
public class Game { public static void main(String[] args) { Gun _98k = new _98K(); show(_98k); _98k = new ExpansionCartridgeClip(_98k); show(_98k); _98k = new SightTelescope(_98k); show(_98k); Gun m16 = new M16(); show(m16); m16 = new Grip(m16); show(m16); m16 = new ExpansionCartridgeClip(m16); show(m16); m16 = new SightTelescope(m16); show(m16); } private static void show(Gun gun){ System.out.println("拥有了" + gun.getName() + "\n" + gun.showPower()); } }
我们首先创建了98k的对象,使用打印方法查看它的状态,然后我们的装饰者,也就是组件开始对98k进行装饰,我们正在组装一把威力强大的98K,有人也许想问,为啥new出来的组件对象可以直接赋值给98k对象呢,别忘了,装饰者模式的关键,装饰者和被装饰对象有着相同的超类型。
同样的,我们使用组件装饰了一把m16步枪,是时候开火了,让我们看看到底这些枪有没有按照我们的想法正常工作呢?
duang,就像加了许多特技,我们的枪再也不用裸奔了,他现在是拥有很多组件的大杀器,是时候送快递大吉大利,今晚吃鸡了。
如果我们喜欢,还可以再加上很多的组件,只要它实现了装饰者接口并按照正确的方式实现,我们就可以在运行时动态的,不限量地使用它来装饰对象,而且不用修改原有的代码,对象可以在任何时候被装饰,只要它的确是一个被装饰者(实现了被装饰者的接口),而这些,都是单纯的继承无法实现的,这也就是为什么我们开头所说,装饰者提供了比继承更具有弹性的方案。这个模式再次着重表现了我们的一个设计原则——对修改关闭,对扩展开放!
感谢大家听我絮絮叨叨这么久,那么装饰者模式的浅谈就先进行到这里,不说了,我先吃鸡去了(^O^)