OOP中decorator 设计模式 2021-07-05

decorator 设计模式

在OOP的学习中,有种软件设计模式是 decorator 设计模式,而且在之后的实验中用到了。当时感觉不是很好理解,后来通过查阅资料有了一点感悟,将其记录如下。
decorator 设计模式能够向一个现有的对象添加修饰的设计和新的功能,同时又可以不改变对象原本的结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

decorator 模式的结构与实现

  1. 模式的结构
    装饰器模式结构主要包含以下部分。
    抽象构件:定义一个抽象接口以规范准备接收附加责任的对象。
    具体构件:实现抽象构件,通过装饰角色为其添加一些职责。
    抽象装饰:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
    具体装饰:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
    在这里插入图片描述
    如上图所示,最顶层的是一个抽象构件Component,是一个规范准备接收附加责任对象的抽象接口。而ConcreteComponent是实现上面抽象接口的部件,实现了我们的基本功能。Decorator就是抽像装饰,它相当于继承了Component,包含具体构件的实例,内部完全调用了父类进行实现,并没有增加装饰。因为实现抽象装饰的相关方法要在Decorator的子类ConcreteDecotator中实现。

下面通过实验中一个实例具体讲解。
实验要完成一个 mutable 的 ADT,描述了一组在时间轴上分布的“时间段”(interval),每个时间段附着一个特定的标签,且标签不重复。
例如:下图的时间段集合可表示为{ A=[0,5], B=[10,30], C=[30,35] }。在该例子中,标签是简单的字符串(“A”、“B”、“C”),但实际应用中标签可以是任何 immutable 类型的 ADT,也就是 IntervalSet中的 L。
在这里插入图片描述

这是抽象接口的代码相当于抽象构件(为了方便说明,以下代码有很多简化)

public interface IntervalSet<L> {
	  
	public static <L> IntervalSet<L> empty() {
		return new CommonIntervalSet<L>();
		
	}
	/**
	 * 插入标签和时间段
	 */
	public void insert(long start, long end, L label);
	/**
	 * 
	 * @return 标签集
	 */
	public Set<L> labels();
	/**
	 * 对对应标签进行删除
	 * @param 标签
	 * @return 成功删除返回true,否则返回false
	 */
	
	public boolean remove(L label);
	/**
	 * 
	 * @param 标签
	 * @return 对应标签开始时间
	 */
	
	public long start (L label);
	/**
	 * 
	 * @param 标签
	 * @return 对应标签结束时间
	 */
	
	public long end (L label);
}

抽象构件由具体构件来实现。

public class CommonIntervalSet<L> implements IntervalSet<L> {
	private final Map<L, Time> itmap = new HashMap<>();

	/**
	 * 
	 */
	public CommonIntervalSet() {
		// TODO 自动生成的构造函数存根
	}

	/**
	 * 插入标签和时间段
	 */

	@Override
	public void insert(long start, long end, L label) {
		if (start < 0 || start > end) {
			return;
		}

		if (itmap.containsKey(label)) {
			System.out.println("已存在");
		} else {

			Time time = new Time(start, end);
			itmap.put(label, time);
		}

	}

	@Override
	public Set<L> labels() {
		return itmap.keySet();
	}

	@Override
	public boolean remove(L label) {
		// TODO 自动生成的方法存根
		if (itmap.containsKey(label)) {
			itmap.remove(label);
			return true;
		}
		return false;
	}

	@Override
	public long start(L label) {
		if (itmap.containsKey(label)) {
			return itmap.get(label).getstart();
		}
		return -1;
	}

	@Override
	public long end(L label) {
		if (itmap.containsKey(label)) {
			return itmap.get(label).getend();
		}
		return -1;
	}
}

}

但是这个具体构件是允许时间上有重叠的,像实现时间上无重叠就不行了。这时候想不改变原来的实现,就要用到装饰器。
定义一个抽象装饰继承抽象构件,完全用抽象构件的实现方法。

public abstract class IntervalSetDecorator<L> implements IntervalSet<L> {

	protected final IntervalSet<L> intervalSet;
	
	public IntervalSetDecorator(IntervalSet<L> in) {
		this.intervalSet=in;
	}
	/**
	 * 插入标签和时间段
	 */
	@Override
	public void insert(long start, long end, L label)  {
		intervalSet.insert(start, end, label);
		
	}

	@Override
	public Set<L> labels() {
		return intervalSet.labels();
	}

	@Override
	public boolean remove(L label) {
		return intervalSet.remove(label);
	}

	@Override
	public long start(L label) {
		return intervalSet.start(label);
	}

	@Override
	public long end(L label) {
		return intervalSet.end(label);
	}

定义具体装饰继承抽象装饰来实现我们的新增功能。
可以看到,通过一个HashMap在insert中增加了判断时间是否有覆盖的功能,如果覆盖就进行提示,不再插入。删除时记HashMAp中对应的标签也要删除,否则再添加时可能会发生时间冲突的错误。

public class NonOverlapIntervalSet<L> extends IntervalSetDecorator<L> implements IntervalSet<L> {
	private final Map<L, Time> itMap = new HashMap<>();

	public NonOverlapIntervalSet(IntervalSet<L> in) {
		super(in);
		// TODO 自动生成的构造函数存根
	}

	/**
	 * 插入标签和时间段
	 */
	@Override
	public void insert(long start, long end, L label) {
		for (L lab : itMap.keySet()) {
			Time time = itMap.get(lab);
			if (start >= time.getend() || end <= time.getstart())
				continue;
			else {
				System.out.println("时间冲突");
				System.out.println(lab.toString() + time.toString());
				System.out.println(label.toString());
				return;
			}
		}
		super.insert(start, end, label);
		itMap.put(label, new Time(start, end));
	}

	@Override
	public boolean remove(L label) {
		// TODO 自动生成的方法存根
		if (itMap.containsKey(label)) {
			super.remove(label);
			itMap.remove(label);
			return true;
		}
		return false;
	}

}

这样装饰器模式的结构就实现了。

具体用法

            IntervalSet<L> duty = new CommonIntervalSet<>();
			duty = new NonOverlapIntervalSet<>(duty);
		   duty = new BlankIntervalSet<>(start, end, duty);
		

使用方法就是先用抽象构件定义ADT用具体构件实现IntervalSet duty = new CommonIntervalSet<>();此时duty实现的就是允许时间重叠。加上修饰duty = new NonOverlapIntervalSet<>(duty);,此时duty就变成不允许时间重叠了。如果还有其他修饰也可继续添加,如上所示。

装饰器优缺点

1.优点
装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用
通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果。

2.缺点
装饰器模式会增加许多子类,过度使用会增加程序得复杂性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值