设计模式 2

您所在的位置:网站首页 delicious蛋糕的价格 设计模式 2

设计模式 2

2024-06-22 04:08| 来源: 网络整理| 查看: 265

装饰者模式: 动态的给对象添加一些额外的属性或行为。相比于使用继承,装饰者模式更加灵活。

UML图:

装饰者.png

 

一般来说装饰者模式有下面几个参与者:

Component:装饰者和被装饰者共同的父类,是一个接口或者抽象类,用来定义基本行为ConcreteComponent:定义具体对象,即被装饰者Decorator:抽象装饰者,继承自Component,从外类来扩展ConcreteComponent。对于ConcreteComponent来说,不需要知道Decorator的存在,Decorator是一个接口或抽象类ConcreteDecorator:具体装饰者,用于扩展ConcreteComponent

注:装饰者和被装饰者对象有相同的超类型,因为装饰者和被装饰者必须是一样的类型,这里利用继承是为了达到类型匹配,而不是利用继承获得行为。

利用继承设计子类,只能在编译时静态决定,并且所有子类都会继承相同的行为;利用组合的做法扩展对象,就可以在运行时动态的进行扩展。装饰者模式遵循开放-关闭原则:类应该对扩展开放,对修改关闭。利用装饰者,我们可以实现新的装饰者增加新的行为而不用修改现有代码,而如果单纯依赖继承,每当需要新行为时,还得修改现有的代码。

实例:

假设一家甜品店,出售蛋糕,除了蛋糕外,还可以在蛋糕上布置水果,蜡烛等,但是水果和蜡烛需要额外收费,假设一个蛋糕的价格是66元,水果和蜡烛分别需要额外付10元,那么怎么样来动态的计算价格呢?例子所贴代码已上传Github:装饰者模式。 首先,定义组件类,也是装饰者和被装饰者的超类Sweet .java:

public abstract class Sweet { String description = "Sweet"; public String getDescription() { return description; } public abstract double cost(); }

定义被装饰者蛋糕类,Cake .java:

public class Cake extends Sweet { @Override public String getDescription() { return "一个蛋糕"; } @Override public double cost() { return 66; } }

定义抽象装饰者类Decorator.java:

public abstract class Decorator extends Sweet { public abstract String getDescription(); }

定义具体装饰者水果类,FruitDecorator.java:

public class FruitDecorator extends Decorator { Sweet sweet; public FruitDecorator(Sweet sweet) { this.sweet = sweet; } @Override public String getDescription() { return sweet.getDescription() + ",水果"; } @Override public double cost() { return sweet.cost() + 10; } }

定义具体装饰者蜡烛类,CandleDecorator.java:

public class CandleDecorator extends Decorator { Sweet sweet; public CandleDecorator(Sweet sweet) { this.sweet = sweet; } @Override public String getDescription() { return sweet.getDescription() + ",蜡烛"; } @Override public double cost() { return sweet.cost() + 10; } }

最后根据不同的选择来结算价格:

public static void main(String[] args) { Cake cake = new Cake(); System.out.println(cake.getDescription() + "总共花费" + cake.cost()); FruitDecorator fruitDecorator = new FruitDecorator(cake); System.out.println(fruitDecorator.getDescription() + "总共花费" + fruitDecorator.cost()); CandleDecorator candleDecorator = new CandleDecorator(fruitDecorator); System.out.println(candleDecorator.getDescription() + "总共花费" + candleDecorator.cost()); }

完整代码地址已上传Github:装饰者模式。 执行结果:

一个蛋糕,总共花费66.0 一个蛋糕,水果,总共花费76.0 一个蛋糕,水果,一根蜡烛,总共花费86.0

可见,装饰者模式可以非常灵活地动态地给被装饰者添加新行为,它的缺点也显现出来了,那就是必须管理好更多的对象,但是,装饰者模式可以和工厂模式或生成器这样的模式一块使用来避免这个问题。

PS:我们使用java.IO类时类似于下面的这种写法:

new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream( "io.txt" )));

java.IO类用到的也是装饰者模式!是不是一下清楚了好多~

总结: 1、装饰者和被装饰者对象有相同的超类型,所以在任何需要原始对象(被装饰者)的场合,都可以用装饰过得对象代替原始对象。 2、可以用一个或多个装饰者包装一个对象(被装饰者) 3、装饰者可以在所委托的装饰者行为之前或之后加上自己的行为,以达到特定的目的 4、被装饰者可以在任何时候被装饰,所以可以在运行时动态地、不限量地用你喜欢的装饰者来装饰对象。 5、装饰者会导致出现很多小对象,如果过度使用,会让程序变得复杂。

 

装饰模式在Java I/O库中的应用

  java.io包内的类太多了,简直是……“排山倒海”。你第一次(还有第二次和第三次)看到这些API发出“哇”的惊叹时,放心,你不是唯一受到惊吓的人。现在,你已经知道装饰者模式,这些I/O的相关类对你来说应该更有意义了,因为其中许多类都是装饰者。下面是一个典型的对象集合,用装饰者来将功能结合起来,以读取文件数据:

 

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

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

  但是JavaAI/O也引出装饰者模式的一个“缺点”:利用装饰者模式,常常造成设计中有大量的小类,数量实在太多,可能会造成使用此API程序员的困扰。但是,现在你已经了解了装饰者的工作原理,以后当使用别人的大量装饰的API时,就可以很容易地辨别出他们的装饰者类是如何组织的,以方便用包装方式取得想要的行为。

 

编写自己的Java I/O装饰者,将大写字符转换为小写字符:

package com.xingle_test.designpattern; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; /** * 编写一个装饰者把所有的输入流内的大写字符转化成小写字符: * @ClassName: LowerCaseInputStream * * @author Xingle * @date 2014-7-29 下午3:19:04 */ public class LowerCaseInputStream extends FilterInputStream{ /** * @param in */ protected LowerCaseInputStream(InputStream in) { super(in); // TODO Auto-generated constructor stub } /** * 针对字节 */ public int read() throws IOException{ int c = super.read(); return (c == -1 ? c : Character.toLowerCase((char) c)); } /** * 针对字节数组 */ public int read(byte[] b,int offset,int len)throws IOException{ int result = super.read(b, offset, len); for(int i=0;i= 0) { System.out.print((char) c); } in.close(); } catch (IOException e) { e.printStackTrace(); } } }

 

D:\test\1.txt 原本的文件内容为:Test LowerCaseInputStream HELLO

执行结果:

test lowercaseinputstream hello

 

转载地址:

https://www.jianshu.com/p/c26b9b4a9d9e

https://www.cnblogs.com/xingele0917/p/3870307.html



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3