设计模式应用实例三(装饰模式的应用)

前言
在结构型设计模式中,装饰模式相对于代理模式和适配器模式比较简单。装饰模式多数情况下也不应用于复杂的业务运算,只为对象增加一些属性

学习目标

1、识别代理模式,适配器模式与装饰模式的区别
2、明白装饰模式的原理,用法,使用场景。

概念

对象结构型模式。指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。
可以理解为设计者为应对表现的细微变化,并充分利用资源的一种模式,而其本体内容并无发生变化。例如:字体的变化黑体斜体渐变颜色字体 等等。
----在概念中,要特别注意“动态”的含义,它是指设计出不同的装饰类,再根据业务需要将不同的装饰类层层嵌套,可以得到各种行为组合变化的类,例如:黑斜体 。该模式在游戏程序设计中被广泛应用皮肤、攻击渲染、武器加成、回血加成等,在管理型业务系统中较少应用。
当然你可以根据需求实现真正的动态加载(可在运行时动态选择装饰类)。
例如:
业务1的组合可以是 A(B(C()))
业务2的组合可以是A(C())
缺点:如果嵌套层数过多,将影响系统性能,同时增加代码阅读难度

记忆关键点:

套娃,一个套一个,一层套一层。不同场合穿不同风格的衣服。
在这里插入图片描述

类结构图

完整的结构图

在这里插入图片描述

简化的结构图

在这里插入图片描述
完整的结构图有原TargetComponent接口,多个Target实现类,及抽象装饰类和具体的装饰类。
真实的应用中按照完整的结构来实现装饰模式是非常少的。一般使用简化版的装饰模式,简化版的则装饰类直接实现目标接口,可以实现多个装饰类。包装childrenComponent后可以层层嵌套,从而有不同的表现组合。

相似模式,区分及应用

代理:

区别:代理(静态代理)与装饰在结构上较难区分,代理类与目标类实现同一接口。装饰类与目标类也是实现同一接口。它们的区分在于业务逻辑的区分。
代理:数据在使用代理类与使用目标类后所期望的结果是一致的,所请求的参数是一致的。
代理类是为本系统健壮性,完整性,风险可控性(系统安全及日志追踪等)服务,服务目的不是客户端,是本系统。它对客户端来说是透明的,客户端不能感知它的存在。
装饰:设计者为应对业务的细微变化,将目标类进行包装,并向客户端展示这种变化。
参考:《设计模式应用实例二(代理模式的应用)》

适配器:

区别:适配器类是为应对业务利益最大化现有资源,从而为开发节省成本。可理解为协议的转换。客户端不能直接使用原协议接口,在客户端与原协议接口间,为适应客户端的调用设计一种新协议接口,该协议接口将请求参数按原协议接口的要求对请求参数进行处理,增删\计算变换等操作。这个新增的协议接口就是你所设计的适配器。适配器类与原协议接口实现类可能不是同一接口。
装饰:装饰类也是利益最大化资源,装饰不对原参数进行变换,装饰只对目标本体进行增强。

扩展联想

试想一下,装饰模式是否可以用在权限控制的数据控制层面,给不同的用户展示不同的数据,做到按权限配置展现不同的数据。

实例

前述

介绍Spring框架里几个使用装饰模式的地方。Spring框架里的装饰模式使用都没有复杂的结构和过多的业务运算(对bean的操作),有时甚至是为了解耦目标类而进行的简单包装。在spring中使用得也并不多。
装饰模式的使用还是在java的io包中使用得较多。
最后介绍本人在项目里使用装饰模式的一些情况。

spring中的用法

spring–BeanDefinitionDecorator
应用于标记的自定义属性,实现可以根据需要将自定义标签中的元数据转换为原始的spring的BeanDefinition,而无需从头编写标签生成BeanDefinition的程序。
spring中解释xml文档时对BeanDefinition类进行增强org.springframework.beans.factory.xml.BeanDefinitionDecorator
例如为了给bean标签添加属性beanAttrDecorator

<bean id="myDecoratorbean" class="com.example.MyExampleBean" beanAttrDecorator="this is my bean decorator"/>

按照spring的规范,自定义标签必须指定handlers与schemas,handlers文件指定xml标签解释的处理器,schemas指定xml标签的结构,即XSD文件(XSD是指XML结构定义,可以理解为XSD是xml文件标签内容的元数据)如下:
spring.handlers文件

http\://www.example.com/schema/patterndecorator=com.example.BeanAttrDecoratorNameSpaceHandler

BeanAttrDecoratorNameSpaceHandler的代码

public class BeanAttrDecoratorNameSpaceHandler extends NamespaceHandlerSupport {
  @Override
  public void init() {
    registerBeanDefinitionDecoratorForAttribute("beanAttrDecorator", 
      new BeanAttrDefinitionDecorator());
  }
}
BeanAttrDefinitionDecorator代码
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Attr;
import org.w3c.dom.Node;
public class BeanAttrDefinitionDecorator implements BeanDefinitionDecorator {
  @Override
  public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
    String desc = ((Attr) node).getValue();
    definition.getBeanDefinition().getPropertyValues().addPropertyValue("beanAttrDecorator", desc);//只对MyExampleBean起作用,MyExampleBean有属性beanAttrDecorator
    return definition;
  }
}

spring.schemas文件

http\://www.example.com/schema/patterndecorator.xsd=com/pattern/decorator/myexampledesc.xsd

myexampledesc.xsd内容如下

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.example.com/schema/patterndecorator"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  targetNamespace="www.example.com/schema/patterndecorator"
  elementFormDefault="qualified"
  attributeFormDefault="unqualified">
  
    <xsd:attribute name="beanAttrDecorator" type="xsd:string"/>

</xsd:schema>

apache beanutils的用法

在commons-beanutils-1.9.0中也有使用装饰模式,
org.apache.commons.beanutils.BaseDynaBeanMapDecorator
看名字是对目标Bean的装饰,使用Map接口来对Bean的Property进行处理,只要用于DynaBean接口的实现类中,如LazyDynaBean

java.io的应用

在java.io包中全部应用decorator模式,读者自行阅读源码。

真实案例用法

下面的代码是取自本人真实项目中的,用于导出文件中的一些特别处理,当然这种方案不是最优方案,只是说明装饰模式的用法

public class EmployeeExportDto extends EmployeeDto {
	private EmployeeDto inner;

	public EmployeeExportDto(EmployeeDto parent) {
		this.inner = parent;
	}
	@Override
	public String getApplyMethod() {
		if (inner.getApplyMethod().equals("1"))
			return "配发标准";
		if (inner.getApplyMethod().equals("2"))
			return "自选点数";
		return inner.getApplyMethod();
	}

	@Override
	public String getCreateTime(){
	    return  inner.getCreateTime()!=null? (new SimpleDateFormat("yyyy-MM-dd").format(inner.getCreateTime())):null;
    }
}

回顾

1、 重点关注装饰模式的动态增强功能的含义。
2、 重点关注代理,适配器,装饰三种模式的区别。
3、 介绍一些开源框架中的装饰模式应用。
4、 介绍了真实案例的装饰器使用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值