装饰者模式:动态地将责任附加到对象上,若要扩展功能,装饰者提供比继承更有弹性的替代方案。
设计类(开放-关闭原则):要对扩展开放,对修改关闭。多用组合,少用继承。高内聚低耦合~
具体原因: 当我们设计的类不能满足我们的需求的时候,我们可能设计一个类去继承它,但是这样就会使对象之间高度的耦合。
此时, 我们就可以考虑使用装饰者模式(把对象嵌入我们要扩展功能的类中,调用他的方法,然后跟我们定义的方法一起返回我们需要的类型)。
这个可能跟适配器模式有点相似,但又不同。
UML类图:
我们先来看看装饰者模式的类图,再来详细讲述:
1、Component是基类。通常是一个抽象类或者一个接口,定义了属性或者方法,方法的实现可以由子类实现或者自己实现。通常不会直接使用该类,而是通过继承该类来实现特定的功能,它约束了整个继承树的行为。比如说,如果Component代表人,即使通过装饰也不会使人变成别的动物。
2、ConcreteComponent是Component的子类,实现了相应的方法,它充当了“被装饰者”的角色。
3、Decorator也是Component的子类,它是装饰者共同实现的抽象类(也可以是接口)。比如说,Decorator代表衣服这一类装饰者,那么它的子类应该是T恤、裙子这样的具体的装饰者。
4、ConcreteDecorator是Decorator的子类,是具体的装饰者,由于它同时也是Component的子类,因此它能方便地拓展Component的状态(比如添加新的方法)。
每个装饰者都应该有一个实例变量用以保存某个Component的引用,这也是利用了组合的特性。在持有Component的引用后,由于其自身也是Component的子类,那么,相当于ConcreteDecorator包裹了Component,不但有Component的特性,同时自身也可以有别的特性,也就是所谓的装饰。
具体例子:
当我们去咖啡厅的时候。本来你点一杯苦coffe,然后他的价格是10元,你尝了之后发现有点苦,想要加点糖,然后加这个糖的价格是2元;
然后过了一会你的朋友过来了,他叫了一杯拿铁coffee,然后加了点牛奶(假设可以加),这个牛奶的价格是3元。怎么实现?
(一) coffie抽象类
public abstract class coffee {
String coffeeInformation = "普通咖啡";
public String getCoffeeInformation(){
return coffeeInformation;
}
public abstract double cost();
}
(二)具体被装饰者(coffie的两种具体子类)
public class coffee1 extends coffee {
public coffee1(){
coffeeInformation = "coffee1";
}
@Override
public double cost() {
return 10.0;
}
}
public class coffee2 extends coffee {
public coffee2(){
coffeeInformation = "coffee2";
}
@Override
public double cost() {
return 12.0;
}
}
(三)装饰者类(抽象类)
public abstract class AddThings extends coffee {
public abstract String getCoffeeInformation();
}
(四)具体装饰者
public class milk extends AddThings{
coffee coff; //创建coffie的引用(组合思想)
public milk(coffee c) {
this.coff = c;
}
@Override
public String getCoffeeInformation() {
String addThings = coff.getCoffeeInformation()+"添加"+"milk";
return addThings;
}
@Override
public double cost() {
return 3.0+ coff.cost();
}
}
public class sugar extends AddThings {
coffee coff;
public sugar(coffee c) {
this.coff = c;
}
@Override
public String getCoffeeInformation() {
String addThings = coff.getCoffeeInformation()+"添加"+"sugar";
return addThings;
}
@Override
public double cost() {
return 2.0+coff.cost();
}
}
(五)
public class cost {
public static void main(String[] args) {
coffee c1 = new coffee1();
coffee c2 = new coffee2();
coffee a1 = new sugar(c1);
coffee a2 = new milk(c2);
System.out.println(a1.getCoffeeInformation()+" "+a1.cost());
System.out.println(a2.getCoffeeInformation()+" "+a2.cost());
}
}
韦恩图
特点
(1)装饰者和被装饰者有相同的接口(或有相同的父类)。
(2)装饰者保存了一个被装饰者的引用。
(3)装饰者接受所有客户端的请求,并且这些请求最终都会返回给被装饰者(参见韦恩图)。
(4)在运行时动态地为对象添加属性,不必改变对象的结构。
使用装饰者模式的最大好处就是其拓展性十分良好,通过使用不同的装饰类来使得对象具有多种多样的属性,灵活性比直接继承好。
然而它也有缺点,那就是会出现很多小类,即装饰类,使程序变得复杂。