設計模式之行為類模式01

設計模式の行為型模式(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個怎么辦?

總結以下:什么時候我們會用到命令方法菇晃?

  1. 需要處理一些復雜事物册倒。
  2. 這些復雜事物一般涉及到其他類型的事物處理。(比如上面例子中的QQ群和出納人員)
  3. 這些復雜事物有一定的關聯(lián)性磺送。(都是行政部門的事情)
  4. 這些復雜事物的數(shù)量和內容不確定驻子。

使用命令模式的時候要注意:

Command類型自己是不包含任何其他具體功能性代碼的灿意,即:除了上面接口中的Handle()方法以外不應該有其他開放方法。

解釋器模式

略崇呵,個人印象好像沒怎么用過缤剧。意思大概是我給定一種數(shù)據(jù)(可以是字符串也可以是其他自定義數(shù)據(jù)),最終通過解釋器來給出一種答案域慷。

應用場景:

  • 計算器:輸入一個算式荒辕,解釋出最終答案
  • 翻譯軟件: 輸入一個語言的句子, 解釋出另一種語言的意思犹褒。
  • 自定義編程語言: 輸入自定義編程語言的代碼抵窒, 給出最終執(zhí)行結果。有些語言比如python或者ruby等都有自帶命令行解釋器化漆,Chrome瀏覽器也自帶javascript解釋器估脆。

迭代器模式

何謂迭代器模式?所謂迭代器模式就是提供一種方法順序訪問一個聚合對象中的各個元素座云,而不是暴露其內部的表示疙赠。在實際的開發(fā)過程中,我們可能需要針對不同的需求朦拖,可能需要以不同的方式來遍歷整個整合對象圃阳,但是我們不希望在聚合對象的抽象接口層中充斥著各種不同的便利操作。這個時候我們就需要這樣一種東西璧帝,它應該具備如下三個功能:

  1. 能夠便利一個聚合對象捍岳。

  2. 我們不需要了解聚合對象的內部結構。

  3. 能夠提供多種不同的遍歷方式睬隶。

迭代器這玩意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完成互相之間的溝通议谷。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末炉爆,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子卧晓,更是在濱河造成了極大的恐慌芬首,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件逼裆,死亡現(xiàn)場離奇詭異郁稍,居然都是意外死亡,警方通過查閱死者的電腦和手機波附,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門艺晴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人掸屡,你說我怎么就攤上這事封寞。” “怎么了仅财?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵狈究,是天一觀的道長。 經(jīng)常有香客問我盏求,道長抖锥,這世上最難降的妖魔是什么亿眠? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮磅废,結果婚禮上纳像,老公的妹妹穿的比我還像新娘。我一直安慰自己拯勉,他們只是感情好竟趾,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著宫峦,像睡著了一般岔帽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上导绷,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天犀勒,我揣著相機與錄音,去河邊找鬼妥曲。 笑死贾费,一個胖子當著我的面吹牛,可吹牛的內容都是我干的逾一。 我是一名探鬼主播铸本,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼遵堵!你這毒婦竟也來了箱玷?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤陌宿,失蹤者是張志新(化名)和其女友劉穎锡足,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體壳坪,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡舶得,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了爽蝴。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沐批。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蝎亚,靈堂內的尸體忽然破棺而出九孩,到底是詐尸還是另有隱情,我是刑警寧澤发框,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布躺彬,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏宪拥。R本人自食惡果不足惜仿野,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望她君。 院中可真熱鬧脚作,春花似錦、人聲如沸缔刹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽桨螺。三九已至,卻和暖如春酿秸,著一層夾襖步出監(jiān)牢的瞬間灭翔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工辣苏, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留肝箱,地道東北人。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓稀蟋,卻偏偏與公主長得像煌张,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子退客,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內容

  • 如果需要原文檔(因文體限制骏融,部分表格無法呈現(xiàn))請聯(lián)系QQ1769090563 本文由中醫(yī)仲景協(xié)會整理收集 《內經(jīng)選...
    陶墨閱讀 34,365評論 0 33
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn)萌狂,斷路器档玻,智...
    卡卡羅2017閱讀 134,661評論 18 139
  • 作為一個南北“混血”,我自小就生活在南北文化的交匯中心茫藏。正因為這樣误趴,我有幸能擁有來自南北兩個不同地域的生活體驗,從...
    煙雨闕閱讀 397評論 2 3
  • 翻到此時的bingo君的表情务傲。 翻譯自日本作家池井戶潤的小說《銀翼のイカロス》是半澤直樹系列小說的第四部凉当。伊卡洛斯...
    萬事bingo君閱讀 6,213評論 2 33