扒开腿挺进岳湿润的花苞视频|将军边走边挺进她H树林|挺进朋友人妻张婉莹身体|岳脱得精光让我挺进去视频|第一次挺进莹莹的身体里视频|脱岳裙子从后面挺进去视频

新疆軟件開發(fā)

本站首頁 軟件開發(fā) 成功案例 公司新聞 公司簡介 客服中心 軟件技術 網(wǎng)站建設
  您現(xiàn)在的位置: 新疆二域軟件開發(fā)公司 >> .Net技術 >> 文章正文

模式怎樣使用,講解模式的使用方法!

           模式到今天已經(jīng)20年歷史了, 尤其是GoF模式, 更廣泛被大家熟知, 這些模式就像武俠小說里的招式, 什么時候該使用什么招式, 師傅們只能泛泛而談, 正確的判斷必須由我自己的做出. 以后會介紹些自己的淺薄經(jīng)驗, 并和大家討論, 只是不知道如何寫起; 無論你是存在問題, 還是已經(jīng)有了自己的答案, 都可以給我留言; 對于前者我不見得會馬上回復, 但是對我如何去寫有很大啟發(fā).
在這里先整理一些有疑問的使用方式, 作為未來的素材.

職責鏈使用情景一: 原帖
在表達我的想法之前, 我們先優(yōu)化一下作者原來的代碼中如下部分, 雖然這些優(yōu)化中有一些對于真正的職責鏈模式是錯誤的, 而在這情況下使用鏈表是存在很大疑問的, 但是我們?nèi)绻痪心嘤诼氊熸湻Q號, 也不考慮使用鏈表的合理性, 則這些優(yōu)化可以當作一次重構的展示:

對象一:
if (變量 == "信息一") 某操作 else if (存在下一個對象) 下一對象.執(zhí)行() else 默認操作
對象二:
if (變量 == "信息二") 某操作 else if (存在下一個對象) 下一對象.執(zhí)行() else 默認操作
...
...

這是什么? 這就是重復, 這種重復如果混進代碼里往往沒有那么明顯; 先把最明顯的部分搞掉吧:

    public class BasicHttpBindingConstraint:BindingConstraint
    {
        public override bool Constraint(ServiceEndpoint endpoint)
        {
            if (endpoint.Binding.Name == "BasicHttpBinding")
            {
                //原操作
            }
            return base.Constraint(endpoint);
        }
    }
判斷是否有下一個等等寫入基類, 還是原來那些代碼. 這樣重構的好處除了消除了代碼重復, 原文中兩個protected的字段, 也不用再向?qū)崿F(xiàn)類暴露, 做到了更好的信息隱藏. 但是真的沒有重復了嗎? 看看這句: if (endpoint.Binding.Name == "BasicHttpBinding"), 其中變化的只有引號內(nèi)部的部分, 其余的還是重復. 同時, 每一個實現(xiàn)類, 雖然不用寫else了但是base.Constraint(endpoint)卻得Copy&Paste一遍.

接下來我們這么干一下基類:

    public abstract class BindingConstraint//:IEndpointConstraint
    {
        #region protected fields
        //只要是field, 最好連protected也不要.
        #endregion

        private BindingConstraint m_bindingConstraint; //與原來相比, private做到了更好的信息隱藏
        private bool m_hasNextConstraint = false;       

        #region public methods

        public void AddConstraint(BindingConstraint constraint)
        {
            m_bindingConstraint = constraint;
            m_hasNextConstraint = true;
        }

        public bool Constraint(ServiceEndpoint endpoint) //注意, 不再abstract了
        {
            if (BindingConstraintName == endpoint.Binding.Name)
            {
                return DoConstraint(endpoint);//注意, 這么優(yōu)化是不對的, 見后面關于職責鏈的討論
            }
            else
            {
                if (m_hasNextConstraint)
                {
                    return m_bindingConstraint.Constraint(endpoint);
                }
                else
                {
                    return false;
                }
            }
        }              

        #endregion       

        #region public abstract methods
        //新增了兩個
        protected abstract bool DoConstraint(ServiceEndpoint endpoint);
        protected abstract String BindingConstraintName
        {
            get;
        }

        #endregion

    }
