装饰者模式
类型:结构型
动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性。
具体做法就是不断的包装,然后获得总价可以通过递归的形式由内向外获得所有花费之和
可以参考下面这张图
- 将LongBlack咖啡先包装进Milk类的对象内,表示加入了牛奶
- 然后再包装进Chocalate类的对象内,表示加入了巧克力
- 然后又包装进Chocalate类的对象内,表示再次加入了巧克力
角色
- Component(被装饰对象的基类)
定义一个对象接口,可以给这些对象动态地添加职责。 - ConcreteComponent(具体被装饰对象)
定义一个对象,可以给这个对象添加一些职责。 - Decorator(装饰者抽象类)
维持一个指向Component实例的引用,并定义一个与Component接口一致的接口。 - ConcreteDecorator(具体装饰者)
具体的装饰对象,给内部持有的具体被装饰对象,增加具体的职责。
UML
包结构
component
基类
package 装饰者模式.component;
public abstract class Dish {
private String description="";
private int price=0;
public abstract int getAllCost();
public abstract String getAllDesc();
public void setDescription(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
具体子类
package 装饰者模式.component;
public class Fish extends Dish{
public Fish(){
super.setDescription("一条鱼");
super.setPrice(100000);
}
@Override
public int getAllCost() {
return super.getPrice();
}
@Override
public String getAllDesc() {
return super.getDescription();
}
}
--------------------------------------------
package 装饰者模式.component;
public class Soup extends Dish{
public Soup(){
super.setDescription("一碗汤");
super.setPrice(1000);
}
@Override
public int getAllCost() {
return super.getPrice();
}
@Override
public String getAllDesc() {
return super.getDescription();
}
}
--------------------------------------------
package 装饰者模式.component;
public class Tofu extends Dish{
public Tofu(){
super.setDescription("一块豆腐");
super.setPrice(10000);
}
@Override
public int getAllCost() {
return super.getPrice();
}
@Override
public String getAllDesc() {
return super.getDescription();
}
}
装饰者
基类
package 装饰者模式.decorator;
import 装饰者模式.component.Dish;
public class Seasoning extends Dish{
private Dish dish;
public Seasoning(Dish dish){
this.dish=dish;
}
@Override
public int getAllCost() {
//用递归的方法去获得价格
//加号前面为该菜之前的花费,加号后面为当前调料的花费
return dish.getAllCost()+super.getPrice();
}
@Override
public String getAllDesc() {
//用递归的方法去获得描述
return dish.getAllDesc()+super.getDescription();
}
}
具体子类
package 装饰者模式.decorator;
import 装饰者模式.component.Dish;
public class Oil extends Seasoning{
public Oil(Dish dish) {
super(dish);
super.setPrice(100);
super.setDescription("+油");
}
}
----------------------------------------
package 装饰者模式.decorator;
import 装饰者模式.component.Dish;
public class Salt extends Seasoning{
public Salt(Dish dish) {
super(dish);
super.setPrice(10);
super.setDescription("+盐");
}
}
----------------------------------------
package 装饰者模式.decorator;
import 装饰者模式.component.Dish;
public class Sugar extends Seasoning{
public Sugar(Dish dish) {
super(dish);
super.setPrice(1);
super.setDescription("+糖");
}
}
客户端
package 装饰者模式.client;
import 装饰者模式.component.Dish;
import 装饰者模式.component.Fish;
import 装饰者模式.component.Tofu;
import 装饰者模式.decorator.Oil;
import 装饰者模式.decorator.Salt;
import 装饰者模式.decorator.Sugar;
public class Client {
public static void main(String[] args) {
//未经处理的鱼
Dish fish = new Fish();
System.out.println(fish.getAllDesc());
System.out.println("花费为"+fish.getAllCost()+'\n');
//加油
fish = new Oil(fish);
System.out.println(fish.getAllDesc());
System.out.println("花费为"+fish.getAllCost()+'\n');
//加盐
fish = new Salt(fish);
System.out.println(fish.getAllDesc());
System.out.println("花费为"+fish.getAllCost()+'\n');
//加糖
fish = new Sugar(fish);
System.out.println(fish.getAllDesc());
System.out.println("花费为"+fish.getAllCost()+'\n');
//加盐
fish = new Salt(fish);
System.out.println(fish.getAllDesc());
System.out.println("花费为"+fish.getAllCost()+'\n');
/*-----------------------------------------------------*/
//未经处理的豆腐
Dish tofu = new Tofu();
tofu=new Oil(new Salt(tofu));
System.out.println(tofu.getAllDesc());
System.out.println("花费为"+tofu.getAllCost()+'\n');
}
}
效果
一条鱼
花费为100000
一条鱼+油
花费为100100
一条鱼+油+盐
花费为100110
一条鱼+油+盐+糖
花费为100111
一条鱼+油+盐+糖+盐
花费为100121
一块豆腐+盐+油
花费为10110
Process finished with exit code 0
总结
- 装饰者和被装饰者之间必须是一样的类型,也就是要有共同的超类。
- 在这里使用继承并不是实现方法的复制,而是实现类型的匹配。因为装饰者和被装饰者是同一个类型,因此装饰者可以取代被装饰者,这样就使被装饰者拥有了装饰者独有的行为。
- 虽然装饰者模式可以为设计注入弹性,但装饰者也常常造成设计中有大量的小对象,如果过度使用会让程序变得很复杂。
-
- 装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型