装饰模式

1、装饰模式又名包装模式,是以对客户透明的方式扩展对象的功能,是继承关系的一个替代方案。

2、装饰模式结构:


抽象构件角色:给出一个抽象接口,以规范准备接收附加任务的对象。
具体构件角色:定义一个将要接收附加责任的类。
装饰角色:持有一个构件对象的实例,并定义一个与抽象构件接口一致的接口。
具体装饰角色:负责给构件对象附加上附加责任。

3、一个例子:

//抽象构件
public interface Component {

// 某个商业方法
void sampleOperation();

}

//具体构件
public class ConcreteComponent implements Component {

// 无参构造
public ConcreteComponent() {
super();
System.out.println("具体构件无参构造");
}

@Override
public void sampleOperation() {
System.out.println("具体构件商业方法");
}

}

//装饰角色
public class Decorator implements Component {
private Component component;

// 有参构造
public Decorator(Component component) {
this.component = component;
System.out.println("装饰有参构造");
}

// 无参构造
public Decorator() {
super();
System.out.println("装饰无参构造");
}

// 商业方法,委派给构件
@Override
public void sampleOperation() {
component.sampleOperation();
System.out.println("装饰商业方法");
}

}

//具体装饰1
public class ConcreteDecorator1 extends Decorator {

// 无参构造
public ConcreteDecorator1() {
System.out.println("具体装饰1无参构造");
}

public ConcreteDecorator1(Component component) {
super(component);
}

// 商业方法
@Override
public void sampleOperation() {

super.sampleOperation();
redecorator();
System.out.println("具体装饰1商业方法");
}

private void redecorator() {
System.out.println("具体装饰1包装一次");
}

}

//具体装饰2
public class ConcreteDecorator2 extends Decorator {

public ConcreteDecorator2(Component component) {
super(component);
}

// 商业方法
@Override
public void sampleOperation() {

super.sampleOperation();
redecorator();
System.out.println("具体装饰2商业方法");
}

private void redecorator() {
System.out.println("具体装饰2包装一次");
}

}

public class client {
public static void main(String[] args) {

ConcreteComponent component = new ConcreteComponent();

// 包装一次
ConcreteDecorator1 cd1 = new ConcreteDecorator1(component);
// 包装二次
ConcreteDecorator2 cd2 = new ConcreteDecorator2(cd1);

cd2.sampleOperation();

System.out.println();

}
}

在上面例子中cd2的内部结构:

结果打印:
具体构件无参构造
装饰有参构造
装饰有参构造
具体构件商业方法
装饰商业方法
具体装饰1包装一次
具体装饰1商业方法
装饰商业方法
具体装饰2包装一次
具体装饰2商业方法

4、使用场景

1、需要扩展一个类的功能,或者为一个类增加附加责任;
2、需要动态地给一个对象增加功能,这些功能可以再动态撤销;
3、需要增加由一些基本功能的排列组合而产生非常大量的功能,继承关系行不通。

5、一个猴王的例子:

//抽象构件,定义移动方法
public interface TheGreateSage {

public void move();
}

//具体构件
public class Monkey implements TheGreateSage {

@Override
public void move() {
System.out.println("猴子的移动方法!");
}

}

//装饰角色
public class Changes implements TheGreateSage {

// 委派
private TheGreateSage thegreatesage;

// 有参构造
public Changes(TheGreateSage thegreatesage) {
super();
this.thegreatesage = thegreatesage;
}

@Override
public void move() {
thegreatesage.move();
}
}

//具体装饰角色,鱼儿
public class Fish extends Changes {

public Fish(TheGreateSage thegreatesage) {
super(thegreatesage);
}

// 扩展功能
public void move() {
System.out.println("鱼儿在游泳!");
}

// 扩展功能
public void swim() {
System.out.println("鱼儿游泳方法!");
}
}

//具体装饰角色,鸟儿
public class Bird extends Changes {

public Bird(TheGreateSage thegreatesage) {
super(thegreatesage);
}

// 扩展功能
public void move() {
System.out.println("鸟儿在飞翔!");
}

// 扩展功能
public void fly() {
System.out.println("鸟儿飞翔方法!");
}
}