根據(jù)傳說中的單一職責, 作為實現(xiàn)的子類, 無需再負責鏈表的相關工作, 只要管自己干嘛, 并提供自己負責事情的代號, 就可以了. 但是我要說明的是, GoF的代碼例子, 是我和當前例子的作者的那個中間值: 第一次優(yōu)化后那個結果; 為什么呢?

第一次優(yōu)化后, base.Constraint(endpoint)這個, 還可以不執(zhí)行, 這樣我們就有了一個機會, 定義默認操作, 甚至將職責鏈原本的鏈條改變, 引導到另外一個職責鏈上去. 請大家注意這個事實: 使用base.Constraint(endpoint)的方法, 增加了重復, 但換到了靈活性; 也就是說, 只要是損失的, 必須換來其它的東西, 否則回家咱老婆也不干啊不是.

只是針對這個例子, 把去優(yōu)化它兩次沒啥問題, 不過仍然可以看作吃飽了撐的沒事干, 唯一的作用就是順便就問這么一個事情:

信息隱藏完美, 沒有重復, 使用優(yōu)雅, 對擴展開放, 一切符合教科書上各種原則的代碼. 就一定是正確的代碼嗎?

該文作者使用鏈表作設計, 無可厚非, 畢竟, 他達到了他想要的目的; 但是其實這種用法, 存在著一個的問題: 鏈表上每一個對象, 都要保存著上一個對象的引用, 從接到請求, 到有一個對象響應, 中間有多少個對象, 就有多少個廢開銷. 有開銷沒有問題, 問題是開銷換來的是什么? 該文作者總結到:

引用(錯誤!見后)使用職責鏈模式必須恰到好處,否則會成為模式濫用的反面教材。根據(jù)我對職責鏈模式的理解,可以認定只要同時符合下列三個條件,就可以引入職責鏈模式:
1、當一個方法的傳入?yún)?shù)將成為分支語句的判斷條件時;
2、當每一個分支的職責相對獨立,且邏輯較為復雜時;
3、當分支條件存在擴展的可能時。
我的看法是, 基本上打到靶子外去了. 那些可能產(chǎn)生的開銷, 其實是白白犧牲了還被扣了個"廢"的帽子; 勤奮工作的CPU, 被我一個初學者硬生生的釘上了"貪污和浪費, 是極大的犯罪"的歷史的恥辱柱. 我們這次付出, 不像上面GoF的老大們選擇的不優(yōu)化, 反而像談戀愛, 大把的白花花的銀子花出去, 最后只等到一張好人卡~

比如用Strategy模式實現(xiàn), 照樣可以得到這些好處. 比如該文作者的例子:

1. 創(chuàng)建一個類作為執(zhí)行者, 執(zhí)行者中持有一個保寸字符串和IBindingConstraint的字典(比如Dictionary, HashTable);
2. 這個字典的初始化, 既可以用原來Add的方式, 也可以通過一個配置文件初始化;
3. 實現(xiàn)一個Execute, 根據(jù)endpoint.Binding.Name從字典取出IBindingConstraint, 并執(zhí)行Constraint;
4. 使用時調(diào)用: 推薦者.Execute(endpoint).

很顯然, 這樣的做法, 不但有更小的開銷, 也有更少的代碼, 同時擁有更合理的結構. 在這里我要隆重的提醒大家注意使用字典, 這種方式雖然不入GoF 23種模式大雅之堂, 卻是n多大牛/大嘴推薦的萬靈丹. 對字典的使用也有一個廣泛學名, 叫做"表模式"(還是什么來著? 記性不好...). 當然就針對當前的討論所針對的問題來說, 我們也可以把它理解為策略模式的一個應用.

為什么說這種方式更合理呢? 讓我們來看一下原文作者所說的三條, 其模型到底是什么樣的:

信息1 -> 過程或?qū)ο笠?BR>信息2 -> 過程或?qū)ο蠖?BR>....

SELECT 對象 FROM 某處 WHERE 信息 = @變量, 查表, 赤裸裸的查表! 職責鏈呢? 作為一個設計模式, 僅僅是針對查表然后執(zhí)行算法, 那策略模式早就對這個搶飯碗的提反對意見了. 事實上職責鏈到底是干嘛地的呢? 搞清楚這個問題, 就可以判斷真正的職責鏈在這里是不是合適, 或者原文作者使用鏈表的方式, 是否是職責鏈所覆蓋的范疇了.

