C# 设计模式(三)装饰者模式(unity演示)
1、引言
在软件开发中,我们常常碰到想要给一类对象添加不同的功能。比如游戏中,一个游戏角色可以穿戴不同的物品,来不同的外观。这就是我们常说的皮肤像:靴子、护腕、武器、腰带、头盔、盔甲等,这样使玩家有了不同的属性。为了解决这个问题,我们可以使用装饰者模式来动态的给一个对象添加额外的指责。下面我们一起来探讨一下装饰者模式。
2、装饰者模式详解
2.1 定义
装饰者模式(Decorator Pattern)
饰者模式就是动态的给一个对象添加一些更多的指责。就功能而言,装饰者模式相比生成子类更为灵活。
2.2 模式结构
下面是装饰者模式的类图,我们一起来看看!
通过上图我们不难发现,在装饰者模式中有四个角色:
- 抽象构件 (被装饰的这一类对象Component)角色:给出一个抽象接口,来规范被添加职责的对象;
- 具体构件(具体要被装饰的对象ConcreteComponent)角色:定义一个将要接收附加责任的具体对象;
- 装饰抽象类(Decorator):持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口;
- 具体装饰对象(ConcreteDecoratorA和ConcreteDecoratorB):负责给构件对象 ”贴上“附加的责任。起到给Component添加指责的功能。
2.3 类图实现
具体实现代码如下所示:
Component类,抽象接口规范被添加职责的对象:
abstract class Component
{
public abstract void Operate();
}
ConcreteComponent类,具体被添加职责的对象
class ConcreteComponent : Component
{
public override void Operate()
{
Console.WriteLine("具体对象的操作!");
}
}
Decorator抽象类,继承了Component类,从外类来扩展Component类的功能
abstract class Decorator : Component
{
protected Component component;
public void SetComponent(Component component)
{
this.component = component;
}
public override void Operate()
{
if (component != null)
{
component.Operate();//执行的实际是父类的(COmponent的operate())
}
}
}
ConcreteDecoratorA和ConcreteDecoratorB类,这两个类是具体的装饰品,起到给Component添加职责的功能
class ConcreteDecoratorA : Decorator
{
private string addedState;
public override void Operate()
{
base.Operate();
addedState = "new state";
Console.WriteLine("具体装饰者A的操作!");
}
}
class ConcreteDecoratorB : Decorator
{
public override void Operate()
{
base.Operate();
AddedBehavior();
Console.WriteLine("具体装饰者B的操作!");
}
public void AddedBehavior()
{
Console.WriteLine("添加行为!");
}
}
测试一下:
ConcreteComponent c = new ConcreteComponent();
ConcreteDecoratorA a = new ConcreteDecoratorA();
ConcreteDecoratorB b = new ConcreteDecoratorB();
a.SetComponent(c);
b.SetComponent(a);
b.Operate();
Console.ReadKey();
运行结果:
具体对象的操作!
具体装饰者A的操作!
添加行为!
具体装饰者B的操作!
3、 装饰者模式在.NET中应用
在.NET类库中也有装饰者模式的实现,该类就是System.IO.Stream,下面看看Stream类结构:
上图中,BufferedStream、CryptoStream和GZipStream其实就是两个具体装饰类,这里的装饰者模式省略了抽象装饰角色(Decorator)。下面演示下客户端如何动态地为MemoryStream动态增加功能的。
MemoryStream memoryStream = new MemoryStream(new byte[] {95,96,97,98,99});
// 扩展缓冲的功能
BufferedStream buffStream = new BufferedStream(memoryStream);
// 添加加密的功能
CryptoStream cryptoStream = new CryptoStream(memoryStream,new AesManaged().CreateEncryptor(),CryptoStreamMode.Write);
// 添加压缩功能
GZipStream gzipStream = new GZipStream(memoryStream, CompressionMode.Compress, true);
4、 装饰者模式的优缺点
以上就是装饰者模式的详细介绍,下面我们来分析下它的优缺点。
-
优点:
- 装饰这模式和继承的目的都是扩展对象的功能,但装饰者模式比继承更灵活
- 通过使用不同的具体装饰类以及这些类的排列组合,设计师可以创造出很多不同行为的组合
装饰者模式有很好地可扩展性
- 缺点:
- 装饰者模式会导致设计中出现许多小对象,如果过度使用,会让程序变的更复杂。并且更多的对象会使得查错变得困难,特别是这些对象看上去都很像。
5、装饰者模式的适用场景
装饰者模式具体在哪些情况下使用呢这里也做了总结,在以下情况下应当使用装饰者模式:
- 需要扩展一个类的功能或给一个类增加附加责任。
- 需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
- 需要增加由一些基本功能的排列组合而产生的非常大量的功能
6、应用举例(unity)
下面我们来设定一个情景:
在ARPG游戏中,武器和装饰物就采用该模式扩展了武器的行为,比如剑上镶嵌一颗宝石使得宝剑在基础攻击功能上增加使敌人产生眩晕效果,你可能会想只要在剑的类中添加一个眩晕方法不就行了吗,但是这样做后,所有剑的实例对象都具备了眩晕功能,这并不是我们想要的,我们只是想单独给某个对象添加眩晕功能,你可能会说对装饰物派生一个宝石宝剑类,这样宝石宝剑类的实例化对象就具备了眩晕功能,这样就会造成一些排列组合成的类,因为装饰物是多种多样的,会有红宝石宝剑类,蓝宝石宝剑类,如果武器有刀,就会还要红宝石刀类,蓝宝石刀类,大量的排序组合类使得代码难以维护。泛化(继承)可以扩展一个类的功能,不要忘了组合一样可以扩展功能,面向对象的原则就是优先使用组合而非继承。装饰者模式就是同时使用了继承和组合来扩展武器的功能。
下面是UML类图:
我们来一起实现以下:
武器的基类:
using System.Collections;
using System.Collections.Generic;
/// <summary>
/// 武器类
/// </summary>
public abstract class Weapon
{
/// <summary>
/// 攻击方法
/// </summary>
public abstract void Attack();
}
装饰者(通过传入的武器来攻击):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
/// <summary>
/// 装饰者基类
/// </summary>
public abstract class Decorator:Weapon
{
private Weapon m_weapon;
public Decorator(Weapon weapon)
{
this.m_weapon = weapon;
}
/// <summary>
/// 武器攻击
/// </summary>
public override void Attack()
{
m_weapon.Attack();
}
}
剑类(武器的一种):
using UnityEngine;
/// <summary>
/// 剑类
/// </summary>
public class Sword : Weapon
{
/// <summary>
/// 攻击方法
/// </summary>
public override void Attack()
{
Debug.Log("剑普攻!");
}
}
红宝石武器:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
/// <summary>
/// 红宝石
/// </summary>
public class RedDiamond : Decorator
{
public RedDiamond(Weapon weapon) : base(weapon)
{
}
private void Dizziness()
{
Debug.Log("眩晕...");
}
public override void Attack()
{
base.Attack();
Dizziness();
}
}
蓝宝石武器:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
/// <summary>
/// 蓝宝石
/// </summary>
public class BlueDiamond : Decorator
{
public BlueDiamond(Weapon weapon) : base(weapon)
{
}
public override void Attack()
{
base.Attack();
Frozen();
}
/// <summary>
/// 冰冻
/// </summary>
private void Frozen()
{
Debug.Log("冰冻...");
}
}
测试类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 测试
/// </summary>
public class DecoratorTest : MonoBehaviour {
void Start () {
Weapon sw = new Sword();
sw.Attack();
print("----------------------------");
sw = new RedDiamond(sw);
sw.Attack();
print("========================");
sw = new BlueDiamond(sw);
sw.Attack();
}
}
运行一下,结果如下:
这样如果再出现装饰的派生类或者武器的派生类都可以很好的扩展:
继续扩展武器,匕首类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
/// <summary>
/// 匕首
/// </summary>
public class Dagger : Weapon
{
public override void Attack()
{
Debug.Log("匕首普攻...");
}
}
农药类(毒):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
public class YellowDiamond : Decorator
{
public YellowDiamond(Weapon weapon) : base(weapon)
{
}
public override void Attack()
{
base.Attack();
Poisoning();
}
private void Poisoning()
{
Debug.Log("中毒...");
}
}
测试:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 测试
/// </summary>
public class DecoratorTest : MonoBehaviour {
void Start () {
Weapon sw = new Sword();
sw.Attack();
print("----------------------------");
sw = new RedDiamond(sw);
sw.Attack();
print("========================");
sw = new BlueDiamond(sw);
sw.Attack();
print("+++++++++++++++++++=");
Weapon wp = new Dagger();
wp.Attack();
print("------------------------");
wp = new YellowDiamond(wp);
wp.Attack();
}
// Update is called once per frame
void Update () {
}
}
再次运行,结果如下:
我们通过装饰者,来实现了给武器增加功能,而无需关心是如何如何来添加这个功能的。这样使对象分离开来,只用关心自己的功能就可以了。
7、总结
讲真,到这里,装饰者模式的介绍就结束了,装饰者模式采用对象组合而非继承的方式实现了在运行时动态地扩展对象功能的能力,而且可以根据需要扩展多个功能,避免了单独使用继承带来的 “灵活性差”和”多子类衍生问题“。同时它很好地符合面向对象设计原则中“优先使用对象组合而非继承“和”开放-封闭“原则。装饰者模式是结构型模式的一种。
8、unity演示工程下载
在文章的最后我们给出上述例子的工程下载链接,方便朋友们探讨交流!本次演示版本Unity5.6.3f1(64-bit),VS2015需要下载的朋友,请点击此处下载。
The End
好了,今天的分享就到这里,如有不足之处,还望大家及时指正,随时欢迎探讨交流!!!
喜欢的朋友们,请帮顶、点赞、评论!您的肯定是我写作的不竭动力!
相关阅读
C# 23种设计模式(unity演示)