-
定义:以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,在不改变原来代码的情况下,扩展功能。
-
设计原则:开闭原则,对扩展开放,对修改关闭。
-
举个栗子:
星巴克有很多种咖啡,有很多种调料,目的是为了计算最终的价格cost方法(添加调料后)和此种咖啡的描述getDesp方法(咖啡名+调料1+调料2),这里引申出来的问题是如何动态的添加调料,在不修改原来代码的情况下计算。到这里,我们可以想想一下代码结构了,首先有一个咖啡类,他是个父类,里面有cost和getDesp方法,若干个子类继承此类重写实现。
-
想当然的做法:
如果按照web系统开发的思维方法,再有持久层的时候,我们可以把咖啡作为一个实体对象,调料呢可以是一个字典(id,名字,价格),想象一下,你在网页上有多个select下拉框,这里把调料都展示出来,用户要什么就选中什么,后面再有个输入框是数量,而咖啡则与调料则是一对多关系,在实体中有个map体现,key为调料的id,value为数量,而计算价格和描述呢,遍历这个map就可以了,这是贼舒服。
-
分析:
可是为啥还要用这个装饰着模式来解决?我认为学习设计模式,看懂这个模式的代码贼简单,关键是要理解他为什么这么干,当他提出问题的场景时,我们先自己考虑一种做法,再与真正的模式作比较,思考模式带来的好处。那么上面提出的想当然做法,其实也挺好啊,与模式比较,谁对说错呢?其实首先我们进入了一个误区,用设计模式 不该考虑持久化的事情,应该用单纯的javase解决问题。从持久化来看,我们调料是一个类,有若干条数据,如果不用持久化,则我们每一个调料都是一个类,然后咖啡持有一个list调料的集合,往里面add,遍历这个list即可了,与持久化相比不过是每条数据算一个类罢了,这样可不可以呢???其实在咖啡计算这个场景下是可以的,但是这里我们只是简单的计算功能,这里调料类不过起到了存储数据的作用,如果功能更复杂了呢?需要多行代码处理一些其他的事情?回到上面的问题,如何在不修改原来代码,动态扩展功能(添加调料,计算价格),此为开闭原则。
-
术语:组件(相当于咖啡),装饰者(相当于调料)
-
本人总结:
装饰者要扩展原来组件的功能,怎么扩展?说白了:装饰者要持有组件对象,装饰者有一个方法对应扩展组件中的一个方法。怎么理解:要扩展原来对象的功能,我首先得有原来对象啊,所以我用一个成员变量持有原来对象,我再有个扩展方法 扩展 组件中需要扩展的方法,前提是这个需要扩展的方法,是有返回值的,他不可能是void啊,因为我需要对他TA个值再进行二次的处理啊,这才叫扩展啊。这个道理很容易理解,其实这里扮演装饰者的角色,你当然可以在任何一个类,方法中都可以做到,但那怎么还称之为模式啊,这里装饰者类是只做这个事情,只与原来组件相关,他是不耦合任何其他操作的。模式是有固定的抽象层面的套路,这里的套路是组件有个父类,组件中需要变化的方法在父类中,由子类重写,而装饰者是为了扩展这些变化的方法,所以装饰者直接或间接也继承组件父类,并持有组件父类引用。
值得注意的是,java中io是用了装饰者模式,这就不难理解为什么有的io类构造方法是传入另一个io类。
-
UML:
-
代码:
public abstract class AbstractCoffee {
String name = "unknown coffee";
public String getName() {
return name;
}
public abstract double cost();
}
public class ACofffee extends AbstractCoffee {
public ACofffee() {
name = "A";
}
@Override
public double cost() {
return 1.99;
}
}
public class BCoffee extends AbstractCoffee {
public BCoffee() {
name = "B";
}
@Override
public double cost() {
return 0.99;
}
}
public class Mocha extends AbstractCoffee {
private AbstractCoffee abstractCoffee;
public Mocha(AbstractCoffee abstractCoffee) {
this.abstractCoffee = abstractCoffee;
}
@Override
public double cost() {
return abstractCoffee.cost() + 0.2;
}
@Override
public String getName() {
return abstractCoffee.getName() + ",Mocha";
}
}
public static void main(String[] args) {
AbstractCoffee coffee = new ACofffee();
coffee = new Mocha(coffee);
coffee = new Mocha(coffee);
System.out.println(coffee.cost());
}
结果:`2.39`