Java设计模式(3 / 23):装饰者模式

文章目录

    • 定义
    • 案例1:三点几啦
      • 首次尝试
      • 再次尝试
      • 设计原则:类应该对扩展开放,对修改关闭
      • 尝用装饰者模式
      • 装饰者模式特征
      • 本例的类图
      • 放码过来
        • 饮料类
          • HouseBlend
          • DarkRoast
          • Espresso
          • Decaf
          • 调料装饰类
            • Milk
            • Mocha
            • Soy
            • Whip
        • 运行测试类
    • 案例2:编写自己的Java I/0装饰者
      • JDK中的IO流概述
        • 放码过来
          • LowerCaseInputStream
          • 创建一测试文件D:\\test.txt
          • 运行测试类
    • 参考资料

定义

装饰者(Decorator)模式动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案

案例1:三点几啦

更新咖啡连锁店的订单系统,原先类的设计:

咖啡店为拓展业务,允许顾客在饮料上添加各种调料,如:

  1. 蒸奶 Steamed Milk
  2. 豆浆 Soy
  3. 摩卡(巧克力风味) Mocha
  4. 覆盖奶泡

加入的调料收取不同的费用。

首次尝试

类数量爆炸

这违背严重两条设计原则

  1. 多用组合,少用继承。
  2. 为了交互对象之间的松耦合设计而努力。

再次尝试

利用实例变量和继承,追踪这些调料:

这次尝试的局限性:

  1. 调料价钱的改变会使我们更改现有代码。
  2. 一旦出现新的调料,我们就需要加上新的方法,并改变超类中的cost()方法。
  3. 以后可能会开发出新饮料。对这些饮料而言(例如:冰茶),某些调料可能并不适合,但是在这个设计方式中,Tea(茶)子类仍将继承那些不适合的方法,例如:hasWhip()(加奶泡)。
  4. 万一顾客想要双倍摩卡咖啡,怎么办?

设计原则:类应该对扩展开放,对修改关闭

我们的目标是允许类容易扩展,在不修改现有代码的情况下,就可搭配新的行为。

如能实现这样的目标,这样的设计具有弹性可以应对改变,可以接受新的功能来应对改变的需求。

虽然似乎有点矛盾,但是的确有一些技术可以允许在不直接修改代码的情况下对其进行扩展。

在选择需要被扩展的代码部分时要小心。每个地方都采用开放-关闭原则,是一种浪费,也没必要,还会导致代码变得复杂且难以理解。

(MyNote:随机应变)

尝用装饰者模式

我们要以饮料为主体,然后在运行时以调料来“装饰”(decorate)饮料。比方说,如果顾客想要摩卡
和奶泡深焙咖啡,那么,要做的是:

1.拿一个深焙咖啡(DarkRoast)对象

2.以摩卡(Mocha)对象装饰它

3.以奶泡(Whip)对象装饰它

4.调用cost()方法,并依赖委托(delegate)将调料的价
钱加上去。

装饰者模式特征

  • 装饰者和被装饰对象有相同的超类型。

  • 你可以用一个或多个装饰者包装一个对象。

  • 既然装饰者和被装饰对象有相同的超类型,所以在任何需要原始对象(被包装的)的场合,可以用装饰过的对象代替它。

  • 装饰者可以在所委托被装饰者的行为之前与/或之后,加上自己的行为,以达到特定的目的。

  • 对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地用你喜欢的装饰者来装饰对象。

本例的类图

放码过来

饮料类