/*客户端角色,对于客户端而言,看到的依然是猴子的,客户端并不知道,进过装饰类的
的包装,猴子已经具有了鸟儿和鱼儿功能,所以对于客户端这种扩展是透明的。*/
public class client {
public static void main(String[] args) {
TheGreateSage sage = new Monkey();

// 第一次包装,猴王具有了鱼儿的功能
TheGreateSage fish = new Fish(sage);

fish.move();

// 第二次包装,猴王具有了鱼儿的功能以及鸟儿的功能
TheGreateSage bird = new Bird(fish);
// 另一种写法
TheGreateSage bird1 = new Bird(new Fish(sage));

bird.move();
bird1.move();

/*
* 在具体装饰类中,鸟儿和鱼儿都由自己的方法,因为在Component里面没有 这些方法,所以透明的包装是客户无法调用这些方法
* fish.swim();显然会报错。 纯粹的装饰模式很难找到,装饰模式的用意是不改变接口的前提下,增强
* 所考虑的类的性能,在增强性能时往往需要建立新的公开的方法。这导致大多数
* 的装饰模式是半透明的。换言之是装饰模式允许改变接口,增加新的方法。客户端可以声明 具体装饰类型的变量,从而可以调用具体装饰类才有的方法。
*/

Fish fish1 = new Fish(sage);// 半透明模式
fish1.swim();// 可以调到具体装饰类的独有方法。
Bird bird2 = new Bird(fish);
bird2.fly();

/*
* 只要客户端在不需要调用这些属于装饰的方法,而值调用Component的方法 装饰模式依然等同于透明的。
*/

}
}

6、装饰模式与适配器模式关系:
半透明的装饰模式是介于装饰模式和适配器模式之间的方法,适配器模式的用意是改变所考虑类的接口,也可以通过 改写一个或者几个方法,或者增加新的方法来增强或改变所考虑类的功能。
大多是的装饰模式实际是半透明的装饰模式,这样的模式也叫半装饰模式、半适配器模式。

7、一个打发票的例子(加深理解):

import java.text.NumberFormat;
import java.util.Date;
import java.util.Vector;

//抽象构件角色
public abstract class Order {

private OrderLine lnkOrderLine;
protected String customerName;// 客户名
protected Date salesDate;// 销售日期
protected Vector items = new Vector(10);// Vector聚集,存储任意多个OrderLine对象

//类似上面代码的商业方法
public void print() {
System.out.println("抽象构件打印");
for (int i = 0; i < items.size(); i++) {
OrderLine item = (OrderLine) items.get(i);
item.printLine();
}
}

public String getCustomerName() {
return customerName;
}

public void setCustomerName(String customerName) {
this.customerName = customerName;
}

public Date getSalesDate() {
return salesDate;
}

public void setSalesDate(Date salesDate) {
this.salesDate = salesDate;
}

// 增加一行销售产品
public void addItem(OrderLine item) {
items.add(item);
}

// 删除一行销售产品

public void remove(OrderLine item) {
items.remove(item);
}

// 得到总计金额
public double getGarntTotal() {
double amnt = 0.0D;
for (int i = 0; i < items.size(); i++) {
OrderLine item = (OrderLine) items.get(i);
amnt += item.getSubtotal();
}
return amnt;
}

// 金额格式化
private String formateCurrency(double amnt) {
return NumberFormat.getCurrencyInstance().format(amnt);
}
}

import java.text.NumberFormat;
//商品实体类
//发票的货品清单的一行,给出产品名,单价,购买数量,小计金额等。

public class OrderLine {
private String itemName;// 产品名
private int units;// 单位数量
private double unitPrice;// 单价

public String getItemName() {
return itemName;
}

public void setItemName(String itemName) {
this.itemName = itemName;
}

public int getUnits() {
return units;
}

public void setUnits(int units) {
this.units = units;
}

public double getUnitPrice() {
return unitPrice;
}

public void setUnitPrice(double unitPrice) {
this.unitPrice = unitPrice;
}

public void printLine() {
System.out.println(itemName + "\t" + units + "\t"
+ formateCurrency(unitPrice) + "\t"
+ formateCurrency(getSubtotal()));

}

// 金额格式化
private String formateCurrency(double amnt) {
return NumberFormat.getCurrencyInstance().format(amnt);
}

// 小计金额取值
public double getSubtotal() {
return unitPrice * units;
}
}

//具体构件类SalesOrder,发票的主部
public class SalesOrder extends Order {

// 无参构造方法
public SalesOrder() {
System.out.println("发票的主部无参构造方法");
}

public void print() {
System.out.println("发票的主部打印方法");
super.print();// 调用父类打印
}
}

//抽象装饰角色 OrderDecorator
public abstract class OrderDecorator extends Order {

// 依赖于被装饰的对象
protected Order order;

// 有参构造
public OrderDecorator(Order order) {
this.order = order;
this.setSalesDate(order.getSalesDate());
this.setCustomerName(order.getCustomerName());
}

public void print() {
System.out.println("抽象装饰打印");
super.print();
}
}

