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;流行的游戏编程库。它…

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

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

数据结构课上笔记6

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

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

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

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;几乎总有更…

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开始报…

链表相交问题

本来想自己写&#xff0c;写了一半发现一篇文章&#xff0c;解释写得简单易懂&#xff0c;我就直接拿过来了。 这个问题值得反复地写&#xff0c;锻炼链表coding能力的好题。 //如果两个链表都不带环 int NotCycleCheckCross(pLinkNode head1,pLinkNode head2) {pLinkNode lis…

双栈

利用栈底位置相对不变的特性&#xff0c;可以让两个顺序栈共享一个空间。 具体实现方法大概有两种&#xff1a; 一种是奇偶栈&#xff0c;就是所有下标为奇数的是一个栈&#xff0c;偶数是另一个栈。但是这样一个栈的最大存储就确定了&#xff0c;并没有起到互补空缺的作用&a…

单调队列优化的背包问题

对于背包问题&#xff0c;经典的背包九讲已经讲的很明白了&#xff0c;本来就不打算写这方面问题了。 但是吧。 我发现&#xff0c;那个最出名的九讲竟然没写队列优化的背包。。。。 那我必须写一下咯嘿嘿&#xff0c;这么好的思想。 我们回顾一下背包问题吧。 01背包问题 …

用Python去除扫描型PDF中的水印

内容概述 含水印扫描型PDF文件&#xff0c;其中某页如下图所示&#xff0c;用Python去除其页顶及页底的水印。 处理思路&#xff1a;PDF中的每一页的水印的相对位置基本相同&#xff0c;将PDF每一页输出成图片&#xff0c;然后进行图片编辑&#xff0c;用白色填充方形覆盖水印…

二阶有源滤波器

滤波器是一种使用信号通过而同时抑制无用频率信号的电子装置, 在信息处理、数据传送和抑制干扰等自动控制、通信及其它电子系统中应用广泛。滤波一般可分为有源滤波和无源滤波, 有源滤波可以使幅频特性比较陡峭, 而无源滤波设计简单易行, 但幅频特性不如滤波器, 而且体积较大。…

用JS写了一个30分钟倒计时器

效果图 额外功能 左键单击计时器数字区&#xff0c;不显示或显示秒钟区。左键双击计时器数字区&#xff0c;暂停或启动计时器。计时完毕&#xff0c;只能刷新页面启动计时器。输入框可输入备注信息&#xff0c;输入框失去焦点或计时完毕后&#xff0c;时间戳附带备注信息会存入…

为什么高手离不了Linux系统?我想这就是理由!

通过本文来记录下我在Linux系统的学习经历&#xff0c;聊聊我为什么离不了Linux系统&#xff0c;同时也为那些想要尝试Linux而又有所顾忌的用户答疑解惑&#xff0c;下面将为你介绍我所喜欢的Linux系统&#xff0c;这里有一些你应该知道并为之自豪的事实。 这里你应该首先抛开W…

用JS写一个电影《黑客帝国》显示屏黑底绿字雨风格的唐诗欣赏器

效果图 放码过来 <!DOCTYPE HTML> <html><head><meta http-equiv"Content-Type" content"text/html;charsetutf-8"/><title>Black Screen And Green Characters</title><style type"text/css">table…

元器件封装大全:图解+文字详述

先图解如下&#xff1a; 元器件封装类型&#xff1a; A.Axial  轴状的封装&#xff08;电阻的封装&#xff09;AGP &#xff08;Accelerate raphical Port&#xff09; 加速图形接口 AMR(Audio/MODEM Riser) 声音/调制解调器插卡BBGA&#xff08;Ball Grid Array&#xff09;…

用JS写一个丐版《2048》小游戏

效果图 放马过来 <!DOCTYPE HTML> <html><head><meta http-equiv"Content-Type" content"text/html;charsetutf-8"/><title>2048</title><style type"text/css">.basic{height:80px;width:80px;back…

如何有效申请TI的免费样片

转自如何有效申请TI的免费样片 TI公司愿意为支持中国大学的师生们的教学、实验、创新实践、竞赛和科研项目&#xff0c;提供有限数量的免费样片。首先需要指出的是&#xff1a;所有的样片申请应该是诚实正当的&#xff0c;所有不恰当的申请&#xff08;包括不必要或多余的&…