public abstract class Beverage {String description = "Unknown Beverage";public String getDescription() {return description;}public abstract double cost();
}
HouseBlend
public class HouseBlend extends Beverage {public HouseBlend() {description = "House Blend Coffee";}public double cost() {return .89;}
}
DarkRoast
public class DarkRoast extends Beverage {public DarkRoast() {description = "Dark Roast Coffee";}public double cost() {return .99;}
}
Espresso
public class Espresso extends Beverage {public Espresso() {description = "Espresso";}public double cost() {return 1.99;}
}
Decaf
public class Decaf extends Beverage {public Decaf() {description = "Decaf Coffee";}public double cost() {return 1.05;}
}
调料装饰类
public abstract class CondimentDecorator extends Beverage {Beverage beverage;public abstract String getDescription();
}
Milk
public class Milk extends CondimentDecorator {public Milk(Beverage beverage) {this.beverage = beverage;}public String getDescription() {return beverage.getDescription() + ", Milk";}public double cost() {return .10 + beverage.cost();}
}
Mocha
public class Mocha extends CondimentDecorator {public Mocha(Beverage beverage) {this.beverage = beverage;}public String getDescription() {return beverage.getDescription() + ", Mocha";}public double cost() {return .20 + beverage.cost();}
}
Soy
public class Soy extends CondimentDecorator {public Soy(Beverage beverage) {this.beverage = beverage;}public String getDescription() {return beverage.getDescription() + ", Soy";}public double cost() {return .15 + beverage.cost();}
}
Whip
public class Whip extends CondimentDecorator {public Whip(Beverage beverage) {this.beverage = beverage;}public String getDescription() {return beverage.getDescription() + ", Whip";}public double cost() {return .10 + beverage.cost();}
}

运行测试类

public class StarbuzzCoffee {public static void main(String args[]) {Beverage beverage = new Espresso();System.out.println(beverage.getDescription() + " $" + beverage.cost());System.out.println("---");Beverage beverage2 = new DarkRoast();beverage2 = new Mocha(beverage2);beverage2 = new Mocha(beverage2);beverage2 = new Whip(beverage2);System.out.println(beverage2.getDescription() + " $" + beverage2.cost());System.out.println("---");Beverage beverage3 = new HouseBlend();beverage3 = new Soy(beverage3);beverage3 = new Mocha(beverage3);beverage3 = new Whip(beverage3);System.out.println(beverage3.getDescription() + " $" + beverage3.cost());}
}

运行结果:

Espresso $1.99
---
Dark Roast Coffee, Mocha, Mocha, Whip $1.49
---
House Blend Coffee, Soy, Mocha, Whip $1.34

案例2:编写自己的Java I/0装饰者

JDK中的IO流概述

java.io包内的类中许多类都是装饰者。

下面是一个典型的对象集合,用装饰者来将功能结合起来,
以读取文件数据:

BufferedInputStream及LineNumberInputStream都扩展自FilterInputStream,而FilterInputStream是一个抽象的装饰类。

java.io其实没有多大的差异。我们把java.io API范围缩小,让你容易查看它的文件,并组合各种“输入”流装饰者来符合你的用途。

你会发现输出流(OutputStream)的设计方式也是一样的。你可能还会发现Reader/Writer流(作为基于字符数据的输入输出)和输入流/输出流的类相当类似(虽然有一些小差异和不一致之处,但是相当雷同,所以你应该可以了解这些类)。

但是Java I/O也引出装饰者模式的一个缺点:利用装饰者模式,常常造成设计中有大量的小类,数量实在太多,可能会造成使用此API程序员的困扰。

但是,现在你已经了解了装饰者的工作原理,以后当使用别人的大量装饰的API时,就可以很容易地辨别出他们的装饰者类是如何组织的,以方便用包装方式取得想要的行为。

放码过来

这个想法怎么样:编写一个装饰者,把输入流内的所有大写字符转成小写。

举例:当读取“I know the Decorator Pattern thereforeI RULE!”,装饰者会将它转成“i know thedecorator pattern therefore i rule ! ”

LowerCaseInputStream

扩展FilterInputStream,这是所有InputStream的抽象装饰者:

