設計模式の行為型模式(1)
我們經(jīng)歷了創(chuàng)建型模式,結構型模式,最終來到了設計模式的終點站:行為型模式.
以下我們講的一些模式主要關注的是對象或類之間的行為和職責,避免導致混亂的代碼邏輯徒增維護成本.
責任鏈模式
責任鏈根本上就是個鏈表結構,我們從鏈條的頂端一節(jié)一節(jié)往下找應該負責的節(jié)點,如果當前節(jié)點處理事物完畢僵闯,我們就不再繼續(xù)尋找了。
比如一個斗地主游戲,我們要判斷玩家選擇的幾張牌是不是符合規(guī)則摆马,我們就要做一個卡牌規(guī)則責任鏈性穿,一個一個規(guī)則判斷下去。
public class Card
{
public int Number { get; set; }
}
public enum CardSuit
{
Spade, Heart, Club, Diamond
}
public abstract class CardRuleDetectionBase
{
public abstracct bool Handle(Card[] cards);
public CardRuleDetectionBase Next { get; set; }
}
現(xiàn)在我們創(chuàng)建一個紙牌的規(guī)則責任鏈
public class SingleCardRule : CardRuleDetection
{
public override bool Handle(Card[] cards)
{
if(cards.Length == 1)
return true;
return false;
}
}
public class DoubleCardRule : CardRuleDetection
{
public override bool Handle(Card[] cards)
{
if(cards.Length == 2 && cards[0].Number == cards[1].Number)
return true;
return false;
}
}
public class TripleCardRule : CardRuleDetection
{
public override bool Handle(Card[] cards)
{
if(cards.Length == e && cards[0].Number == cards[1].Number && cards[1].Number == cards[2].Number)
return true;
return false;
}
}
最后是這么使用的.
public class Program
{
public static void Main(string[] args)
{
var sr = new SingleCardRule();
sr.Next = new DoubleCardRule();
sr.Next.Next = new TripleCardRule();
var cards = new []
{
new Card() { Number = 2 },
new Card() { Number = 2 },
new Card() { Number = 2 }
};
Console.WriteLine(IsCardValid(cards, sr)); // true
cards = new []
{
new Card() { Number = 13 }
};
Console.WriteLine(IsCardValid(cards, sr)); // true
cards = new []
{
new Card() { Number = 3 },
new Card() { Number = 4 },
new Card() { Number = 5 },
new Card() { Number = 6 },
new Card() { Number = 7 }
};
Console.WriteLine(IsCardValid(cards, sr)); // false
}
public static void IsCardValid(Card[] cards, CardRuleDetection rule)
{
do
{
if(rule.Handle(cards))
return true;
rule = rule.Next;
}
while(rule != null);
return false;
}
}
這種鏈的模式我并不是很喜歡, 數(shù)組不也行嘛..
public class Program
{
public static void Main(string[] args)
{
var rules = new CardRuleDetection[]
{
new SingleCardRule(),
new DoubleCardRule(),
new TripleCardRule()
};
var cards = new []
{
new Card() { Number = 2 },
new Card() { Number = 2 },
new Card() { Number = 2 }
};
Console.WriteLine(IsCardValid(rules, cards)); // true
}
public static void IsCardValid(CardRuleDetection[] rules, Card[] cards)
{
foreach(var r in rules)
{
if(r.Handle(cards))
return true;
}
return false;
}
}
命令模式
命令模式可以解決我們應對復雜多變的請求的情況.比如一個公司的行政部要處理的是非常的繁雜,如果行政部門只有一個人,并且他做的事情都很不確定,請假報銷流程等等流程變化之后會讓那個人很頭痛.
理想狀態(tài)下我們應該是這樣做的:每件事情由一個熟練工干,如果這件事的流程變化的話,就把這個人開掉,再招熟悉新的流程的人來做.然后行政部門以訂單形式處理事務,每個事物包含一個請求內容.這樣就能高效的處理任何事情了.
不過這種理念在現(xiàn)實中肯定是不可能實現(xiàn)的了,但是在程序世界里是完全可以實現(xiàn)的.
首先我們定義一個訂單處理流程:
public interface IOrderCommand<T> where T : Order
{
string OrderName { get; }
void Handle(T request);
}
public class Order
{
public Order(string orderName)
{
OrderName = orderName;
}
public string OrderName { get; private set; }
}
public class OrderDispatch
{
private List<IOrderCommand> commands = new List<IOrderHandler>();
public void RegisterCommand(IOrderCommand h)
{
commands.Add(h);
}
public void Handle(Order order)
{
foreach(var c in commands)
{
if(o.OrderName == c.OrderName)
{
c.Handle(o);
break;
}
}
}
}
我們模擬一下平時我們公司的行政事務:
//快遞通知業(yè)務
public class PackageOrder : Order
{
public PackageOrder() : base("package") { }
public Package Package { get; set; }
}
public class PackageNotifyCommand<PackageOrder> : IOrderCommand
{
public PackageNotifyCommand(QQGroup group)
{
this.group = group;
}
private QQGroup group;
public string OrderName { get { return "package"; } }
public void Handle(PackageOrder order)
{
var qqPersion = group.FindName(order.Package.Receiver);
if(qqPerson != null)
qqPerson.SendText("前臺有你的快遞過來拿一下~");
}
}
//活動報銷業(yè)務
public class ReimbursementOrder : Order
{
public ReimbursementOrder() : base("reimbursement") { }
public Person Person { get; set; }
public String Type { get; set; }
public float Amount { get; set; }
}
public class ReimbursementCommand<ReimbursementOrder> : IOrderCommand
{
public ReimbursementCommand(Cashier cashier)
{
this.cashier = cashier;
}
private Cashier cashier;
public void Handle(ReimbursementOrder order)
{
if(order.Type == "food")
return;
var money = cashier.Request(order.Amount, order.Person.Name + ": "+order.Type);
order.Person.Give(money);
}
}
最后模擬一下應用場景
public void Main()
{
var dispatch = new OrderDispatch();
var pnc = new PackageNotifyCommand(new QQGroup("XXX公司群"));
var rc = new ReimbursementCommand(new Cashier("韓梅梅"));
dispatch.RegisterCommand(pnc);
dispatch.RegisterCommand(rc);
var pOrder = new PackageOrder();
var package = new Package() { Receiver = "李雷", content = "肥皂" };
pOrder.Package = package;
dispatch.Handle(pOrder);
var rOrder = new ReimbursementOrder();
rOrder.Person = new Person () { Name = "Jim" }
rOrder.Type = "texi";
rOrder.Amount = 20;
dispatch.Handle(rOrder);
}
如果我們回到最原始的寫法應該是怎么樣的愕鼓?
public class AdminDepartment
{
public QQGroup group;
public Cashier cashier;
public void HandlePackage(Package p)
{
group.FindName(p.Receiver).SendText("快遞!");
}
public void HandleReimbursement(Person p, string type, float amount)
{
if(type=="food" || type == "texi")
return;
var money = cashier.Request(amount / 2, p.Name + ": "+type);
order.Person.Give(money);
}
}
以上方法嚴重違反OCP和SRP钙态。試想以下如果需要處理的事物超過20個怎么辦?
總結以下:什么時候我們會用到命令方法菇晃?
- 需要處理一些復雜事物册倒。
- 這些復雜事物一般涉及到其他類型的事物處理。(比如上面例子中的QQ群和出納人員)
- 這些復雜事物有一定的關聯(lián)性磺送。(都是行政部門的事情)
- 這些復雜事物的數(shù)量和內容不確定驻子。
使用命令模式的時候要注意:
Command類型自己是不包含任何其他具體功能性代碼的灿意,即:除了上面接口中的Handle()方法以外不應該有其他開放方法。
解釋器模式
略崇呵,個人印象好像沒怎么用過缤剧。意思大概是我給定一種數(shù)據(jù)(可以是字符串也可以是其他自定義數(shù)據(jù)),最終通過解釋器來給出一種答案域慷。
應用場景:
- 計算器:輸入一個算式荒辕,解釋出最終答案
- 翻譯軟件: 輸入一個語言的句子, 解釋出另一種語言的意思犹褒。
- 自定義編程語言: 輸入自定義編程語言的代碼抵窒, 給出最終執(zhí)行結果。有些語言比如python或者ruby等都有自帶命令行解釋器化漆,Chrome瀏覽器也自帶javascript解釋器估脆。
迭代器模式
何謂迭代器模式?所謂迭代器模式就是提供一種方法順序訪問一個聚合對象中的各個元素座云,而不是暴露其內部的表示疙赠。在實際的開發(fā)過程中,我們可能需要針對不同的需求朦拖,可能需要以不同的方式來遍歷整個整合對象圃阳,但是我們不希望在聚合對象的抽象接口層中充斥著各種不同的便利操作。這個時候我們就需要這樣一種東西璧帝,它應該具備如下三個功能:
能夠便利一個聚合對象捍岳。
我們不需要了解聚合對象的內部結構。
能夠提供多種不同的遍歷方式睬隶。
迭代器這玩意C#自帶锣夹,我們看看C#迭代器的定義是什么吧:
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
public interface IEnumerator
{
/// <summary>Gets the current element in the collection.</summary>
/// <returns>The current element in the collection.</returns>
object Current
{
get;
}
/// <summary>Advances the enumerator to the next element of the collection.</summary>
/// <returns>true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.</returns>
/// <exception cref="T:System.InvalidOperationException">The collection was modified after the enumerator was created. </exception>
bool MoveNext();
/// <summary>Sets the enumerator to its initial position, which is before the first element in the collection.</summary>
/// <exception cref="T:System.InvalidOperationException">The collection was modified after the enumerator was created. </exception>
void Reset();
}
可以從源碼看到, IEnumerator只給了使用者順序訪問該對象所有成員的功能,僅此而已.
中介模式
中介這兩個字立馬讓我先到了房屋中介。苏潜。程序開發(fā)中的中介和房屋中介有一種情形是類似的:未來房客和房東之間不方便見面或溝通(比如空閑事件不一致银萍,語言不通的問題),由中介代替雙方完成溝通恤左。
程序開發(fā)中我們也經(jīng)常會遇到一個問題: A大系統(tǒng)的中的一個子功能要和B大系統(tǒng)中的一個子功能需要互相溝通贴唇。
但是A大功能所在的環(huán)境和B大功能完全沒有任何交集。A無法引用到B飞袋,或者說A不應該引用到B戳气。這時候需要用到中介模式。
舉個例子巧鸭,雪糕工廠里面有7個大階段瓶您,每個階段理論上互相獨立,互不影響,但是在銜接的時候難免出現(xiàn)一些相互都要處理的事物览闰。
一個盤子里的碗由于各種原因導致不是直接傳遞給下一個階段芯肤,而是下一個階段直接新建一塊碗。這就導致了在觀感上出現(xiàn)了2塊碗的情況压鉴。于是下一個階段要通知上一個階段把舊的碗給隱藏掉。
由于兩個階段是互相獨立的锻拘,他們直接無法輕易地(或者說是美觀地)引用到對方油吭,于是我們使用了一個叫通知中心的類。
設:上個階段為A署拟,下個階段為B婉宰,通知中心為C。A監(jiān)聽C未來將要發(fā)出的“隱藏碗”的消息推穷,并在收到這個消息以后將碗給隱藏起來心包;B在合適的事件發(fā)送“隱藏碗”的消息給C;C收到消息后發(fā)現(xiàn)A需要這個消息馒铃,于是C調用了A的處理方法蟹腾。A成功將碗隱藏了起來。
于是A和B之間的通訊就這么產生了区宇。然而他們之間還是沒有直接的聯(lián)系娃殖。
總結: 使用中介模式的情況為A和B無法或無法優(yōu)美的引用到對方,這時需要第三者插足來為A和B完成互相之間的溝通议谷。