DP955.1 CHAIN OF RESPONSIBILITY
1. 意圖
使多個對象都有機會處理請求......
2. 動機
考慮一個圖形界面中上下文有關的幫助機制.....如果對于那一部分界面沒有特定的幫助信息, 那么...顯示一個關于當前上下文的較一般的幫助信息, 比如說.....
因此很自然地, 應根據(jù)普遍性即從最特殊的到最普通的順序來組織..
3. 適用性

有多個對象可以處理一個請求, 哪個對象處理一個請求運行時刻自動確定.
...
...

找啊找啊找朋友, 找到一個女朋友~ 沒錯, 職責鏈說的就是這個事: 你認識一個特別出色的女人, 你跟她說, 做我馬子吧, 如果她同意了, 自然好; 如果她說, 你就像我哥哥, 于是你說了, 既然我是你哥哥, 你得幫哥哥介紹一個吧? 她為了盡快擺脫你, 把一個比她差點的朋友介紹給了你, 然后你說, 做我馬子吧... 如此下去, 直到有一個人接受你為止.

有一個問題我們要想到: 假設那個特別出色的女人是A, 她那天正好想到的朋友是B, 你就去給B花錢了; 如果想到的是C, 那你就是給C花錢; 同時, B如果不同意, 也許會給你C的電話, 也許會給你D或者E的電話. 而BCDE都有可能接受你. 也就是說, 誰把你接收了是不一定的, 但總有一個能接收你, 當然這里假設你是個女人就行, 換個說法, 你是無要求的, 響應者是不固定的. 將Gof的例子形象化, 想像所有的東西都是卡片, 疊在一起 如下:


           --  
        ------
   --------------
--------------------


從正上方看,  是一個窗口平面,  最頂層的那個區(qū)域是某種Widget1, 下面一種是 Widget2, 再下面一種是Widget3, 最底下是4, 記住, 全都是不同的class, 繼承自一個Handler. 當我們用小問號點擊Widget1所在區(qū)域的時候, 如果Widget1含有幫助信息, 就顯示W(wǎng)idget1的, 如果沒有, 就顯示W(wǎng)idget2的以此類推. 當然, 真實的例子不會這么擺, 只會比這個更復雜, 從最底層的對象開始, 往上基本是一個樹狀的樣子. 我們可以認為上面的圖, 是這棵樹某一個分支, 正好就這么簡單.

我們換一個放法: Widget1下面是Widget7, Widget3, Widget9(按順序), 也就是說, 隊伍根本不同了; 尋找的過程仍然相似, 只是最終顯示的幫助信息有可能不一樣.  為什么呢? 我們考慮一個控件: Label. Label本身一般沒有幫助, 我們把它放入Widget2里, 它如果被點擊了, 其效果和點擊Widget2應該是一樣的; 換成Widget7如果Widget7有幫助, 就應該顯示W(wǎng)idget7的幫助. 但如果正好Widget7也沒有幫助呢? 就顯示W(wǎng)idget3的唄, 因為Label既然是Widget7的一部分, 顯然也是Widget3的一部分; Widget7自己被點擊, 顯示的也是Widget3的幫助.

我們需要看到的是: Widget7和Widget3, 顯示幫助的方式可能完全不同, 這原因并非因為Widget7對應了一個既定的傳入信息而Widget3對應了另外一個, 而是因為Widget的畫圖方式可能和Widget3不同; 即這種不同不是對應于外部對象, 而是職責鏈上對象本身的特性所確定的. 這就決定了, 在運行時, 對于某一信息, 我們改變了這一信息的處理方式. 沒人處理的方式一般也不是返回false, 而是交給最終的一個DefaultHandler, 做默認操作; 這表面看跟直接返回沒啥區(qū)別, 每次還必須將DefaultHandler接到職責鏈尾端, 但實質(zhì)上保證了處理方式的統(tǒng)一.