import java.io.*;public class LowerCaseInputStream extends FilterInputStream {public LowerCaseInputStream(InputStream in) {super(in);}public int read() throws IOException {int c = in.read();return (c == -1 ? c : Character.toLowerCase((char)c));}public int read(byte[] b, int offset, int len) throws IOException {int result = in.read(b, offset, len);for (int i = offset; i < offset+result; i++) {b[i] = (byte)Character.toLowerCase((char)b[i]);}return result;}
}
创建一测试文件D:\test.txt
I know the Decorator Pattern therefore I RULE!
运行测试类
import java.io.*;public class InputTest {public static void main(String[] args) throws IOException {int c;InputStream in = null;try {in = new LowerCaseInputStream( new BufferedInputStream(new FileInputStream("D:\\text.txt")));while((c = in.read()) >= 0) {System.out.print((char)c);}} catch (IOException e) {e.printStackTrace();} finally {if (in != null) { in.close(); }}System.out.println();System.out.println("---");//try (InputStream in2 = new LowerCaseInputStream(new BufferedInputStream(new FileInputStream("D:\\text.txt")))) {while((c = in2.read()) >= 0) {System.out.print((char)c);}} catch (IOException e) {e.printStackTrace();}}
}

运行结果:

i know the decorator pattern therefore i rule!
---
i know the decorator pattern therefore i rule!

参考资料

  1. 《Head First 设计模式》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/445597.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c语言知识体系

原文&#xff1a;https://blog.csdn.net/lf_2016/article/details/80126296#comments

《游戏编程入门 4th》笔记(1 / 14):Windows初步

文章目录Windows编程概述获取Windows理解Windows消息机制多任务多线程事件处理DirectX快速概览Direct3D是什么Window程序基础创建第一个Win32项目理解WinMainWinMain函数调用完整的WinMainGetMessage函数调用寻求帮助Windows编程概述 DirectX&#xff0c;流行的游戏编程库。它…

17校招真题题集(1)1-5

注&#xff1a;本系列题目全是按照通过率降序来排列的&#xff0c;基本保证题目难度递增。 1、 题目名称&#xff1a;游戏任务标记 来源&#xff1a;腾讯 题目描述 游戏里面有很多各式各样的任务&#xff0c;其中有一种任务玩家只能做一次&#xff0c;这类任务一共有1024个…

《游戏编程入门 4th》笔记(2 / 14):监听Windows消息

文章目录编写一个Windows程序理解InitInstanceInitInstance函数调用InitInstance的结构理解MyRegisterClassMyRegisterClass函数调用MyRegisterClass的作用揭露WinProc的秘密WinProc函数调用WinProc的大秘密什么是游戏循环The Old WinMain对持续性的需要实时终止器WinMain和循环…

17校招真题题集(2)6-10

注&#xff1a;本系列题目全是按照通过率降序来排列的&#xff0c;基本保证题目难度递增。 6、 题目名称&#xff1a;Fibonacci数列 来源&#xff1a;网易 题目描述 Fibonacci数列是这样定义的&#xff1a; F[0] 0 F[1] 1 for each i ≥ 2: F[i] F[i-1] F[i-2] 因此&am…

QT5的数据库

#include <QtSql> QT sql QSqlDatabase类实现了数据库连接的操作 QSqlQuery类执行SQL语句 QSqlRecord类封装数据库所有记录 QSqlDatabase类 [cpp] view plaincopy print?QSqlDatabase db QSqlDatabase::addDatabase("QOCI"); db.setHostName("localh…

数据结构课上笔记6

本节课介绍了单链表的操作实现细节&#xff0c;介绍了静态链表。 链表带头的作用&#xff1a;对链表进行操作时&#xff0c;可以对空表、非空表的情况以及 对首元结点进行统一处理&#xff0c;编程更方便。 下面给出带头的单链表实现思路&#xff1a; 按下标查找&#xff1a; …

《Unity2018入门与实战》笔记(9 / 9):个人总结

个人总结 脚本语言学习的窍门 尽可能多读、多写、多说脚本语言&#xff01; Link 游戏制作步骤 设计游戏时一般会遵循5个步骤&#xff1a; 罗列出画面上所有的对象。确定游戏对象运行需要哪些控制器脚本。确定自动生成游戏对象需要哪些生成器脚本。准备好用于更新UI的调度…

17校招真题题集(3)11-15

注&#xff1a;本系列题目全是按照通过率降序来排列的&#xff0c;基本保证题目难度递增。 11、 题目名称&#xff1a;买苹果 来源&#xff1a;网易 题目描述 小易去附近的商店买苹果&#xff0c;奸诈的商贩使用了捆绑交易&#xff0c;只提供6个每袋和8个每袋的包装(包装不…

Qt学习:QDomDocument

QDomDocument类代表了一个XML文件 QDomDocument类代表整个的XML文件。概念上讲&#xff1a;它是文档树的根节点&#xff0c;并提供了文档数据的基本访问方法。由于元素、文本节点、注释、指令执行等等不可能脱离一个文档的上下文&#xff0c;所以文档类也包含了需要用来创建这些…

《事实:用数据思考,避免情绪化决策》笔记

文章目录一分为二负面思维直线思维恐惧本能规模错觉以偏概全命中注定单一视角归咎他人情急生乱一分为二 要做到实事求是&#xff0c; 就要做到当你听到一分为二的说法时&#xff0c; 你就能迅速认识到这种说法描述的是一种两极分化的图画&#xff0c; 而两极之间存在一道巨大的…

顺序存储线性表实现

在计算机中用一组地址连续的存储单元依次存储线性表的各个数据元素,称作线性表的顺序存储结构。 顺序存储结构的主要优点是节省存储空间&#xff0c;因为分配给数据的存储单元全用存放结点的数据&#xff08;不考虑c/c语言中数组需指定大小的情况&#xff09;&#xff0c;结点之…

QT5生成.exe文件时,出现缺少QT5core.dll文件解决方法

在 http://qt-project.org/downloads 下载Qt SDK安装需要Qt版本。在QtCreator下&#xff0c;程序可以正常运行&#xff0c;但是当关闭QtCreator后&#xff0c;在DeBug目录下再运行相应的*.exe程序时&#xff0c;会提示缺少Qt5Core.dll错误。解决方法&#xff1a;添加电脑环境变…

《基于Java实现的遗传算法》笔记(7 / 7):个人总结

文章目录为何采用遗传算法哪些问题适合用遗传算法解决遗传算法基本术语一般遗传算法的过程基本遗传算法的伪代码为何采用遗传算法 遗传算法是机器学习的子集。在实践中&#xff0c;遗传算法通常不是用来解决单一的、特定问题的最好算法。对任何一个问题&#xff0c;几乎总有更…

单链表不带头标准c语言实现

链表是一种物理存储单元上非连续、非顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点&#xff08;链表中每一个元素称为结点&#xff09;组成&#xff0c;结点可以在运行时动态生成。每个结点包括两个部分&#xff1a;一个是…

Java设计模式(4 / 23):单例模式

文章目录单例模式的应用场景饿汉式单例模式懒汉式单例模式改进&#xff1a;synchronized改进&#xff1a;双重检查锁改进&#xff1a;静态内部类破坏单例用反射破坏单例用序列化破坏单例解密注册式单例模式枚举式单例模式解密容器式单例线程单例实现ThreadLocal单例模式小结参考…

约瑟夫环-(数组、循环链表、数学)

约瑟夫环&#xff08;约瑟夫问题&#xff09;是一个数学的应用问题&#xff1a;已知n个人&#xff08;以编号1&#xff0c;2&#xff0c;3...n分别表示&#xff09;围坐在一张圆桌周围。从编号为k的人开始报数&#xff0c;数到m的那个人出列&#xff1b;他的下一个人又从1开始报…

Ubuntu麒麟下搭建FTP服务

一.怎么搭建FTP服务&#xff1a; 第一步>>更新库 linuxidclinuxidc:~$ sudo apt-get update 第二步>>采用如下命令安装VSFTPD的包 linuxidclinuxidc:~$ sudo apt-get install vsftpd 第三步>>安装完成后打开 /etc/vsftpd.conf 文件&#xff0c;按如下所述…

《数据结构上机实验(C语言实现)》笔记(1 / 12):绪论

文章目录验证性实验求1~n的连续整数和说明放码结果常见算法时间函数的增长趋势分析说明放码结果设计性实验求素数个数说明放码结果求连续整数阶乘的和说明放码结果验证性实验 求1~n的连续整数和 说明 对于给定的正整数n&#xff0c;求12…n12…n12…n&#xff0c;采用逐个累…

线性表实现一元多项式操作

数组存放&#xff1a; 不需要记录幂&#xff0c;下标就是。 比如1&#xff0c;2&#xff0c;3&#xff0c;5表示12x3x^25x^3 有了思路&#xff0c;我们很容易定义结构 typedef struct node{float * coef;//系数数组int maxSize;//最大容量int order;//最高阶数 }Polynomial…