《Head First 设计模式》:装饰者模式
2024-09-07 14:52:21
正文
一、定义
装饰者模式动态地将责任(功能)附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
要点:
- 装饰者和被装饰者有相同的超类型。
- 可以用一个或多个装饰者包装一个对象。
- 既然装饰者和被装饰者有相同的超类型,所以在任何需要原始对象(被装饰者)的场合,都可以用装饰过的对象代替它。
- 装饰者可以在被装饰者的行为之前与/或之后,加上自己的行为,甚至将被装饰者的行为整个取代掉,以到达特定的目的。
- 对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地用装饰者装饰对象。
- 装饰者会导致设计中出现许多小对象,如果过度使用,会让程序变得很复杂。
二、实现步骤
1、创建组件接口
装饰者和被装饰者都必须实现组件接口。
也可以用组件抽象类,然后让装饰者和被装饰者继承组件抽象类,只要装饰者和被装饰者具有相同的超类型即可。
/**
* 组件接口(装饰者和被装饰者都必须实现该接口)
*/
public interface Component {
public void doSomething();
}
2、创建具体的组件,并实现组件接口
/**
* 具体组件(被装饰者)
*/
public class ConcreteComponent implements Component {
@Override
public void doSomething() {
System.out.println("ConcreteComponent do something...");
}
}
3、创建装饰者抽象类,并实现组件接口
如果只有一个装饰者,也可以不创建装饰者抽象类,而是由具体的装饰者直接实现组件接口
/**
* 组件装饰者抽象类
*/
public abstract class ComponentDecorator implements Component {
protected Component component;
public ComponentDecorator(Component component) {
// 通过构造传入组件(被装饰者)
this.component = component;
}
@Override
public void doSomething() {
// 委托给组件(被装饰者)
component.doSomething();
}
}
4、创建具体的装饰者,并继承装饰者抽象类
(1)装饰者 A
/**
* 装饰者A
*/
public class ComponentDecoratorA extends ComponentDecorator {
public ComponentDecoratorA(Component component) {
super(component);
}
@Override
public void doSomething() {
// 装饰者添加自己的业务代码
component.doSomething();
// 装饰者添加自己的业务代码
System.out.println("ComponentDecoratorA do something...");
}
}
(2)装饰者 B
/**
* 装饰者B
*/
public class ComponentDecoratorB extends ComponentDecorator {
public ComponentDecoratorB(Component component) {
super(component);
}
@Override
public void doSomething() {
// 装饰者添加自己的业务代码
component.doSomething();
// 装饰者添加自己的业务代码
System.out.println("ComponentDecoratorB do something...");
}
}
5、使用装饰者装饰组件
public class Test {
public static void main(String[] args) {
// 具体组件(被装饰者)
Component component = new ConcreteComponent();
// 用装饰者A装饰组件
ComponentDecorator componentDecoratorA = new ComponentDecoratorA(component);
// 用装饰者B装饰组件
ComponentDecorator componentDecoratorB = new ComponentDecoratorB(component);
component.doSomething();
componentDecoratorA.doSomething();
componentDecoratorB.doSomething();
}
}
三、举个栗子
1、背景
星巴兹是以扩张速度最快而闻名的咖啡连锁店。因为扩张速度实在太快了,他们准备更新订单系统,以合乎他们的饮料供应要求——
顾客在购买咖啡时,可以要求在其中加入各种调料,例如:蒸奶、豆浆、摩卡(巧克力风味)或覆盖奶泡。星巴兹会根据所加入的调料收取不同的费用。所以订单系统必须考虑到这些调料部分。
2、实现
把调料理解为饮料装饰者,然后以饮料为主体,用调料来“装饰”饮料。
(1)创建饮料抽象类
/**
* 饮料抽象类(组件)
*/
public abstract class Beverage {
public String description = "Unknown Beverage";
/**
* 描述
*/
public String getDescription() {
return description;
}
/**
* 价格
*/
public abstract double cost();
}
(2)创建具体的饮料,并继承饮料抽象类
/**
* 浓缩咖啡
*/
public class Espresso extends Beverage {
public Espresso() {
description = "Espresso";
}
@Override
public double cost() {
return 1.99;
}
}
/**
* 综合咖啡
*/
public class HouseBlend extends Beverage {
public HouseBlend() {
description = "House Blend Coffee";
}
@Override
public double cost() {
return 0.89;
}
}
(3)创建调料抽象类,并继承饮料抽象类
/**
* 调料抽象类(装饰者抽象类)
*/
public abstract class CondimentDecorator extends Beverage {
@Override
public abstract String getDescription();
}
(4)创建具体的调料,并继承调料抽象类
/**
* 摩卡(装饰者)
*/
public class Mocha extends CondimentDecorator {
Beverage beverage;
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ", Mocha";
}
@Override
public double cost() {
// 加上摩卡的价格
return beverage.cost() + 0.20;
}
}
/**
* 豆浆(装饰者)
*/
public class Soy extends CondimentDecorator {
Beverage beverage;
public Soy(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ", Soy";
}
@Override
public double cost() {
// 加上豆浆的价格
return beverage.cost() + 0.15;
}
}
/**
* 奶泡(装饰者)
*/
public class Whip extends CondimentDecorator {
Beverage beverage;
public Whip(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ", Whip";
}
@Override
public double cost() {
// 加上奶泡的价格
return beverage.cost() + 0.10;
}
}
(5)测试
public class Test {
public static void main(String[] args) {
// 浓缩咖啡
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription() + " $" + beverage.cost());
// 综合咖啡
Beverage beverage2 = new HouseBlend();
System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
// 添加摩卡
beverage2 = new Mocha(beverage2);
// 添加豆浆
beverage2 = new Soy(beverage2);
// 添加奶泡
beverage = new Whip(beverage2);
System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
}
}
最新文章
- iOS---iOS10适配iOS当前所有系统的远程推送
- 在html中添加script脚本的方法和注意事项
- What does ";size"; in int(size) of MySQL mean?
- FineReport根据点击次数奇偶性排序之字符型
- php高版本不再使用mysql_connect()来连接数据库
- 使用UltraEdit实现从DOS文件到UNIX文件的批量转换
- [LintCode] Reverse Pairs 翻转对
- POJ 3281:Dining(最大流)
- 【转】Warning: mysql_connect(): mysqlnd cannot connect to MySQL 4.1+ using the old insecure authenticat
- Apache Avro 与 Thrift 比较
- css全局设置
- document.all使用
- python专题-异常处理(基础)
- PAT乙级-1043. 输出PATest(20)
- SSM-MyBatis-11:Mybatis中查询全部用resultmap
- 使用Dockerfile创建镜像
- 微信小程序:import导入公共文件方式
- SSM商城项目(九)
- post网络请求坑
- webVR全景图多种方案实现(pannellum,aframe,Krpano,three,jquery-vrview)
热门文章
- mitmdump+python的使用(代码篇)
- rust 宏
- 11.实战交付一套dubbo微服务到k8s集群(3)之dubbo微服务底包镜像制作
- html/css 滚动到元素位置,显示加载动画
- npm 更换镜像,解决cnpm仍然太慢的问题
- Unable to resolve dependency for ':app@debug/compileClasspath': Could not find any version that matc
- 手把手教你使用Python生成图灵智能小伙伴,实现工作助手/闲聊功能
- java基础——并发1
- 爬取B站弹幕并且制作词云
- spring cloud config 配置文件更新