《Head First 设计模式》:装饰者模式
正文
一、定义
装饰者模式动态地将责任(功能)附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
要点:
- 装饰者和被装饰者有相同的超类型。
- 可以用一个或多个装饰者包装一个对象。
- 既然装饰者和被装饰者有相同的超类型,所以在任何需要原始对象(被装饰者)的场合,都可以用装饰过的对象代替它。
- 装饰者可以在被装饰者的行为之前与/或之后,加上自己的行为,甚至将被装饰者的行为整个取代掉,以到达特定的目的。
- 对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地用装饰者装饰对象。
- 装饰者会导致设计中出现许多小对象,如果过度使用,会让程序变得很复杂。
二、实现步骤
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());
}
}
《Head First 设计模式》:装饰者模式的更多相关文章
- Java设计模式——装饰者模式
JAVA 设计模式 装饰者模式 用途 装饰者模式 (Decorator) 动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator 模式相比生成子类更为灵活. 装饰者模式是一种结构式模式 ...
- JAVA设计模式--装饰器模式
装饰器模式 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰 ...
- 从源码角度理解Java设计模式——装饰者模式
一.饰器者模式介绍 装饰者模式定义:在不改变原有对象的基础上附加功能,相比生成子类更灵活. 适用场景:动态的给一个对象添加或者撤销功能. 优点:可以不改变原有对象的情况下动态扩展功能,可以使扩展的多个 ...
- 【设计模式】Java设计模式 - 装饰者模式
Java设计模式 - 装饰者模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 原创作品,更多关注我CSDN: 一个有梦有戏的人 准备将博客园.CSDN一起记录分享自 ...
- [Head First设计模式]山西面馆中的设计模式——装饰者模式
引言 在山西面馆吃鸡蛋面的时候突然想起装饰者这个模式,觉得面馆这个场景跟书中的星巴兹咖啡的场景很像,边吃边思考装饰者模式.这里也就依葫芦画瓢,换汤不换药的用装饰者模式来模拟一碗鸡蛋面是怎么出来的吧.吃 ...
- JAVA 设计模式 装饰者模式
用途 装饰者模式 (Decorator) 动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator 模式相比生成子类更为灵活. 装饰者模式是一种结构式模式. 结构
- 浅谈设计模式--装饰者模式(Decorator Pattern)
挖了设计模式这个坑,得继续填上.继续设计模式之路.这次讨论的模式,是 装饰者模式(Decorator Pattern) 装饰者模式,有时也叫包装者(Wrapper),主要用于静态或动态地为一个特定的对 ...
- javascript设计模式——装饰者模式
前面的话 在程序开发中,许多时候都并不希望某个类天生就非常庞大,一次性包含许多职责.那么可以使用装饰者模式.装饰者模式可以动态地给某个对象添加一些额外的职责,而不会影响从这个类中派生的其他对象.本文将 ...
- 设计模式-装饰者模式(Decorator Pattern)
本文由@呆代待殆原创,转载请注明出处. 此设计模式遵循的设计原则之一:类应该支持扩展,而拒绝修改(Open-Closed Principle) 装饰者模式简述 装饰者模式通过组合的方式扩展对象的特性, ...
- C#设计模式--装饰器模式
0.C#设计模式-简单工厂模式 1.C#设计模式--工厂方法模式 2.C#设计模式--抽象工厂模式 3.C#设计模式--单例模式 4.C#设计模式--建造者模式 5.C#设计模式--原型模式 6.C# ...
随机推荐
- 用头发换JS的第一天
因为平时也在上班,这些都是在地铁上抽空开始看的,总结来说还是有一些收获存在,每次看都能发现一些新东西,废话不多说,开撸! JavaScript是一种网页编程语言,可以对网页的行为进行编程,简单来说HT ...
- Quartz.Net系列(三):解读Quartz.Net源码领略设计模式在其中的应用
1.Builder(建造者)模式 JobBuilder DateBuilder 其他的Builder(TriggerBuilder.SchedulerBuilder等) 2.抽象工厂模式 ISch ...
- TensorFlow从0到1之TensorFlow超参数及其调整(24)
正如你目前所看到的,神经网络的性能非常依赖超参数.因此,了解这些参数如何影响网络变得至关重要. 常见的超参数是学习率.正则化器.正则化系数.隐藏层的维数.初始权重值,甚至选择什么样的优化器优化权重和偏 ...
- 【JMeter_13】JMeter逻辑控制器__执行时间控制器<Runtime Controller>
执行时间控制器<Runtime Controller> 业务逻辑: 根据输入的Runtime的值,对当前节点下的执行时长进行控制,当执行时长超过限定时长后,执行完当前正在执行的取样器后,跳 ...
- ca74a_c++__文件流对象的使用-用来读写文件ifstream
/*ca74a_c++__文件流对象的使用-用来读写文件将文件流对象绑定到文件上检查文件是否打开成功将文件流与新文件重新绑定清楚文件流的状态infile.close();//关闭流 infile.cl ...
- phpmyadmin通过慢查询日志getshell连载(二)
这是phpmyadmin系列渗透思路的第二篇文章,前面一篇文章阐述了通过全局日志getshell,但是还有一个日志可以getshell,那就是本次实验的慢查询日志,操作类似,毕竟实战中多一条路就多一次 ...
- 一个线上问题的思考:Eureka注册中心集群如何实现客户端请求负载及故障转移?
前言 先抛一个问题给我聪明的读者,如果你们使用微服务SpringCloud-Netflix进行业务开发,那么线上注册中心肯定也是用了集群部署,问题来了: 你了解Eureka注册中心集群如何实现客户端请 ...
- 3、尚硅谷_SSM高级整合_使用ajax操作实现删除的功能
点击删除的时候,要删除联系人,这里同点击编辑按钮一样给删除按钮添加点击事件的时候不能使用 $(".delete_btn").click(function(){ }); 这种方式,因 ...
- 阿里巴巴--mysql中Mysql模糊查询like效率,以及更高效的写法
在使用msyql进行模糊查询的时候,很自然的会用到like语句,通常情况下,在数据量小的时候,不容易看出查询的效率,但在数据量达到百万级,千万级的时候,查询的效率就很容易显现出来.这个时候查询的效率就 ...
- python变量拷贝
写python代码时候,如:A = 0,B = A,B = 1, 有时候会发现A变成了1,那么怎么办呢? 以下是伪代码: import copy ... X_ = copy.copy(X) ... 这 ...