装饰模式

一、装饰模式概述

  装饰模式动态地给一个对象增加一些额外的职责,它可以灵活地给对象增加新的功能,而且增加新功能时使用关联关系而不是继承关系,从而可降低系统的耦合度。需要装饰的具体构件类和具体装饰类可以独立的变化,从而可以动态的增加或删除对象的职责。通常我们可以通过继承机制或关联机制实现给类或对象增加新功能。但是继承是静态的,用户不能控制 增加功能的方式和时机。关联关系通过将一个类的对象嵌入另一个对象中,由另一个对象来决定是否调用嵌入对象的功能并扩展自己的行为,这个嵌入的对象通过称为装饰器。为了使得装饰器和它所装饰的对象对客户端来说透明,装饰器类和被装饰的类必须实现相同的接口,客户端使用时无须关心一个类的对象是否被装饰过,从而可以一致地操作使用未被装饰的对象以及被装饰的对象。

  当需要给已存在的类增加一些特定情况下才会执行的功能时,装饰模式就是一种很好的选择,我们可以把要添加的功能放在单独的一个类中,并在这个类中包含需要被它装饰的对象,这样客户端就可以根据需要,有选择地使用这些新功能了。

  下面是一个典型的装饰模式结构图


从上图可以看到,装饰模式主要包含如下角色:

1、 Component(抽象构件):它定义了对象的接口,可以给这些对象动态增加职责,它声明

了在具体构件中实现的业务方法,客户端通过它可以一致的方式处理未被装饰的对象以及被装饰后的对象。

2、 ConcreteComponent(具体构件):它定义了具体的构件对象,实现了在抽象构件中

声明的方法。

3、 Decorator(抽象装饰类):它是抽象构件类的子类,用于给具体构件增加职责,具体

职责在其子类中实现。该类中有一个指向抽象构件对象的引用,可通过该引用调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。

4、 ConcreteDecorator(具体装饰类):它负责向构件添加新的职责,在这里可以定义一

些新的方法以实现新的功能。

 

二、装饰模式案例

、现在假设我们要创建一些游戏角色,它们可以穿戴不同的衣服和饰物,我们可以用装饰模式来实现这个功能,这样我们就可以复用系统中的衣服和饰物对象,降低系统内存的占用率和复杂度,提高系统的灵活度,使角色可以按需要改变外观。下图是这个实现的粗略类图(这个例子只是个非常简单化的例子)


下面是这个实现的代码(我多加了一个子类)

#include <string>
#include <iostream>
using namespace std;

//游戏角色类 
class GameRole
{
private:
	string roleName;
	
public:
	GameRole(string strName)
	{
		roleName = strName;
	}

	GameRole(){}
	
	virtual void show()
	{
		cout << "The decorated role is:" << roleName << endl;
	}
};

//装饰类
class Decorator : public GameRole
{
protected:
	GameRole *gameRole;
	
public:
	void decorate(GameRole *tRole) // tRole 被丢进来的时候,它就会被 Decorator 包住(被Decorator 修饰)
	{
		gameRole = tRole;
	}
	virtual void show()
	{
 		gameRole->show();
	}
};

//裙子 
class Skirt : public Decorator
{
public:
	virtual void show()
	{
		cout << "I have a Shirts" << endl;
		gameRole->show();
	}
};

//太阳帽 
class SunHat : public Decorator
{
public:
	virtual void show()
	{
		cout << "I have a SunHat" << endl;
		gameRole->show();
	}
};

//
class Sword : public Decorator
{
public:
	virtual void show()
	{
		cout << "I have a Sword" << endl;
		gameRole->show();
	}
};

//客户端
int main()
{
	
	GameRole *gameRole = new GameRole("Lion"); //创建一个初始对象 	
	Skirt *skirt = new Skirt(); //创建一个skirt(裙子)对象	 
	SunHat *sunHat = new SunHat(); //创建一个sunHat(太阳帽)对象	
Sword *sword = new Sword();
	
	skirt->decorate(gameRole); //用户skirt对象装饰初始对象gameRole 
	sunHat->decorate(skirt); // 用户sunHat对象装饰已被skirt对象 
Sword->decorate(sunHat);
	sword->show(); //显示最终对象 
	return 0;
}

