OO之装饰者模式
以下为装饰者模式详解:
引子:
假如有一个快餐店,基本种类分为米饭,水饺,粉面等,但每一种类型的快餐又可以搭配不同的料,如米饭可以点各种不同的菜(排骨,青菜,土豆等),如果按照一般的设计,快餐为基类,加不同的料的搭配为一个类,分别继承到基类,那么N种搭配就有N个类,这是类的爆炸。
要解决这个问题我们就可以用装饰者模式了,首先来了解一个OO设计原则:类对扩展的开放,对修改的关闭。这并不矛盾,像上面的类爆炸子类的行为(继承)是在编译时就静态决定了的,且所有子类都会继承到相同的行为,要解决问题就要做到动态的扩展,在类进行扩展时不修改现有的代码而添加新的行为,这样就要动态的组合对象,弹性的应对改变,可以使引进Bug的可能性大幅减少。
注意:使用这种原则,组合对象并不是百利而无一害的,这需要花费更多的时间来设计且通常会引入新的抽象层,使代码变得复杂,所以不能过多地方使用,只要在最有可能改变的地方使用。
下面我们用装饰者模式来设计这个快餐店,以快餐以主体,用菜码来“装饰”快餐。比如顾客要来一份排骨套餐,那么做法如下:
1:先来一个米饭对象。
2:用青菜来装饰它。
3:用排骨来装饰它。
4:最后调用排骨对象的cost()方法来计费(用委托把菜的价钱加上去)。
如下图:
这样的话米饭和装饰它的青菜,排骨都要有共同的基类,且基类中有一个抽象的cost()方法用来计费,这样每一层都必须复写cost()方法。青菜对象是一个装饰者,它的类型也反映(继承自同一个基类)了它所装饰的对象(米饭),当需要计算价格时调用最外层的cost()外层的又调用内层的算出价格再加上自己的价格(逐层委托)。
理解如下:
1:装饰者和被装饰者对象有相同的基类。
2:可以用一个或多个装饰者包装一个对象。
3:在任何需要原始对象(被包装者)的场合,可以用装饰过的对象来代替它(继承自同一父类)。
4:装饰者可以在委托被装饰者行为之前或之后,加上自己的行为,以达到目的。
5:对象可以在任何时候,动态地,不限量的用你喜欢的装饰者来装饰。
下面为装饰者模式的基本类图:
为什么用继承呢?上面说过装饰者和被装饰者必须是同一类型,这里运行继承不是要过得“行为 ”,而是要达到“类型的匹配”。当需要加入新的行为时,用不同的装饰者进行组合,混合着用,不需要改变已有代码(如果依赖继承就得修改之前的代码)。
下面开始设计快餐店的代码:
/**
* 抽象组件类
* @author Homg
*
*/
public abstract class FastFood {
String description = "Unknown Food"; //描述方法,已经实现,因为组件是可以单独使用的
public String getDescription() {
return description;
} //抽象的计费方法,由具体组件去实现
public abstract double cost();
}
/**
* 继承自组件基类(快餐)的具体组件(水饺)
*
* @author Homg
*
*/
public class BoiledDumpling extends FastFood {
// 描述自己,变量是继承自组件(快餐)基类的
public BoiledDumpling() {
description = "水饺";
} // 计算自己的价格,没有加料的
@Override
public double cost() {
return 8.5;
} }
/**
* 继承自组件基类(快餐)的具体组件(米饭)
*
* @author Homg
*
*/
public class Rice extends FastFood {
// 描述自己,变量是继承自组件基类的
public Rice() {
description = "米饭";
} // 计算自己的价格,没有加料的
@Override
public double cost() {
return 5.5;
} }
/**
* 继承自组件的抽象装饰者
*
* @author Homg
*
*/
public abstract class CondimentDecorator extends FastFood {
// 抽象的描述方法,因为下面的装饰者都要完整的描述出组件和所加的装饰,所以它们必须重新实现。
public abstract String getDescription();
}
/**
* 具体装饰者(香菜)
* @author Homg
*
*/
public class Caraway extends CondimentDecorator {
// 用一个实例变量记录组件(快餐)
private FastFood fastFood; // 传入组件
public Caraway(FastFood fastFood) {
this.fastFood = fastFood;
} // 通过组件的调用来获得加料后的完整描述
@Override
public String getDescription() {
return fastFood.getDescription() + ", 香菜";
} // 先计算被装饰者的价格再加上自己的价格
@Override
public double cost() {
return fastFood.cost() + 1.5;
} }
/**
* 具体装饰者(排骨)
*
* @author Homg
*
*/
public class PorkRibs extends CondimentDecorator { // 用一个实例变量记录组件(快餐)
private FastFood fastFood; // 传入组件
public PorkRibs(FastFood fastFood) {
this.fastFood = fastFood;
} // 通过组件的调用来获得加料后的完整描述
@Override
public String getDescription() {
return fastFood.getDescription() + ", 排骨";
} // 先计算被装饰者的价格再加上自己的价格
@Override
public double cost() {
return fastFood.cost() + 12.5;
} }
装饰什么就把什么传进去:
public static void main(String[] args) {
/*------------1部分------------*/
//创建一个水饺对象,不加一点料
// FastFood fastFood = new BoiledDumpling();
// System.out.println(fastFood.getDescription() + ",¥" + fastFood.cost());
/*------------2部分------------*/
// //给水饺加上香菜和排骨
// fastFood = new Caraway(fastFood);
// fastFood = new PorkRibs(fastFood);
// System.out.println(fastFood.getDescription() + ",¥" + fastFood.cost());
/*------------3部分------------*/
//创建一个米饭对象,给米饭加上香菜和排骨
FastFood fastFood2 = new Rice();
fastFood2 = new Caraway(fastFood2);
fastFood2 = new PorkRibs(fastFood2);
System.out.println(fastFood2.getDescription() + ",¥" + fastFood2.cost()); }
上面main函数中逐个运行3个部分,注释掉另外两个部分。
运行结果如下:
1部分:
水饺,¥8.5
2部分:
水饺, 香菜, 排骨,¥22.5
3部分:
米饭, 香菜, 排骨,¥19.5
快餐店的类图:
应用:
Java的i/o,直接与文件接触的组件InputStream,装饰它的装饰者有BufferedInputStream等。
总结:
装饰者模式是动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
完整代码下载地址(也可以留下邮箱发给你):
http://download.csdn.net/detail/homg92/6862759
OO之装饰者模式的更多相关文章
- [Head First设计模式]山西面馆中的设计模式——装饰者模式
引言 在山西面馆吃鸡蛋面的时候突然想起装饰者这个模式,觉得面馆这个场景跟书中的星巴兹咖啡的场景很像,边吃边思考装饰者模式.这里也就依葫芦画瓢,换汤不换药的用装饰者模式来模拟一碗鸡蛋面是怎么出来的吧.吃 ...
- Head First设计模式之装饰者模式(Decorator Pattern)
前言: 本节将深度讨论继承滥用问题,将会学到使用对象组合的方式,在运行时装饰类,在不修改任何底层代码的情况下,给对象赋予新的职责. 1. 基本需求:咖啡连锁店业务扩张需要重新设计订单系统 背景: ...
- Java IO 装饰者模式
装饰模式(Decorator) 装饰模式又名包装(Wrapper)模式. 装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案. 装饰模式通过创建一个包装对象,也就是装饰,来包裹真实的 ...
- 【java设计模式】(5)---装饰者模式(案例解析)
设计模式之装饰者模式 一.概念 1.什么是装饰者模式 装饰模式是在不使用继承和不改变原类文件的情况下,动态的扩展一个对象的功能.它是通过创建一个包装对象,也就是装饰来包裹真实的对象. 这一个解释 ...
- java模式—装饰者模式
装饰者模式 1.意图: 动态地给一个对象添加一些额外的职责.就增加功能来说, Decorator模式相比生成子类更为灵活.该模式以对客 户端透明的方式扩展对象的功能. 2.适用环境 (1)在不影响其他 ...
- 面向对象程序设计(OOP设计模式)-结构型模式之装饰器模式的应用与实现
课程名称:程序设计方法学 实验4:OOP设计模式-结构型模式的应用与实现 时间:2015年11月18日星期三,第3.4节 地点:理1#208 一.实验目的 加深对结构型设计模式的理解以及在开发中的实际 ...
- Java设计模式の装饰者模式
目录 一.问题引入 二.设计原则 三.用装饰者模式解决问题 四.装饰者模式的特点 五.装饰者模式的定义 六.装饰者模式的实现 七.java.io包内的装饰者模式 一.问题引入 咖啡店的类设计: 一个饮 ...
- Java文件与io——装饰者模式
意图: 动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator模式相比于生成子类更为灵活.该模式以对客户端透明的方式扩展对象的功能. 适用环境 在不影响其他对象的情况下,以动态.透明的 ...
- 《Head First 设计模式》学习笔记——观察者模式 + 装饰者模式
装饰者模式是JDK中还有一个使用较多的设计模式,上一个是观察者模式(在Swing中大量使用),业内好的API设计无一离不开常见的设计模式,通常我们所说要阅读源代码,也是为了学习大牛们的设计思路.--- ...
随机推荐
- Centos搭建nginx环境,编译,添加服务,开机启动。
首先安装所需的安装库,yum -y install gcc gcc-c++ autoconf libtool* openssl openssl-devel 编译的时候,若有提示错误,提示缺少某个库,y ...
- 踩到两只“bug”
近期在修复ex和头儿的代码时,碰到两个特别点的bug,其实也不能称之为bug,非常简单的用法,稍不严谨点可能就出错了. 第一个是in_array,大家都知道功能是检查一个值是否在数组中,第三个参数传入 ...
- Lombok(1.14.8) - @SneakyThrows
@SneakyThrows @SneakyThrows,声明异常. package com.huey.lombok; import java.io.UnsupportedEncodingExcepti ...
- Networking - ICMP 协议
发送到远程计算机的数据通常发经过一个或多个路由器,这些路由器在把数据传输到最终目的地的过程中可能发生多种问题.路由器利用 ICMP 把问题通知给源 IP.ICMP 还有用于其他调试和排错的功能. IC ...
- Android Studio 创建aar包与引用
两者区别:*.jar: 只包含了class文件与清单文件 ,不包含资源文件,如图片等所有res中的文件.*.aar: 包含所有资源 ,class以及res资源文件全部包含 一.创建aar包1.创建一个 ...
- JavaScript之图片轮换
<!doctype html> <title>javascript图片轮换</title> <meta charset="utf-8"/& ...
- java createSQLQuery().list()返回日期格式没有时分秒的解决方法
方法一 将Oracel数据库对应表中“收单时间的字段”receive_sheet_time,由原来的Date类型改为timestamp 然后,在java程序中,由 (java.util.timesta ...
- 简单快速的伪Fractional Cascading
Fractional Cascading算法是用于将零散的多个数组(亦可理解成比较高维的空间)中的数据的二分查找速度增加,用的是空间换时间的方法.然而这种方法并不是很好懂,而且中文文献很少.在这里介绍 ...
- JavaScript代码检查工具 — JSHint
静态代码检查是开发工作中不可缺少的一环,毕竟对于程序化的工作人的眼睛是不可靠的,更何况是自己的眼睛看自己的代码.即使最后的运行结果通过,但可能存在一些未定义的变量.定义了但最后没用过的变量.分号有没有 ...
- Linux小白最佳实践:《超容易的Linux系统管理入门书》(连载五)Linux系统的对话方式
本篇是Linux小白最佳实践第5篇,目的就是让白菜们了解Linux进程之间是如何对话的.之前连载的几篇,在微信上引起了很多的反响,有人也反映图多文字少,感觉没有干货.本篇选了大部分是实战讲解的&quo ...