装饰者模式
定义
装饰者(Decorator)模式提供了一种方法,使得在不改变原有类的基础上可以动态的扩展一个对象的功能。即动态的将指着附加到对象上。
装饰者模式的特点
1、 装饰对象和被装饰对象实现了相同的接口。客户端可以不加区分的使用具体是装饰者实现还是被装饰者实现的接口中的方法。
2、装饰对象有一个被装饰对象的实例。这样客户端就可以把被装饰对象的实例委托给装饰对象进行数据加工。
3、装饰对象被创建后,用创建后对象的实例调用接口方法,第一次方法的实现是在被装饰对象内完成。
4、客户端调用装饰对象无先后顺序。
具体场景案例
我们都玩过游戏。游戏中表现人物的情况一般都是用一堆属性来表示,如防御力、攻击力、速度等等。拿网游来举例,刚注册游戏时会让我们选择一个具体的角色(法师,弓箭手,武士等),进入游戏后游戏公司一般会赠送几件入门的装备(长剑,法杖,弓箭,鞋子等)。游戏的人物可以自由搭配各种装备。游戏中具体装备的搭配是有限制的如法师不能使用长剑、弓箭,武士不能使用弓箭、法杖等。为了方便描述在我们这个例子里就不做限制了,同时攻击也不再区分魔法攻击、物理攻击了。
在游戏这个场景中,我们就可以认为具体的角色(法师,弓箭手,武士等)就是被装饰对象,而装备(长剑,法杖,弓箭,鞋子等)就是装饰对象。
如上图定义了IAttribute作为人物属性的接口。接口中定义了getDefense(获取防御力),getPower(获取攻击力) ,getDescribe(获取描述)三个基础方法。Mage(法师)、Sagittary(弓箭手)、Warrior(武士)直接实现IAttribute的三个具体的角色。定义了Equip作为装备的基础抽象类实现IAttribute接口。Shoe(鞋子)、Bow(弓箭)、Sword(剑)、Wand(法杖)作为具体装备继承自Equip抽象类。
实现代码
package decorator;
/**
* 属性
*/
public interface IAttribute {
//获取防御力方法
public float getDefense();
//获取攻击力方法
public float getPower();
//获取描述
public String getDescribe();
}
package decorator.role;
import decorator.IAttribute;
/**
* 法师
*/
public class Mage implements IAttribute {
//防御力
private float defense = 5f;
//攻击力
private float power = 40f;
//描述
private String describe = "法师";
@Override
public String getDescribe() {
return describe;
}
@Override
public float getPower() {
return power;
}
@Override
public float getDefense() {
return defense;
}
}
package decorator.role;
import decorator.IAttribute;
/**
* 弓箭手
*/
public class Sagittary implements IAttribute {
//防御力
private float defense = 10f;
//攻击力
private float power = 30f;
//描述
private String describe = "弓箭手";
@Override
public String getDescribe() {
return describe;
}
@Override
public float getPower() {
return power;
}
@Override
public float getDefense() {
return defense;
}
}
package decorator.role;
import decorator.IAttribute;
/**
* 武士
*/
public class Warrior implements IAttribute {
//防御力
private float defense = 20f;
//攻击力
private float power = 20f;
//描述
private String describe = "武士";
@Override
public String getDescribe() {
return describe;
}
@Override
public float getPower() {
return power;
}
@Override
public float getDefense() {
return defense;
}
}
package decorator.equip;
import decorator.IAttribute;
/**
* 装备基础类
*/
public abstract class Equip implements IAttribute {
protected IAttribute attribute;
}
package decorator.equip;
import decorator.IAttribute;
/**
* 鞋子
*/
public class Shoe extends Equip {
private float defense = 70;
private float power = 10;
private String describe = "鞋子";
public Shoe(IAttribute attribute){
super.attribute = attribute;
}
@Override
public float getDefense() {
return super.attribute.getDefense() + defense;
}
@Override
public float getPower() {
return super.attribute.getPower() + power;
}
@Override
public String getDescribe() {
return super.attribute.getDescribe() + ","+describe;
}
}
package decorator.equip;
import decorator.IAttribute;
/**
* 弓
*/
public class Bow extends Equip {
private float defense = 5;
private float power = 50;
private String describe = "弓箭";
public Bow(IAttribute attribute){
super.attribute = attribute;
}
@Override
public float getDefense() {
return super.attribute.getDefense() + defense;
}
@Override
public float getPower() {
return super.attribute.getPower() + power;
}
@Override
public String getDescribe() {
return super.attribute.getDescribe() + describe +",";
}
}
package decorator.equip;
import decorator.IAttribute;
/**
* 剑
*/
public class Sword extends Equip {
private float defense = 20;
private float power = 40;
private String describe = "剑";
public Sword(IAttribute attribute){
super.attribute = attribute;
}
@Override
public float getDefense() {
return super.attribute.getDefense() + defense;
}
@Override
public float getPower() {
return super.attribute.getPower() + power;
}
@Override
public String getDescribe() {
return super.attribute.getDescribe() + ","+ describe;
}
}
package decorator.equip;
import decorator.IAttribute;
/**
* 法杖
*/
public class Wand extends Equip {
private float defense = 2;
private float power = 90;
private String describe = "法杖";
public Wand(IAttribute attribute){
super.attribute = attribute;
}
@Override
public float getDefense() {
return super.attribute.getDefense() + defense;
}
@Override
public float getPower() {
return super.attribute.getPower() + power;
}
@Override
public String getDescribe() {
return super.attribute.getDescribe() + ","+ describe;
}
}
package decorator;
import decorator.equip.*;
import decorator.role.Mage;
import decorator.role.Sagittary;
import decorator.role.Warrior;
import org.junit.Test;
/**
* 客户端调用方法
*/
public class test {
@Test
public void testDecorator(){
//创建法师
IAttribute attribute = new Mage();
attribute = new Shoe(attribute);//鞋子
attribute = new Wand(attribute);//法杖
pringMsg(attribute);
//创建弓箭手
IAttribute attribute1 = new Sagittary();
attribute1 = new Shoe(attribute1);//鞋子
attribute1 = new Bow(attribute1);//弓
pringMsg(attribute1);
//创建战士
IAttribute attribute2 = new Warrior();
attribute2 = new Shoe(attribute2);//鞋子
attribute2 = new Sword(attribute2);//剑
pringMsg(attribute2);
}
private void pringMsg (IAttribute attribute){
System.out.println("角色描述:"+attribute.getDescribe());
System.out.println("防御力:"+attribute.getDefense());
System.out.println("攻击力:"+attribute.getPower());
}
}
装饰者模式优点
1、装饰对象的添加和撤除非常灵活,客户端可以动态的给被装饰对象添加装饰对象,而且添加的顺序没有要求。
2、客户端通过使用不同的装饰对象的排列组合,可以设计出多种行为的组合。
装饰者模式的缺点
1、装饰者模式需要给每个装饰对象建立一个类,当装饰者非常多则会建立很多的装饰着类,会带来程序的碎片化添加程序的复杂度。
2、装饰者模式非常灵活,这也同时意味着该模式也有着更加多的复杂性。