人类的军工发展史就是一场矛与盾的追逐,矛利则盾坚,盾愈坚则矛愈利。在传统的冶金工艺下,更坚固的盾牌和盔甲往往意味着更迟缓笨重的运动能力和更高昂的移动成本。从战国末期的魏武卒、秦锐士,到两宋之交的铁浮图、重步兵,再到有明一朝的边军四大营、神机营,最后到现代战争的各式主战坦克…防御越高,移动越慢。
甲辰年正月初三,窗外的声声爆竹撩拨着执笔人的心绪。笔者每每读到一些战史时都会有一种幻想,在没有火器的时代,如果身穿一副重型盔甲,刀劈不入,箭射不穿,那不就是高达一样的存在吗?
实际上,在没有技术爆炸的情况下,传统的重型盔甲会让普通人寸步难行,防御每提高一分,重量必然也会提高一分。这种层层嵌套的规律,特别契合装饰者模式的设计理念。
一言
就像打包一个快递一样,装饰者模式动态的将新功能附加在对象上,在对象功能扩展方面,它比继承更有弹性,也体现了开闭原则。
从盔甲到装甲
好的各位,欢迎来到奇珍异品收藏室,在你的面前有原始人的兽皮衣、秦始皇的金缕玉衣、唐太宗的鳞甲、史塔克的马克四十、反浩克装甲和T62主战坦克。
我需要一个程序,在我穿上了不同盔甲又登上了不同的载具之后,算出体系的整体重量。
进击的莽撞人
“大哥,这题俺会!看俺的!”
莽撞人搓搓手,开始展示他的设计思路:
在莽撞人展示思路的同时,我们不妨先尝试理解下问题的本质。
在我们的面前,有大量的盔甲和装甲,我既可以穿着马克装甲进入反浩克装甲,也可以穿着原始人的兽皮衣进入T62主战坦克…
这种组合有很多种,如果求具体的数值一定是一个很经典的排列组合高考题。
在这种情况下,莽撞人的设计思路的确能够暂时的解决业务需求。但是从长远的设计角度考虑,这种设计会产生很多的类,如果奇珍异品收藏室的宝物越来越多,类的数量就会倍增进而引发类爆炸。
考虑到莽撞人的高爆发,我们选择悄咪咪的离开。
才出狼窝又如虎穴
“哥哥!我!他的方案有瑕疵,我有个大胆的想法,你给看看!”
未成想,转角遇到另一个莽撞人。
“哥哥,不就是类爆炸的问题嘛?你看我的,咱把不能贴身穿的都给它做成内置的,怎么样?”
不得不说,这种方案确实没有之前那么鲁莽,但是问题的本质并不是类爆炸,而是由类爆炸带来的扩展性低的问题。现在通过内置的确在一定程度上缓解了类爆炸的问题,但是并没有解决宝物新增带来的系统难以维护的问题。
装饰者模式
终于,两位莽撞人收了神通,准备听听我们的方案。
其实我们通过仔细拆解需求可以发现,是可以将这些宝物分为主体和包装的,这一点在第二个方案里实际上也有体现,只不过第二个方案并没有处理好主体和包装的关系。
主体其实就是莽撞人所说的贴身穿的,比如各式盔甲、马克40等等,而包装则是像T62坦克这种不一定会被方案选中的装甲。主体是被装饰者,包装是装饰者,装饰者可以聚合被装饰者。
举个例子:我穿着马克40装甲坐在两栖作战车上的T60坦克里。贴身的马克装甲是被装饰者,T62和两栖车都是装饰者。
设计
通过weight()递归计算体系重量。
代码实现
抽象
public abstract class AbsSuit {
public String des;
private float wit = 0.0f;
public abstract float weight();
//setter&getter
}
装备&盔甲
public class Suit extends AbsSuit{
@Override
public float weight() {
return super.getWit();
}
}
马克40
public class Mark40 extends Suit{
public Mark40() {
setDes("马克40装甲已装配");
setWit(30000);
}
}
包装抽象
public class Decorator extends Suit{
private Suit obj;
public Decorator(Suit obj) {
this.obj = obj;
}
@Override
public float weight() {
return super.getWit()+obj.weight();
}
@Override
public String getDes() {
return super.des+" "+super.getWit()+"&&"+obj.getDes();
}
}
T62主战坦克
public class T62 extends Decorator{
public T62(Suit suit) {
super(suit);
setDes("T62主战坦克已装配");
setWit(70000);
}
}
两栖运输车
public class TranCar extends Decorator{
public TranCar(Suit suit) {
super(suit);
setDes("两栖运输车已装配");
setWit(10000);
}
}
称重工厂
public class XFactory {
public static void main(String[] args) {
Suit suit = new Mark40();
System.out.println(suit.getDes());
System.out.println("称重1=" + suit.weight() + "kg");
suit = new T62(suit);
System.out.println(suit.getDes());
System.out.println("称重2=" + suit.weight() + "kg");
suit = new TranCar(suit);
System.out.println(suit.getDes());
System.out.println("称重3=" + suit.weight() + "kg");
}
}
____启动?
装饰者模式在JDK IO 的应用
在JDK 的 IO 机构中,InputStream族的设计模式就是装饰者模式。
InputStream是抽象类,FilterInputStream是装饰者,InputStream及其子类都是被装饰对象。
结
相信大家通过以上的例子会对装饰者模式有了进一步的了解。在我看来,装饰者模式就是一个套盒子的游戏,它通过巧妙的嵌套和递归,将原本需要类扩展的逻辑封装在了关系内,在不违反开闭原则的前提下使得代码更加的优雅,便于维护和理解。
关注我,共同进步,龙年大吉。——Wayne