//抽象装饰角色 OrderDecorator
public abstract class OrderDecorator extends Order {

// 依赖于被装饰的对象
protected Order order;

// 有参构造
public OrderDecorator(Order order) {
this.order = order;
this.setSalesDate(order.getSalesDate());
this.setCustomerName(order.getCustomerName());
}

public void print() {
System.out.println("抽象装饰打印");
super.print();
}
}

//具体装饰角色,发票头部
public class HeaderDecorator1 extends OrderDecorator {

// 构造方法
public HeaderDecorator1(Order order) {
super(order);
}

public void print() {
this.printHeader();// 打印发票头部
super.order.print();// 调用被装饰对象的打印功能
}

//扩展功能
private void printHeader() {
System.out.println("\t**\t发票(另外一种头部)\t**");
System.out.println("销售公司");
System.out.println(order.getCustomerName());
System.out.println("===============");
System.out.println("产品名\t数量\t单价\t小计金额");
}
}

import java.text.NumberFormat;

//具体装饰角色,发票尾部
public class FooterDecorator extends OrderDecorator {

public FooterDecorator(Order order) {
super(order);
}

public void print() {
super.order.print();// 调用被装饰对象的打印功能
this.printFooter();// 打印发票尾部
}

private void printFooter() {
System.out.println("===============");

System.out.println("总额:\t\t\t\t"
+ formateCurrency(super.order.getGarntTotal()));// super.order.getGarntTotal()是取父类的父类的getGarntTotal()方法
}

// 金额格式化
private String formateCurrency(double amnt) {
return NumberFormat.getCurrencyInstance().format(amnt);
}
}

import java.util.Date;

/*客户端角色,*/
public class client {
private static Order order, order1 ,footerdecorator;

public static void main(String[] args) {

order = new SalesOrder();
order.setSalesDate(new Date());
order.setCustomerName("汽车店");
OrderLine line1 = new OrderLine();
line1.setItemName("四个轮胎");
line1.setUnitPrice(200);
line1.setUnits(4);
order.addItem(line1);

OrderLine line2 = new OrderLine();
line2.setItemName("车座");
line2.setUnitPrice(1000);
line2.setUnits(1);
order.addItem(line2);

footerdecorator =new FooterDecorator(order);

order = new HeaderDecorator(footerdecorator);// 经过2层包装

order.print();

System.out.println("=============第二种方式===============");
/*
* 关于order.print(); 
* 最先调用的是具体装饰角色,发票头部的打印方法,因为头部包装在最外面,然后调用头部的私有方法(扩展功能),
* 然后调用尾部的打印功能,在调用是又先调用了被装饰对象的打印功能,调用抽象构件的打印方法,调用尾部打印(扩展功能)。
*/

// 采用另外一种头部进行包装
order1 = new HeaderDecorator1(footerdecorator);// 经过2层包装

order1.print();

/*在该实例中,具体装饰类通过把私有的扩展方法放到抽象构件的方法中的方式实现了透明化
也即:
public void print() {
this.printHeader();// 打印发票头部
super.order.print();// 调用被装饰对象的打印功能
}
否则,如果要调用具体装饰类的私有方法,就要通过:
FooterDecorator footerdecorator =new FooterDecorator(order);
HeaderDecorator1 headerdecorator1 = new HeaderDecorator1(footerdecorator);
的方式实现包装,调用具体装饰的私有方法。*/
}
}

发票例子的内部结构:

输出打印:

发票的主部无参构造方法
** 发票 **
销售日期
Thu Jun 15 09:11:09 GMT+08:00 2017
===============
产品名 数量 单价 小计金额
发票的主部打印方法
抽象构件打印
四个轮胎 4 ¥200.00 ¥800.00
车座 1 ¥1,000.00 ¥1,000.00
===============
总额: ¥1,800.00
=============第二种方式===============
** 发票(另外一种头部) **
销售公司
汽车店
===============
产品名 数量 单价 小计金额
发票的主部打印方法
抽象构件打印
四个轮胎 4 ¥200.00 ¥800.00
车座 1 ¥1,000.00 ¥1,000.00
===============
总额: ¥1,800.00

通过此实例,相信对该模式有了进一步的认识。

每天努力一点,每天都在进步。