另外, GoF的例子中, 一個對象是否響應職責鏈, 也是不固定的, 這話看著和上面重復, 其實這是另一個不固定: 任何一個從基類繼承下來的對象, 比如上面這些Widget, 如果它被設置了幫助, 當它被點擊就會響應, 如果沒有設定幫助(默認值), 就應該由它傳遞給更下層的一個. 這個情況下Widget7響應這個請求, 另一個情況也許就不響應了, 這只和是否給Widget7設置了幫助文本有關. 即: 判斷是不是響應的條件, 并非傳入對象持有的信息(事實上GoF的例子根本沒有傳入對象), 而是由職責鏈上的對象自己持有的信息來確定.在GoF的說法里, 非常關鍵的一個詞, 就是上下文相關, 這才是職責鏈的精髓所在, 而傳遞這一形式, 僅僅是具體做法罷了.

最后, 考慮這個窗口平面的例子, 我們用小問號點擊的Widget, 不一定是處于鏈表頭的對象, 我們的點擊可以落在鏈表中任何一層上任何一個對象所對應的范圍內(nèi), 所以鏈表中每一個對象都肯能而且可以成為入口. 按照原文例子的用法, 所有的請求都只能從鏈表頭也就是最上層進去, 這和DP95上所描述的意圖和結構完全不符, 在GoF描述的職責鏈功能和用法中, 每一個具體子類都可能響應請求, 這是由輸入動態(tài)決定的, 如果只從頭部進去, 只不準哪個不該負責的對象就進行了不正確響應: 比如點擊的Widget3, 請求卻讓Widget1給截獲了.

不同的子類, 只是行為不同, 而不代表該子類從根本上只可以處理它對應的信息, 不能處理其它信息, 即不存在(信息1 --> 行為1)這樣的對應關系; 每一個行為都應該可以處理任意一種信息, 子類改變的就只是處理方式, 而到底哪一個用什么方式處理, 則是動態(tài)的根據(jù)狀態(tài)決定的. 正是因為我們不知道某一對象, 是否會處理這個信息, 所以我們就無法應用表模式, 這樣就需要職責鏈模式來解決.這才是23種而不是22種設計模式的原因.

比如這樣的形式:

XxxBindingConstraint x = new XxxBindingConstraint();
x.SetParent(previousBindingConstraint);
x.AddNames({"Basic", "WS", "xxx".});


很顯然, 如果這樣修改原文的例子, 雖然每個對象都能響應請求了, 卻不能正確執(zhí)行: 但因為其設計目的緣故, 比如Basic對應的實現(xiàn)只并不負責處理WS, 只能等著拋出異常, 根本不就不符合這個意義:

DP95第五章開頭
Chain of Responsibility(5.1)....,根據(jù)運行時刻情況任一候選者都可以響應相應的請求.

關于從鏈條中間開始響應請求倒是不需要改, 只是這一行為根本無法做出: 因為這個設計從根本上講只能夠從頭部遍歷到那個屬于他的真命天女, 而并非由運行時輸入所指定的那個開始找一個最合適的對象. 所以我們可以說, 這個例子只是徒具鏈表的形式, 卻根本沒有考慮鏈表更適合哪些情況, 在那些情況下如何發(fā)揮正面作用的; 無論這一設計實現(xiàn)的多么完美, 它相比其它選擇, 更多的是擔負了不該有的負面效果. 拋開是不是明白或誤解了什么設計模式這樣毫無重要性的事情, 這在我看來就像在過去經(jīng)常提到的, 是缺乏對事物的正確分析的緣故. 這一前提, 才是做出良好設計的唯一前提, 哪怕你都不知道設計模式是啥, 甚至連面向?qū)ο髱讉€字怎么寫都不知道.

多說一句, 對于GoF的例子, 其實也存在著很多粗陋之處(過于簡單就不算了); 比如是不是HasHelp, 由基類提供一個方法判斷, 但是如果HasHelp()傳回false, 由咱們實現(xiàn)的子類來轉(zhuǎn)發(fā). 這個模型產(chǎn)生了一個不一致性: 如果基類的HasHelp()認為沒有Help, 但其實你根本沒有用基類這個邏輯, 按照子類的邏輯是有Help的所以你就直接響應了呢? 于是為了消除這個不一致性, 我們實現(xiàn)子類時就不得不給基類提供信息, 這就可能會增加不必要的繁文縟節(jié). 只是這些不完美, 可以理解為示例的不嚴謹, 總體來說是沒有任何大的漏洞的.

