【设计模式】装饰模式

博主声明:

转载请在开头附加本文链接及作者信息,并标记为转载。本文由博主 威威喵 原创,请多支持与指教。

本文首发于此   博主:威威喵  |  博客主页:https://blog.csdn.net/smile_running

    装饰模式是设计模式中属于常用的一种之一,装饰模式也称作包装模式,顾名思义,我们可以想象为衣着装扮、包装盒之类的具体事物,那么它包装、装扮的具体内容就是变相拥有了这个东西,比如我们穿上服饰、还有给蛋糕添加包装盒、给礼物定制一个精致的小盒子,这些行为都是属于对某一个东西进行装饰,通过装饰之后,使得被装饰的东西拥有了曾经没有的属性、能力等。这就是装饰模式的一大功能体现。

    装饰模式这被这样定义的:装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

    动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。因为除了继承的方式,让类的功能变得强大以外,装饰模式也可以使这个类变得强大,同时它并不是通过继承的方式,这就体现了它的灵活和可扩展性较高。

    下面我们通过一个简单的小例子,来学习一下如何使用装饰模式来提高代码的灵活度。就以我们每天都穿衣打扮为例吧,运用装饰模式来写这个例子,通过代码的对比,才能真正的体现装饰模式的益处。

    首先,我们的主角 coder 每天都去上班,996的工作充实了它的日常生活时间,与至于他没有太多的时间去关心衣着打扮,而且每天几乎对着代码,又不是对着小姐姐,在意什么呢。所以呢,每天起来看看头发,穿上衣服、裤子和鞋子就出门了,代码如下:

package com.xww.dp.decorator;

public class Coder {

	public Coder(String coder) {
		System.out.println(coder);
	}

	public void hair(String appearance) {
		System.out.println("头发:"+appearance);
	}

	public void clothes(String appearance) {
		System.out.println("衣服:"+appearance);
	}

	public void trousers(String appearance) {
		System.out.println("裤子:"+appearance);
	}

	public void shoes(String appearance) {
		System.out.println("鞋子:"+appearance);
	}

}

客户端代码:

package com.xww.dp.decorator;

/**
 * 装饰模式(包装模式)
 * 
 * @author xww
 * @博客:https://blog.csdn.net/smile_Running/column/info/27169
 */
public class DecoratorPatternClient {

	public static void main(String[] args) {

		Coder coder = new Coder("程序员_007");

		coder.hair("乌黑稀疏");
		coder.clothes("格子衫");
		coder.trousers("裤衩");
		coder.shoes("人字拖");
	}
}

那么,他每天上班都是这样的:

【设计模式】装饰模式_第1张图片

    很快的,996的生活到了星期日这一天。星期日呢,突然部门说搞娱乐活动,要打扮帅气一点来吸引小女生,那么当天早上,coder 就早早起床了,洗了个头发,穿上了 帅气的西装和皮鞋。

摇身一变,coder 也非常靓:

【设计模式】装饰模式_第2张图片

    后来呢,他发现自己新买的手表还没有戴上,刚刚好,这次可以展现帅气的一面,怎么可以少了手表呢。对了,西装还少了领带,显得比较单调,于是又找出了一条领带。那代码怎么写呢,哦对了,写一个继承 coder 的帅气 boy 类吧,如下:

package com.xww.dp.decorator;

public class Boy extends Coder{

	public Boy(String coder) {
		super(coder);
	}

	public void watch(String appearance) {
		System.out.println("手表:"+appearance);
	}

	public void tie(String appearance) {
		System.out.println("领带:"+appearance);
	}
}

客户端:

package com.xww.dp.decorator;

/**
 * 装饰模式(包装模式)
 * 
 * @author xww
 * @博客:https://blog.csdn.net/smile_Running/column/info/27169
 */
public class DecoratorPatternClient {

	public static void main(String[] args) {

		Boy coder = new Boy("帅气的_程序员_007");

		coder.hair("帅气发型");
		coder.clothes("西装");
		coder.trousers("西裤");
		coder.shoes("皮鞋");
		coder.watch("劳力士");
		coder.tie("白色领结");
	}
}

然后呢,他摇身一变的去了部门参加晚会了。

