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
好了,今天的分享就到這裡,如有不足之處,還望大家及時指正,隨時歡迎探討交流!!!
喜歡的朋友們,請幫頂、點贊、評論!您的肯定是我寫作的不竭動力!