再看看原帖作者的例子: 每一個傳入的對象, 有一個明確的接收者; 鏈上除這個明確接收者之外的其它對象, 絕不會處理請求, 而這個明確的接收者, 是否處理某請求這一要素是固定的. 也就是說, 鏈上如果沒有這個明確的接收者, 該請求不會被處理; 而不是因為鏈上的接收者們在運行時刻根據(jù)具體情況判斷是否響應這個請求, 從而改變響應請求所執(zhí)行的行為. 這樣的用法, 半點職責鏈的獨有好處也得不到, 只能說形似職責鏈, 實則表模式, 而且由于是鏈表, 就變成了負擔職責鏈開銷的策略模式.

怪怪設計模式第一反律: 可以找出對應關系的, 不要使用鏈表, 如果硬以傳遞的方式成鏈, 也不能叫做職責鏈, 只能叫做對象鏈表. 職責鏈是一根鏈上每一環(huán)節(jié)皆可響應, 但不一定響應; 不是只有一個固定響應, 其它負責傳遞; 歸根結底, 是整條鏈有機的負責一個響應一個請求, 而不是一個鏈表中某一對象一個人在戰(zhàn)斗, 職責是鏈的職責而不是某一對象的職責. 否則這條鏈除了因為傳統(tǒng)鏈表的傳遞而增加了負擔又有何意義?

什么時候該用職責鏈, 請自己思考, 但我們完全可以做出什么時候不要使用職責模式的判斷:

1、當一個方法的傳入?yún)?shù)將成為分支語句的判斷條件時;
2、當每一個分支的職責相對獨立,且邏輯較為復雜時;
3、當分支條件存在擴展的可能時。

總而言之, 只要你看見"分支"二字, 請不要使用職責模式.

我們深入一下這個問題的實質(zhì)的話, 就會發(fā)現(xiàn), 因為誰對誰響應這個對應關系, 是在運行之前就確定的, 所以最適合的方式是使用表模式. 由此我們可以得出:

怪怪設計模式第一推廣: 可以找出對應關系的, 優(yōu)先考慮表模式; 看見"分支"二字, 優(yōu)先考慮策略模式.

最后我們再看一下原作者的最佳實踐:

引用(錯誤! 見后)1、應盡量將職責鏈模式的抽象定義為抽象類,而不要定義為接口。這樣有利于一些公共邏輯的重用。
2、應在實現(xiàn)職責鏈模式的同時,提供創(chuàng)建職責鏈的工廠類。
關于1, GoF當初是抽象類, 但是其實接口并不是不可以. 如果你想細究這個問題, 可以看上面關于GoF例子瑕疵的討論, 來思考父類集中操作有多大意義. 但記住請先翻DP95, 了解清楚那個例子. 有沒有抽象類, 關鍵還在于抽象類合不合適:

怪怪第一哲學定律: 一切決定都是交換.

如果你公共操作很多, 那你就付出抽象類站位的代價(提示:DP95的代碼是C++, 可以多繼承); 如果你的其它設計需要基類, 就要付出打字的代價. 這兩者可以兼得嗎? 答案見文章末尾注釋一.

關于2, 完全不靠譜. DP95上說的非常清楚, 職責鏈要解決的一個問題就是, 鏈條上單元的動態(tài)改變. 拿上面的Widget的例子來說, 難道我們應該為每種不同排列順序, 都搞個工廠方法? 不同類別的Widget們只要能互相嵌套, 其排列組合有多少是可以預期的. 另外, 真正的職責鏈, 接收請求的對象可以是鏈條中的任何一個具體子類, 而用了Factory, 我們只能從頭部傳入信息, 職責鏈原本具有的行為就不正常了, 討論見上面Widget那部分.

在文章末尾, 我想嚴肅的說兩句多余的話: 我一直在呼吁的是, 如果作者們不確定自己的理解, 請考慮對博文讀者的影響有多大可能性是壞的; 寫東西時, 也不要太過自信, 如金科玉律; 不如以提問的方法說, 比如: "我是這樣想的, 大家看對不對? "; 更不要著急在得到驗證前, 隨便將自己的文章放傳播到更廣泛的地方上去, 把炸彈變成原子彈.