编译后运行,结果如下

lion@lo:~/storm/programpratise/decorator$ ./decor 
I have a Sword
I have a SunHat
I have a Shirts
The decorated role:Lion 

相信对多数人来说这个程序非常简单,但是main()函数的最后几行代码看起来可能有点别扭。可以这样理解:skirt->decorate(gameRole); 表示用skirt对象装饰gameRole对象(即给gameRole穿上裙子),这实际上是用skirt对象把gameRole对象包装起来。sunHat->decorate(skirt); 可以理解成给已经穿上裙子的gameRole戴上帽子,因为gameRole刚刚被skirt包装起来了,所以再次包装时应该包装的是skirt对象,而不是原生的gameRole对象。即先给 gameRole 穿上裙子,再给它带上帽子,最后再佩一把锐利的剑。

  最后当然是由最外一层对象来调用显示方法了,所以有sunHat ->show(); 这一写法。从这个程序可以看到装饰模式有种通过对原始对象进行层层包装,以实现装饰的目的。

 

2、假设我们要设计一个智能的游戏机控制系统,可以让一个机器人按需要变成不同的形状,如变成可以装货物的卡车、可以载人的汽车等。同样的,我们也可以用装饰模式来实现这一需求。下边是这个需求的实现类图

其实现代码如下:

1、抽象构件接口

public interface Transform {
	public void move();
}


2、具体构件类

public class Robot implements Transform{
	private String name;
	public Robot(String name)
	{		
		this.name = name;
	}
	
	public void move()
	{
		System.out.print("Hello, my name is " + this.name + ",");
		System.out.println(" I can move independently");
	}
}

3、抽象装饰类

public class Decorator implements Transform{
	private Transform transform;
	
	public Decorator(Transform transform)
	{
		this.transform = transform;
	}
	public void move()
	{
		transform.move();
	}
}

4、具体装饰类

public class Truck extends Decorator{
	public Truck(Transform transform)
	{
		super(transform);
		System.out.println("Now i turned into a truck");
	}
	public void loadingGoods()
	{
		System.out.println("I can loading goods");
	}
}

5、具体装饰类
public class Car extends Decorator{
	public Car(Transform transform)
	{
		super(transform);
		System.out.println("Now i turned into a car");		
	}
	public void takePeople()
	{
		System.out.println("I can take people");
	}
}

6、测试类
public class Test {
	public static void main(String argv[])
	{
		Transform robot = new Robot("Lion");
		robot.move();
		System.out.println("\n------ transform_1 ------\n");
		Truck truck = new Truck(robot);
		truck.move();
		truck.loadingGoods();
		System.out.println("\n------ transform_2 ------\n");
		Car car = new Car(robot);
		car.move();
		car.takePeople();
	}
}

三、 装饰模式的优缺点及应用

1、优点:装饰模式能够灵活地扩展对象的功能,且可以通过配置文件在运行时决定使用的装饰器类型,从而实现多态;具体构件类与具体装饰类可以独立的按需要修改变化,且增加具体装饰类和构件类很方便很灵活。

2、缺点:用装饰模式进行系统设计时会产生很多小对象,这会增加系统的复杂度,而且容易出错。

  JDK中的Java IO就使用到了装饰模式,在JDK中,InputStreamOutputStream两个类分别用于处理输入和输出,但是它们都只提供了最简单的流处理方法,只能读入和写出字符,没有缓冲处理,没法处理文件。而InputStream的直接子类FileInputStream用于以流的形式从文件中读取数据,ByteArrayInputStream用于从内存字节数组中读取数据…这些类的设计就使用了装饰模式…




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Storm-Shadow

你的鼓励将是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值