【设计模式】装饰模式_第3张图片

   在晚会上,帅气的 coder 变得与往常不太一样,简直判若两人,获得小姐姐芳心,通过装饰可以彻底的改变一个人。同样的,通过装饰模式,也可以改变一个类。我们上面使用的就是一般的继承写法,虽然没有什么大的问题,但是使用装饰模式可以更加的灵活,不信我们试试看。

运用装饰模式

   在此要新建一个 component 接口,表示 coder 的一系列行为,如整理发型,穿衣等。就是把 Coder 类中方法提到接口中,然后Coder 类实现这个接口,接口代码如下:

package com.xww.dp.decorator;

/**
 * 装饰模式接口,定义基本的操作
 * 
 * @author xww
 * 
 * @博客:https://blog.csdn.net/smile_running?t=1
 *
 */
public interface CoderComponent {

	public void hair(String appearance);

	public void clothes(String appearance);

	public void trousers(String appearance);

	public void shoes(String appearance);

}

    然后,Coder 类必须实现此接口,里面的代码不需要更改,代码还是上面的那样,就不贴出来了。要修改的是 Boy 类,因为 Boy 类装饰了 Coder 类,所以它更牛皮、更帅气。Boy 类要拥有同样的操作,就必须实现此接口,代码如下:

package com.xww.dp.decorator;

public class Boy implements CoderComponent {

	public CoderComponent coder;

	public Boy() {

	}
        //进行装饰
	public void decorator(CoderComponent coder) {
		this.coder = coder;
	}

	public void watch(String appearance) {
		System.out.println("手表:" + appearance);
	}

	public void tie(String appearance) {
		System.out.println("领带:" + appearance);
	}

	@Override
	public void hair(String appearance) {
		if (coder != null)
			coder.hair(appearance);
	}

	@Override
	public void clothes(String appearance) {
		if (coder != null)
			coder.clothes(appearance);
	}

	@Override
	public void trousers(String appearance) {
		if (coder != null)
			coder.clothes(appearance);
	}

	@Override
	public void shoes(String appearance) {
		if (coder != null)
			coder.shoes(appearance);
	}
}

    实现接口,必须实现里面的抽象方法。这里有所不同,因为要对 Coder 类进行包装,所以要新建一个方法,把 CoderComponent  接口作为参数传进来,这样拿到接口的实现类,就可以调用接口里面的方法了。代码没什么难度,接下看看客户端的调用方式:

package com.xww.dp.decorator;

/**
 * 装饰模式(包装模式)
 * 
 * @author xww
 * @博客:https://blog.csdn.net/smile_Running/column/info/27169
 */
public class DecoratorPatternClient {

	public static void main(String[] args) {

		Coder coder = new Coder("帅气的_程序员_007");
		Boy boy = new Boy();
		boy.decorator(coder);

		boy.hair("帅气发型");
		boy.clothes("西装");
		boy.trousers("西裤");
		boy.shoes("皮鞋");

		boy.watch("劳力士");
		boy.tie("白色领结");

	}
}

   看上面代码,coder 本来没有 watch 和 tie 方法的,通过 Boy 类的包装就变得更加强大,直接多了两个方法。这也就是装饰模式的运用,装饰模式对比继承的特点就是扩展性比较好,你可以想象一下这样的场景,如果我们的 boy 类功能越来越多的话,是不是就要继承 boy 类写一个 boy1、boy2,因为要根据一个原则,最好不要修改原有类代码的基础上进行添加新功能,而装饰模式就可以很方便的解决这个问题,只要实现一下通用的操作方法,然后进行扩展即可。

 源码里的装饰模式

    如何才能说明这个装饰模式的强大之处呢,莫不过看源代码更能说明其魅力了。在 Java 中,流的操作是我们最熟悉不过的了,整个流家族的设计,其实都用了这种装饰模式,我们看看调用流的代码就一目了然了。

        try {
            Reader reader = new FileReader("xx.txt");
            BufferedReader bufferedReader = new BufferedReader(reader);
            
            ((BufferedReader) bufferedReader).readLine();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

    BufferedReader 包装了 Reader 类,是其多了一个 readLine() 方法,而且可以调用 Reader 类的方法,这就是典型的装饰模式的使用,通过阅读源码,我们更能体会这种设计模式的好处。

你可能感兴趣的:(趣味设计模式)