从吃鸡中论装饰器模式

在讲装饰器模式之前,我先讲讲代码实例,在讲具体的原理和结构。

情景:游戏中经常使用枪(英文:Gun),有手枪(英文:Pistol),狙击枪(英文:SniperRifle)等等。
然后枪有个基本功能,肯定是fire,也许shot更恰当,如果用代码实现,大致如下:

  • 抽象类:枪(Gun)
public abstract class Gun {
	//开火
	public abstract Fire fire();
}
  • 具体类:手枪(Pistol),继承于 Gun
public class Pistol extends Gun{

	@Override
	public Fire fire() {
		Fire fire = new Fire();
		fire.setRange(50); //射程50
		fire.setVoice(false); //开火有声音
		return fire;
	}
}
  • 具体类:狙击枪(SniperRifle),继承于 Gun
public class SniperRifle extends Gun{

	@Override
	public Fire fire() {
		Fire fire = new Fire();
		fire.setRange(400);//射程400
		fire.setVoice(false);//开火有声音
		return fire;
	}
}

枪可能有很多种,这里,就是举两个。

  • 开火属性类,表示开火的属性。
public class Fire {
	//射程
	private int range; 
	//开枪是否有声音
	private boolean voice; 
	
	@Override
	public String toString() {
		return "[射程:" + range + ","+((voice) ? "消音":"没有消音")+"]";
	}

	/***************************getter & setter*******************************/
	public int getRange() {return range;}
	public void setRange(int range) {this.range = range;}

	public boolean isVoice() {return voice;}
	public void setVoice(boolean voice) {this.voice = voice;}
	/***************************getter & setter*******************************/
}

在没有给枪装任何配件的情况下,这就是裸枪了。开火始终有声音,射程总是不变。
现在,我们用装饰器模式给枪装配件,我们设定如下:

  1. 如果装上消音器(英文:Silencer),可以消音
  2. 如果装上瞄准镜(英文:Collimation Mirror),可以增加射程。

我们代码实现如 :

  • 我们定义一个配件的父类,这个类可以不是抽象类,但是很明显,定义为abstract 更符合语义
/**
 * 配件父类
 */
public abstract class DecorateParts extends Gun{

	protected Gun gun;

	/**核心方法,这个方法很关键,是装饰器模式的核心 */
	@Override
	public Fire fire() {
		return (gun != null) ? gun.fire() : null;
	}

	/**安装配件*/
	public void install(Gun gun) {
		this.gun = gun;
	}
	
}
  • 具体配件类:消音器
/**
 * 消音器
 */
public class Silencer extends DecorateParts{
	
	@Override
	public Fire fire() {
		Fire fireResult = super.fire();
		fireResult.setVoice(true);//装上消音
		return fireResult;
	}
	
}
  • 具体配件类:瞄准镜
/**
 * 瞄准镜
 */
public class CollimationMirror extends DecorateParts{
	//增加的射程,这里设定增加100射程
	private int addRange = 100;
	
	@Override
	public Fire fire() {
		Fire fireResult = super.fire();
		//增加射程
		fireResult.setRange(fireResult.getRange() + addRange);
		return fireResult;
	}
	
}

至此,配件相关的类也写完了,接下来,测试一下:

public class DecorateMain {
	
	public static void main(String[] args) {
		//创建一把手枪
		Pistol gun = new Pistol();
		System.out.println("手枪-[裸枪]-" + gun.fire());
		
		//创建一个瞄准镜,裸枪上,安装瞄准镜
		CollimationMirror mirrorGun = new CollimationMirror();
		mirrorGun.install(gun);
		System.out.println("手枪-[瞄准镜]-" + mirrorGun.fire());
		
		//创建一个消音器,在安装了瞄准镜的基础上,再安装消音器
		Silencer silencerMirrorGun = new Silencer();
		silencerMirrorGun.install(mirrorGun);
		System.out.println("手枪-[瞄准镜,消音器]-" + silencerMirrorGun.fire());
		
		//创建一个消音器,裸枪上,安装消音器
		Silencer silencerGun = new Silencer();
		silencerGun.install(gun);
		System.out.println("手枪-[消音器]-" + silencerGun.fire());
	}
}

具体的运行结果如下:
这里写图片描述

在不影响枪的情况下,对枪进行装饰,主要还是看配件的3个类。
DecorateParts 本质上他不是配件,他继承了Gun,然后还有一个Gun的引用(protected Gun gun),
可以说这是装饰器模式关键。
我们从代码中可以知道,手枪,狙击枪是在Gun的基础上进行扩展,而消音器,瞄准器是在DecorateParts 上扩展。

/**
 * 消音器
 */
public class Silencer extends DecorateParts{
	
	@Override
	public Fire fire() {
		Fire fireResult = super.fire();
		fireResult.setVoice(true);//装上消音
		return fireResult;
	}
	
}

我们主要分析下消音器,使用装饰器模式,基本上第一个的方法的就是super.fire(),也就是调用父类的同名方法,
这也代理模式有些相似,但是并不相同。看如下代码,也是就是配件的装配过程:
Pistol gun = new Pistol();
mirrorGun.install(gun);
silencerMirrorGun.install(mirrorGun);

对于silencerMirrorGun;super.fire()其实就是mirrorGun.fire();
对于mirrorGun,super.fire()其实是gun.fire()
由此可见:silencerMirrorGun.fire()中第一步调用了mirrorGun.fire(),mirrorGun.fire()第一步调用了gun.fire()。
所以mirrorGun.fire()是在gun.fire()上装饰,而silencerMirrorGun.fire()在mirrorGun.fire()的基础上装饰。
这就是装饰模式的真正意义。而代理模式也是类似,不过代理一般不会改变方法的返回结果,装饰器模式一遍都是多层的,也就是说装配了很多东西,而代理模式一般也就只有一层而已。

最后来看看装饰器模式的类图结构,图片来自大话设计模式,如下:
这里写图片描述

与我上面的例子其实很容易对应,如下:
(Component – Gun)
(ConcreteComponent – Pistol)
(Decorator – DecorateParts)
(ConreteDecoratorA – Silencer)
(ConreteDecoratorB – CollimationMirror)

结语:当系统需要新的功能的时候,而且是像旧的类中添加新的代码。这些新的逻辑通常修饰了原有类的核心设计和职责。这时候可以考虑用装饰器模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值