前言
最近看了一部英剧《黑镜》,片中讲述科技与人性碰撞可能带来的一系列社会负影响,包括泛娱乐社会对底层人民精神的麻木作用、人工智能可能带来的将短期痛苦无限延长等等影响,感慨万分,在这里极力推荐大伙看看这部神剧。不扯了,下面开始聊聊装饰者模式。
定义
先给出装饰者模式的定义(来自维基百科):装饰者模式,是面向对象编程领域中,一种动态地往一个类中添加新的行为的设计模式。就功能而言,修饰模式相比生成子类更为灵活,这样可以给某个对象而不是整个类添加一些功能。装饰者模式出现的意义是解决了运行时类功能的拓展,使得可以在运行时进行任一功能组合而不是需要为每种组合设计一个类(参见javaIO的设计)
相关设计原则
装饰者模式遵循开放-关闭原则,及类应该对扩展开放,对修改关闭的原则。也就是说我们在项目开发过程中应尽量减少对已完成部分的修改,把重心放在功能的拓展至上。
基本结构
这里我们通过UML类图来直观地理解装饰者模式的结构。如图:
这里有几个地方需要注意一下:装饰者与被装饰者继承自同一个抽象类或者接口(Component)的原因:我们利用多态,通过将装饰者与被装饰者继承自同一父抽象类或者接口实现被继承者在被装饰后类型不变的目的,也可以实现被装饰者相互修饰的目的。第二个需要注意的是,每个装饰者必须写好它们的描述属性,便于后期检查维护。
实例
下面利用一个例子来讲解装饰者模式的使用方法。下面的例子纯属瞎扯我们的主角VinceBarry是一名NBA新秀,他想要提高自己的篮球能力,通过学习模仿一些巨星的技巧是他最终选择的方案。于是他先学习了杜兰特的跳投技巧,然后不过瘾又学习了库里的三分球和欧文的运球技巧,最终他的能力达到了130超过了2k的上限……扯远了,下面我们利用代码来为VinceBarry增加能力。
创建组件抽象类
首先我们需要一个父抽象类,这里我们命名为PlayBasketball。下面是他的具体实现:
abstract class PlayBasketball {
String description;
public String getDescription(){
return description;
}
public abstract int score();
}
这里我们为VinceBarry和技能们抽象出了两个共有的属性:描述和技能值。从这里也可以看出这个抽象类应该是装饰者和被装饰者共有属性方法的容器。
创建装饰者抽象类
下面我们再创建技能包的抽象类(对照上面的UML类图),我们取名为BasketballSkill,下面贴上代码:
abstract class BasketballSkill extends PlayBasketball {
public abstract String getDescription();
}
这个类继承自父抽象类PlayBasketball,类中有一个方法用于描述不同的装饰者,这个是装饰者必须实现的方法。
创建各种装饰者
接下来就是创建各种技能了,这里我创建了三个技能:CurryThreePoint,DurantJumpShoot,IrvingDribbling。由于后两个代码与第一个类似,我就只展示CurryThreePoint这项技能的代码了。
class CurryThreePoint extends BasketballSkill {
private PlayBasketball playBasketball;
CurryThreePoint(PlayBasketball playBasketball) {
this.playBasketball = playBasketball;
}
@Override
public String getDescription() {
return playBasketball.getDescription()+" has curry's three point skill";
}
@Override
public int score() {
return 40 + playBasketball.score();
}
}
下面阐述一下这个类中的几个关键点:首先是继承自装饰者父类,所以必须重写getDescription()方法。然后我们重写最关键的方法:score()。这个方法的特点是,将装饰者中的特定行为与传入的被装饰者(也可能是装饰者)的行为进行叠加,这里的装饰者是我们在构造方法中传入的Playbasketball类型的对象。通过这种方式实现了对象行为的修改或功能的拓展。也就是在这里我们为VinceBarry加上了库里三分球的能力。
测试代码
下面我们测试一下整个流程(关于装饰者的构建,在工厂和生成器模式中有更加优秀的方案,日后再扯):
public class Court {
public static void main(String[] args) {
PlayBasketball vinceBarry = new VinceBarry();
System.out.println(vinceBarry.getDescription() + " " + vinceBarry.score());
vinceBarry = new DurantJumpShoot(vinceBarry);
vinceBarry = new CurryThreePoint(vinceBarry);
vinceBarry = new IrvingDribbling(vinceBarry);
System.out.println(vinceBarry.getDescription() + " " + vinceBarry.score());
}
}
输出结果为
总结
又到一学期快结束的时间段了,想想近来自己也没学啥,赠给自己一句话:就是干!