实例探索Java模式之路——装饰模式相关推荐

  1. java学习之路2--简单工厂模式实现饮料自动贩卖机

    java学习之路2 用简单工厂模式实现饮料自动贩卖机 功能简介 具体实现 1.简单工厂模式 2.代码 总结 用简单工厂模式实现饮料自动贩卖机) 功能简介 接收用户输入的信息,选择购买的饮料. 可供选择 ...

  2. 探索 Java 同步机制[Monitor Object 并发模式在 Java 同步机制中的实现]

    探索 Java 同步机制[Monitor Object 并发模式在 Java 同步机制中的实现] https://www.ibm.com/developerworks/cn/java/j-lo-syn ...

  3. 深入探索 Java 热部署

    转载自  深入探索 Java 热部署 简介 在 Java 开发领域,热部署一直是一个难以解决的问题,目前的 Java 虚拟机只能实现方法体的修改热部署,对于整个类的结构修改,仍然需要重启虚拟机,对类重 ...

  4. 结构型模式--装饰模式

    下面先用java,然后用Objective-C行对装饰模式的讲解: 对于java的装饰模式讲解和使用比较详细和难度有点偏高,而对于Objective-C的装饰模式讲解和使用方面比较简单,而且和java ...

  5. Java模式参考大全

    Java模式大全 一下模式都在本人博客中有对应的教程,自己搜索 概念 无论承接什么样的需求,是不是身边总有那么几个人代码写的烂,但是却时常有测试小姐姐过来聊天(求改bug).有产品小伙伴送吃的(求写需 ...

  6. php 工厂模式运用实例,php工厂模式的实例

    * 单例模式:用于创建单一类型的唯一实例对象 * 工厂模式:用于创建多种类型的多个实例对象 //声明形状类 class Shape { //声明静态方法create,根据容器形状不同,创建不同图形类的 ...

  7. JAVA设计模式 之九 装饰模式

    装饰模式(装饰设计模式)详解 在现实生活中,常常需要对现有产品增加新的功能或美化其外观,如房子装修.相片加相框等. 在软件开发过程中,有时想用一些现存的组件.这些组件可能只是完成了一些核心功能.但在不 ...

  8. JAVA8常量池监控_深入探索Java常量池

    Java的常量池通常分为两种:静态常量池和运行时常量池 静态常量池:class文件中的常量池,class文件中的常量池包括了字符串(数字)字面值,类和方法的信息,占用了class文件的大部分空间. 运 ...

  9. 我的Java开发之路

    最近有一位小伙伴通过公众号给我留言, "我参加工作没多久,看着圈里的技术大牛,特别羡慕,也渴望成为技术大牛,想让您分享一下从小白到大牛是怎样练成的,我该如何提高自己" 首先,谢谢这 ...

最新文章

  1. alert三秒后关闭_疏通经络后,感觉很疲倦是什么情况?
  2. (12) 需求征集 -- 序列管理、编号管理
  3. ceph cluster monitor
  4. ES6学习笔记 -- 尾调用优化
  5. Javaweb 网上订餐系统
  6. linux4755代表什么权限,CentOS下chmod 755和4755的区别是什么?
  7. 仰睇天路,俯促鸣弦。神仪妩媚,举止详妍
  8. mint-ui引用iconfont图标
  9. 9860计算机测量程序,卡西欧9860测量放样程序-绘星
  10. Ubuntu(linux)系统配置搭建代理服务器
  11. 如何将Caj转Word,免费CAJ转换的方法
  12. 用计算机管理硬盘分区,硬盘分区diskgenius工具使用方法,教你如何进行硬盘管理...
  13. 问题 2111: 连环阵
  14. 云通讯这局棋,声网、容联云怎么破?
  15. kzzi k980 三模键盘 说明书
  16. 在 FPGA 上快速构建 PID 算法
  17. linux下常用alias
  18. Office VBA开发经典-中级进阶卷(75元包邮)
  19. 《软件工程导论第6版》--张海藩 牟永敏 课后答案及其详解 第5章 总体设计
  20. ABP+AdminLTE+Bootstrap Table权限管理系统第五节--WBEAPI及SwaggerUI

热门文章

  1. Adobe Acrobat
  2. 酒吧里经典的英文歌曲专集(4CD)
  3. UDF(PYTHON / JAVA)入门级开发
  4. Spring配置数据源
  5. word文档docx解密wps,word文档docx权限密码多少?
  6. 网站WEB前端开发需要掌握什么技术
  7. [转]你有所不知的HTML發佈Flash的參數(一):allowScriptAccess
  8. anaconda虚拟环境中conda,pip快速换源/显示源/删除源
  9. sea.js的基本使用方法
  10. Spark Broadcast使用