不過我仍然覺得, 今天這塊板磚有點太大了, 因為我拿一本設計模式書籍的作者當作了例子. 我想這篇文章是dudu都不愿看到的(小人之心度君子之腹), 畢竟該作者的大作屬于博客園系列. 我其實非常猶豫發(fā)不發(fā)該文, 因為說實在的, 別人對設計模式會不會誤解, 和我一樣的初學者會不會受誤導, 跟我有屁關系? 我又不是以技術宣傳為生存手段的, 況且如果我打算寫書, 恐怕這篇文章連出版商都不愿意看到; 恐怕他們更愿意的是別人自己寫自己的, 別涉及他們的作者的文章; 哪個出版商會對沒事得罪人的人感興趣? 我之所以意識到這些問題, 卻沒有去換個例子, 也有考慮: 我一直提倡的是, 無論誰寫的是黑紙白字幾十塊一本, 還是隨便討論瞎說幾句, 大家最好都自己思考后再說; 從這個意義上講, 原文作者的情況作為例子倒是最合適的.

且不說招一大片人反感的問題, 作為一個軟件從業(yè)人員和潛在競爭對手, 對我來說, 別人走越多彎路, 是不是對我越有益呢? 我偶爾會思考這個問題, 也希望得到大家的答案.

----------------------------------------
注釋一: 我的答案是可以; 這需要額外的學習和外部條件, 和引入一個新模式, Gasket, 即墊片模式; 這個模式也要交換從而付出很大的代價:該模式有擴散的特性, 導致你寫程序方方面面有徹底的變化, 甚至是沒有接觸過的方式; 所以也要下功夫?qū)W很多新東西.

P.S. 不用Baidu和Google了, 該模式是我自娛自樂的一個野路子, 在經(jīng)過我自己嚴格檢驗前, 不打算發(fā)表(而且對于很多固執(zhí)的人而言肯定不愿意付出某些代價去交換), 先賣個關子吧.

作者:未知 | 文章來源:cnblog | 更新時間:2008-2-18 10:53:08

  • 上一篇文章:

  • 下一篇文章:

  • 相關文章:
    沒有相關文章
    軟件技術
    · 開發(fā)語言
    · Java技術
    · .Net技術
    · 數(shù)據(jù)庫開發(fā)
    最新文章  
    ·.net技術 asp.net MVC3 Vi
    ·.net將視頻文件格式轉(zhuǎn)換成
    ·NET Framework 2.0 Servic
    ·如何動態(tài)修改winform的app
    ·asp,net軟件結構設計和相關
    ·學習怎樣使用ASP.NET中的虛
    ·,net基礎類的學習:system
    ·.net學習:c#事件的深入分
    ·Facade Pattern學習總結
    ·C#如何設置或者獲取目錄的
    ·如何使用XSL來定義ASP.NET
    ·理解WCF Data Contract契約
    ·如何能在.net2.0開發(fā)的控件
    ·模式怎樣使用,講解模式的
    ·如何解決Menu菜單被frame遮
    關于我們 | 軟件開發(fā) | 下載試用 | 客服中心 | 聯(lián)系我們 | 友情鏈接 | 網(wǎng)站地圖 | 新疆電子地圖 | RSS訂閱
    版權所有 © 2016 新疆二域軟件開發(fā)網(wǎng) m.jdtbank.com All Rights Reserved 新ICP備14003571號
    新疆軟件開發(fā)總機:0991-4842803、4811639.
    客服QQ:596589785 ;地址:新疆烏魯木齊北京中路華聯(lián)大廈A-5C 郵編:830000
     
    射阳县| 安化县| 易门县| 台东市| 崇信县| 佛冈县| 柞水县| 鹤峰县| 宁阳县| 昌宁县| 梨树县| 孟州市| 西平县| 无极县| 万荣县| 汝阳县| 来凤县| 明水县| 阳谷县| 沂源县| 枞阳县| 利辛县| 凉山| 黔江区| 万安县| 原平市| 新蔡县| 桓仁| 宜州市| 南川市| 五原县| 大竹县| 沽源县| 白银市| 彝良县| 七台河市| 洛阳市| 西林县| 宣化县| 邯郸市| 维西|