什么是装饰者模式
装饰者模式是动态地给一个对象添加一些额外的职责,就增加功能来说,装饰者模式比生成子类更为灵活。
原理类图:
需要说明的一点是,不必一定按照上面的形式,如果没有抽象出Component的需求,可以直接让装饰者继承具体组件,毕竟,本来装饰者就是给具体组件装饰的。
为什么要用装饰者模式
假设有一位女生想要出门,这出门要想精致一点,就得化化妆,化妆这件事用代码怎么模拟呢?
基本实现如下:
首先得有一个人:
public class Person {
public void foundation() {
System.out.println("打粉底...");
}
public void shadow() {
System.out.println("画眼影...");
}
public void lipstick() {
System.out.println("涂口红...");
}
}
现在出门:
public class GoOut {
public static void main(String[] args) {
Person person = new Person();
person.foundation();
person.shadow();
person.lipstick();
}
}
很好,现在就完成了出门化妆的工作。
但是,这似乎是违背开闭原则的吧。如果再想要涂个腮红,岂不是要修改Person类了?
那么就来修改一下,人不再具有这些动作,而是让各个化妆品独立出来,成为一个个独立的类:
Person:
public class Person {
public void makeUp(){
System.out.println("Person make up...");
}
}
化妆品:
public abstract class Cosmetic {
public abstract void makeUp();
}
具体化妆品:
public class Foundation extends Cosmetic {
@Override
public void makeUp() {
System.out.println("打粉底...");
}
}
public class Lipstick extends Cosmetic{
@Override
public void makeUp() {
System.out.println("涂口红...");
}
}
public class Shadow extends Cosmetic {
@Override
public void makeUp() {
System.out.println("画眼影...");
}
}
现在出门:
public class GoOut {
public static void main(String[] args) {
Person person = new Person();
Foundation foundation = new Foundation();
Lipstick lipstick = new Lipstick();
Shadow shadow = new Shadow();
person.makeUp();
foundation.makeUp();
lipstick.makeUp();
shadow.makeUp();
}
}
非常好,现在我们不光完成了出门化妆的动作,还遵循了开闭原则,多一个化妆动作就多来一个类即可。
但是,再仔细想一想这样的结果,这是出了门了,然后打粉底、画眼影、涂口红,这不就是素颜出了门,然后在路上化的妆吗。这似乎有些与日常不符,日常往往都是在家里化完妆再出门。
我们不妨换一个角度想,化妆不是自己在脸上画,不再是一个自己主观的一个动作,而是一个化妆品在脸上装饰的过程。
但是这化妆品怎么自己跑到脸上啊,还化妆?没糊成一坨就不错了。
很容易啊,请个化妆师(装饰者)啊,让化妆师来化妆,那化妆品可不就来装饰脸了吗。
那么实现一下吧:(Person类与之前相同,就不重复代码了)
化妆师:(这里叫类名还是Cosmetic,只是代表化妆师这个位置,并非平常所说的化妆师,假设化妆品自带装饰属性就可以)
public class Cosmetic extends Person {
protected Person person; //要知道是给"谁"化妆(装饰)
public void decorate(Person person) {
this.person = person;
}
public void makeUp() {
if (person != null) {
person.makeUp(); //这将会是一个递归的过程,直至一个没有装饰过的对象
}
}
}
各个化妆品:
public class Lipstick extends Cosmetic{
@Override
public void makeUp() {
super.makeUp();
System.out.println("涂口红...");
//在已经装饰过的对象的基础上,再进行一次装饰,这么说有一些不准确,makeUp()主要是用来展示用的。
}
}
public class Shadow extends Cosmetic {
@Override
public void makeUp() {
super.makeUp();
System.out.println("画眼影...");
}
}
public class Foundation extends Cosmetic {
@Override
public void makeUp() {
super.makeUp();
System.out.println("打粉底...");
}
}
现在出门吧:
public class GoOut {
public static void main(String[] args) {
Person person = new Person();
Foundation foundation = new Foundation();
Lipstick lipstick = new Lipstick();
Shadow shadow = new Shadow();
foundation.decorate(person); //用粉底装饰一个素颜的人
lipstick.decorate(foundation); //用口红装饰一个打过粉底的人
shadow.decorate(lipstick); //用眼影装饰一个涂过口红(也打过粉底)的人
shadow.makeUp(); //化上妆整体展示一下
}
}
这样,就完成了出门的化妆过程。
当前类图:
和建造者模式很像?
我们来看一下这两个模式:
- 建造者模式:开发商找了一个施工队的负责人来负责盖房子
- 装饰者模式:女生找了一个化妆师来给自己化妆
嘶,这似乎还真有点像,但也仅仅是一点了。
回想一下建造者模式(可以点击这里跳转),我们要建的东西是有一套固定流程的,先打地基,再砌墙,最后盖房顶,这几步对于一个型号的房子来说,是固定的,不会因为今天施工队心情好多干一步或心情不好少干一步,是一个固定的流程(或者说是一个静态的过程),如果不按这个流程走,很可能会出现难以预料的问题。
但现在的装饰者模式不一样,可以根据自己的心情来装饰:今天不得不出门,可以不化妆;出门不见什么人,随随便便涂点防晒好了;今天要见的人很重要,要好好打扮一下,打个粉底、涂个口红、画个眼影,甚至还有更多的步骤;这些都没有一套固定的流程,比如画眼影和涂口红,谁先谁后也都无所谓(不过某些步骤还是要注意先后顺序的哈),这是一个动态的过程。
另外需要提到的一点是,建造者模式是创造型模式,而装饰者模式是结构型模式。