<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      小例子背后的大道理——用戶需求+設(shè)計原則+正確應(yīng)用 =設(shè)計方案

      上回問題回顧

          上回的最后,來了兩個用戶,分別提出了兩個不同的需求。一個要求用兩個開關(guān)控制一個燈,一個要求用一個開關(guān)控制所有的燈。本回將就這兩個需求進(jìn)行分析。我寫這段話的時候并沒有想出這個需求的具體方案,重要的過程,思路有時候比結(jié)果更重要。所以,我的方案可能會"跑偏";但是如果你能從過程中體會到些什么,那這篇就沒有白寫。

       

          兩個開關(guān)控制一個燈。這個問題好像很簡單,把兩個Switcher的Switchee都設(shè)置為同一個燈不就結(jié)了嗎?畫個對象圖會是這個樣子。

       clip_image002

      圖1 由雙開關(guān)控制的燈

          有問題嗎?

      用戶的真實(shí)需求

           考慮一下這個問題。如果你用Switcher1了燈,再去一下Switcher2,燈應(yīng)該是保持開著還是關(guān)了呢?從技術(shù)人員的角度來講,調(diào)用的Switcher的開,當(dāng)然應(yīng)該保持開啦。但是策劃會說,這兩個開關(guān)應(yīng)該是相互作用的,還拿出了電路圖給我看。這是的確是張真實(shí)情況下的雙開關(guān)電路圖。

      圖2 雙路開關(guān)電路

           Switcher1的開關(guān),撥到左邊是開還是關(guān),取決于Switcher2現(xiàn)在是撥在左邊兒還是右邊。電路圖的天然連通性就自然而然地做到了這一點(diǎn)。現(xiàn)實(shí)中的Switcher1不會去問Switcher2:嘿,哥們,你現(xiàn)在是個啥狀態(tài)?而我們的代碼中的兩個Switcher間也不應(yīng)該有什么交集。

           總而言之,在這個需求的要求下,用戶要做的,就是撥一下開關(guān)而已(圖3中JustSwitch方法的作用)。

      對當(dāng)前設(shè)計的改進(jìn)

           在以上需求的約束下,就第一篇開始所寫的Switcher而言,就會存在著一個問題。先不說雙開關(guān),單單一個開關(guān)我們的設(shè)計就是不符合產(chǎn)品策劃的要求。因?yàn)橹皩懙腟witcher類是有兩個函數(shù)作開關(guān)控制的。

      public class Switcher

      {

          public ISwitchable Switchee { get; set; }

          public void TurnOn() { Switchee.TurnOn(); }

          public void TurnOff() { Switchee.TurnOff(); }

      }

      代碼1

           這是有問題的。因?yàn)镾witcher是直接給用戶用的。你覺得用戶是想用哪種開關(guān)呢?

      還是 呢?

           總不能讓用戶根據(jù)現(xiàn)在燈是開著還是關(guān)著讓用戶按不同的按鈕。(使用不同的函數(shù)。)所以Switcher的代碼應(yīng)該是這個樣子的。

      public class Switcher

      {

          private bool isOn;

          public ISwitchable Switchee { get; set; }

       

        public void JustSwitch()

        {

          // 根據(jù)當(dāng)前狀態(tài)選擇正確的操作。

          if (isOn)

          {

              Switchee.TurnOff();

              isOn = false;

          }

          else

          {

              Switchee.TurnOn();

              isOn = true;

          }

        }

      }

      代碼2

           Switcher自己保存最后一次操作的結(jié)果(當(dāng)前狀態(tài)),并自動選擇正確的操作。

      支持雙開關(guān)

           當(dāng)每個燈只有一個開關(guān)的時候,這個代碼沒有任何問題。但是出現(xiàn)兩個開關(guān)的話就沒這么好辦了,自己保存的狀態(tài)是無效的,可能會被另一個開關(guān)改掉。如果要達(dá)到和電路圖一樣的效果,Switcher1要么問Switcher2現(xiàn)在是什么狀態(tài),要么問Light是什么狀態(tài)。

           直覺上,問Switcher2這事兒不是個好選擇,因?yàn)橐院筮€可能會有Switcher3、4。但燈就一個。但是等等,我們現(xiàn)在的接口是什么樣的?

       clip_image002[6]

      圖3. 現(xiàn)在的設(shè)計

            ISwitchable接口只定義了TurnOn和TurnOff兩個函數(shù),沒有可以用于查詢燈的當(dāng)前狀態(tài)的方法。這太糟糕了,這意味著接口要改了。改接口永遠(yuǎn)是最糟糕的事情。《軟件框架設(shè)計的藝術(shù)》里說"API就如同恒星,一旦出現(xiàn),便與我們永恒存在。",聽上去接口寫了就不能改,但是我們的情況要好很多,這個接口是公司自己定義的,沒有別人用過。所以改改無妨。J只要小小的加一個方法就可以了。

      clip_image002[8] 

      圖4. 添加查詢接口以支持雙開關(guān)

            Switcher的代碼會是這樣的。Switcher暴露給用戶的應(yīng)該只有 一個接口。

      public interface ISwitchable

      {

          void TurnOn();

          void TurnOff();

          bool IsOn();

      }

       

      public class Switcher

      {

          public ISwitchable Switchee { get; set; }

       

          public void JustSwitch()

          {

              // 根據(jù)當(dāng)前狀態(tài)選擇正確的操作。

              if (Switchee.IsOn())

                  Switchee.TurnOff();

              else

                  Switchee.TurnOn();

          }

      }

      代碼3

      另一個極品方案

           軟件開發(fā)與建筑施工的最大區(qū)別是,軟件開發(fā)可以選擇先蓋地下室還是天花板。

      ——我

           當(dāng)我們把要做的事情抽象一下,就能很容易地從更高的層次思考問題。比如上面,開關(guān)要知道燈的狀態(tài)。可以抽象為:

      圖5. 開關(guān)開燈例子的高度抽象

           各位可以想到什么設(shè)計上的問題?比較明顯的問題有兩個。

      • 拉模式 VS推模式。既然圖中為拉模式,那么另一個思路就是推模式。也許你聽說一個說法,就是推模式比拉模式要好。但是如果真把推模式用在開關(guān)開燈的例子上,就成了燈的亮與熄,要去通知開關(guān),以便開關(guān)下次Switch的時候,能做出正確地動作。想到這里,我邪惡地笑了。這得多蛋痛啊?模式的應(yīng)用,永遠(yuǎn)要看上下文。為了撫慰一些推模式死忠們脆弱的心靈,下面會介紹一個可行的推模式開燈設(shè)計。
      • A依賴B。雖然我們有ISwitchable接口,開關(guān)不直接依賴燈,但是你看,我們?yōu)槭裁匆贗Switchable接口里加入IsOn函數(shù)呢?因?yàn)殚_關(guān)需要知道燈的狀態(tài)。所以說,他們之間不但存在著依賴,而且還直接決定了接口的定義。但是與我第一篇文章中介紹的DIP原則是否沖突呢?這取決于你對開關(guān)的定位。如果只是單純的開關(guān),那么IsOn函數(shù)的引入,就是對燈的功能的抽象,也就違反了DIP原則;如果你希望開關(guān)有點(diǎn)兒AI,那么顯然它得知道更多的信息(但是這違反了單一責(zé)任原則)。所以看上去,無論從哪個角度來講,IsOn的引入都是要違反XXXX原則的。

           好,為了不違反所有現(xiàn)有的原則。構(gòu)想出設(shè)計出如下的設(shè)計:

       clip_image002[10]

      圖6. 引入AI系統(tǒng)對開關(guān)操作進(jìn)行決策(拉模式)

       

           理智一些吧,我們的開關(guān)公司沒有上市,既沒有資本做AI系統(tǒng),也沒有卡馬克這種不要錢只要漢堡和網(wǎng)絡(luò)的技術(shù)狂人,我們的用戶也不會像暗黑的死忠一樣傻等10年,然后等到一個需要接網(wǎng)線才能使用的電燈開關(guān)還能用得很愉悅。在這個發(fā)展階段要做的,只是盡快滿足當(dāng)前的需求。

           在達(dá)成需求前,技術(shù)方案的完美度,永遠(yuǎn)是第二位的。第一位的,是有效率地執(zhí)行 + 新穎的思路和方向。思路放后面,是因?yàn)閷?9.9%的情況來說,最不值錢的就是點(diǎn)子,你能想到的,別人可能都已經(jīng)做出來了。即使是Jobs這種用新意折服世界的人,也要靠"現(xiàn)實(shí)扭曲力場"的幫助把自己的觀點(diǎn)有效地推行下去。

      所以,這個方案雖然很不錯,但是我不愿意繼續(xù)討論了,因?yàn)檫@在現(xiàn)實(shí)中沒有意義,脫離現(xiàn)實(shí)的例子也就不再是好例子了。

           我還想說一句,做項(xiàng)目和做人,都不能走極端。另一個極端是:以敏捷之名,無視一切編碼前的設(shè)計。我猜這在群人眼中,這個系列的文章沒有任何意義。

      ———————————————————牢騷的休止符—————————————————————

      再一個方案

           有人可能會說,讓燈自己控制自己的狀態(tài),也可以解決問題。像下面這樣。

       clip_image002[12]

      圖7 另一種解決方案

           然后把Light類實(shí)現(xiàn)成這個樣子:(多酷啊,目前為止代碼量最少的方案)

      public class Light : ISwitchable

      {

          private bool isGlowing;

       

          public void JustSwitch()

          {

              isGlowing = !isGlowing;

          }

      }

      代碼 4

           這個設(shè)計的確可行,但是哪個方案更好呢?這個問題就留給各位讀者吧。就拿幾個Principle逐個分析下應(yīng)該就可以分析出個所以然來。(下一節(jié)有簡單提示)

      設(shè)計思想(原則)及技術(shù)方案的濫用

            每種設(shè)計都有他的思路和道理。你覺得不可理喻的設(shè)計可能恰恰是別人深思熟慮的結(jié)果,只是每個人的思路不一樣,結(jié)果自然也不相同。但是如果設(shè)計思路被某種設(shè)計思想占據(jù)了絕對主導(dǎo)的地位,就可能會出現(xiàn)設(shè)計上的偏差。

           我把濫用大體上總結(jié)成如下三種:

      1. 單純的濫用。因?yàn)槲視@么做,所以就順便這么做了。他們的理論基礎(chǔ)是:雖然現(xiàn)在沒有這要的需求,誰知道以后有沒有呢?我多做一點(diǎn)兒還不好?最典型的癥狀是,所有的類,都有相應(yīng)的接口,全部使用Dependency Injection來實(shí)例化。這不是有病么?

             這會引出一個比較大的話題,就是怎樣的設(shè)計算是過度設(shè)計?這需要單獨(dú)寫一節(jié)來討論這個問題。就不在這里展開了。

      2. 程度上的濫用。圖7的設(shè)計,就體現(xiàn)了一個叫做"Tell, Don't Ask"的原則,或者說是為了將這個原則"發(fā)揮到極致"而形成的設(shè)計。而在這個原則之下寫出的代碼4又是如此簡潔和優(yōu)美;以至于讓想出這個方案的人,很難主動拋棄這個方案。直到看到這個方案不能滿足的需求才肯承認(rèn)問題。
      3. 適用范圍濫用。把某個原則或是技術(shù)方案當(dāng)萬金油。只要能用得上,就一定要用上一用。導(dǎo)致一葉蔽目,不愿意尋求其它更加合適的技術(shù)方案(項(xiàng)目時間緊是個最常見的借口,一知道某個方案可行,就馬上付諸行動)。一時興起,畫了個漫畫

      圖8. 因?yàn)槭煜せ驊卸瓒峤筮h(yuǎn)

            第一篇就說過,優(yōu)秀的設(shè)計不是藉由幾個原則、模式就可以保證的。何況是某一個原則呢?物極必反。今天就不再啰嗦了。大家也都懂。設(shè)計過程中對某個特定的設(shè)計思想或是技術(shù)過于執(zhí)著,往往會形成一個雖可行、卻畸形的設(shè)計。圖7即是一個例子。

      一個真實(shí)的案例

           有一家著名的咨詢公司,2009年接了一個銀行的大單,為期一年,預(yù)計可以賺到五個億。但是這個項(xiàng)目現(xiàn)在都還沒有結(jié)束,項(xiàng)目延期不僅要給銀行賠償,還要繼續(xù)免費(fèi)給銀行把這個項(xiàng)目做完。(想想國內(nèi)公司會怎么做?)2010-2011年度,公司接的其它項(xiàng)目賺的錢幾乎全部貼給了這個項(xiàng)目。當(dāng)年全公司員工沒有獎金,部分相關(guān)高層降職降薪。

           為什么?一個可以賺到五個億的項(xiàng)目卻虧了幾個億?

           目前項(xiàng)目內(nèi)員工的工作效率極低,一個普通開發(fā)者要兩天才能完成一個報表的修改。(現(xiàn)階段是修改,不是全新開發(fā))。所以說人員成本非常大。項(xiàng)目拖上一個月,數(shù)百萬就打了水漂。

           那么效率為什么這么低?他們所有的業(yè)務(wù)邏輯都用PL-SQL實(shí)現(xiàn),大的報表,涉及到的PL-SQL動輒上萬行,而且層層調(diào)用,加之整個系統(tǒng)有數(shù)千個表。代碼的測試,都要先去數(shù)據(jù)庫造假數(shù)據(jù)。效率能高就怪了。

           為什么要這么搞?因?yàn)橐婚_始做系統(tǒng)設(shè)計的人,對PL-SQL比較熟悉,對Java不熟悉,所以就把Java當(dāng)成了UI Wrapper來用。狗屎吧。當(dāng)然還會有很多其它的因素,但是在技術(shù)層面,這絕對是重要因素之一。因?yàn)樽约簩δ硞€技術(shù)比較熟悉,而不愿意在項(xiàng)目中了解和應(yīng)用更適合的其它技術(shù)方案的人套上CTO之類的外衣,恒等于攪屎棍。

      支持開關(guān)控制多個燈

           簡單而言,要讓一個開關(guān)去控制所有的燈。聽上去很簡單,而且有很多種實(shí)現(xiàn)方式。但是如果仔細(xì)想想,會發(fā)現(xiàn)有很多問題。

      需求分析,不同于簡單的需求整理

           用戶給出的需求總會是很概括甚至模糊的,不是用戶懶得說,而是用戶覺得自己已經(jīng)說清楚了。以開燈為例,用戶需求就是:"要有一個統(tǒng)一開關(guān)。",你如果再去追問用戶,要怎么個統(tǒng)一法?用戶可能就會不高興了,因?yàn)樗X得這是你應(yīng)該解決的問題。但是如果簡單地把"要有統(tǒng)一開關(guān)"這個用戶需求直接寫進(jìn)需求文檔的話,就等著項(xiàng)目失敗或是延期吧。

          需求分析的第一步,就是要對用戶的需求進(jìn)行分解、細(xì)節(jié),找出合理用例。比如:用戶要求的是,要有統(tǒng)一開關(guān),但是并沒有說每個燈就沒有自己的開關(guān)。如果每個燈又都有自己的開關(guān),統(tǒng)一開關(guān)應(yīng)該如何與各個專屬開關(guān)協(xié)作呢?從這個角度,就可以找到一些用例。

      1. 兩個燈,都開著。這時去按統(tǒng)一開關(guān),應(yīng)該是全關(guān)對吧。也就是說,統(tǒng)一開關(guān)應(yīng)該知道當(dāng)前燈的狀態(tài)。并按當(dāng)前燈的狀態(tài)去執(zhí)行操作。
      2. 三個燈,兩個開著。這時去按統(tǒng)一開關(guān)呢?統(tǒng)一開關(guān)的意思就是要有統(tǒng)一的行為,用戶肯定不會希望這個統(tǒng)一開關(guān)的行為是:把開著的燈關(guān)掉,把關(guān)著的燈打開。怎么辦?統(tǒng)計現(xiàn)在開著的比率?開著的多就全關(guān)?那如果是兩個燈,一個開著,一個關(guān)著呢?還有一個辦法是,讓統(tǒng)一開關(guān)使用代碼2的方案:自己記住上次的操作結(jié)果。上次是全關(guān),這次就全開;反之亦然。

            也就是說,用例1和用例2都是合理的,但卻是沖突的。這種沖突甚至是一種邏輯上的沖突,已經(jīng)不是技術(shù)局限性的問題了。這種情況,在軟件開發(fā)中也是很常見的。這時,我們拿著自己的分析、自己的想法去詢問用戶的意見,用戶就會很樂意了。人們都喜歡做選擇題,而不是做問答題。不是么?

      現(xiàn)實(shí)中的電路

           現(xiàn)實(shí)中的電路圖有兩種做法。(非標(biāo)準(zhǔn)電路,請意會。強(qiáng)弱電相關(guān)專業(yè)也許可以參考這里

       clip_image002[14]clip_image002[16]

      圖9. 現(xiàn)實(shí)中的兩種簡單的總控加分支開關(guān)電路

           前一張圖,分支開關(guān)有絕對的控制權(quán);后一張圖中開關(guān)中有一個二極管,開關(guān)的開合用于控制二極管的極性,總開關(guān)的作用就是:把開著的關(guān)掉,把關(guān)著的打開。

           看上去就是兩種開關(guān)嘛。

      基于派生類的方案

           為了讓一個開關(guān)可以控制多個燈,對現(xiàn)有程序設(shè)計上的改動很小。類圖如下:

       clip_image002[18]

      圖10. 多控開關(guān)設(shè)計

           通過派生新的Switcher,來提供不同的功能。在當(dāng)前的需求下,這個方案是可行的。未來的需求,就留給未來去解決吧。

      小結(jié)

            本節(jié)本來是想講講需求決定設(shè)計這個理念,從兩個需求引出兩個互不兼容的設(shè)計方案。所以找了兩個需求一起講,但是最后這兩個需求并沒多大的沖突,也就沒有達(dá)到預(yù)期的目標(biāo)。不過想說的倒是說出來了。就是項(xiàng)目的設(shè)計,最終還是要依賴于需求,任何要做出能夠適應(yīng)未來需求的設(shè)計的想法,都是不切實(shí)際、勞民傷財?shù)摹?/strong>(不知道有多少人會把這句話曲解為"設(shè)計無用論",如果我認(rèn)為設(shè)計無用,還會寫這個系列嗎?)

            這個系列并不是要講設(shè)計模式,也不是用小例子做各種分析。只是想通過最簡單的例子講一些大道理(原則)。如果沒有合適的大道理可說,分析出一百個需求、做出一百個設(shè)計,也只是魚,而非我想說的漁。

            距上節(jié)的發(fā)布也已經(jīng)很久了,因?yàn)槊炕匕l(fā)表時,都會先想好下回寫什么、怎么寫,并結(jié)尾留個引子。這次很可惜,想好了下回寫什么,卻死活想不出合適的、簡單的需求來引出這個問題,也就不知道怎么寫了。所以下回什么能寫好我也不知道。大體上,也許還會有下而一些道理希望能和大家分享:

      • 過度設(shè)計及關(guān)于"過度"的度量。
      • 局部設(shè)計最優(yōu)化與全局次優(yōu)化之間的權(quán)衡。
      • 什么是設(shè)計經(jīng)驗(yàn)及如何正確地借鑒。

      新的用戶

           下一回講哪個還沒有講好,但是用戶的需求卻如潮水般涌來。先權(quán)且記下:

      • 在開關(guān)上加一個小燈,表示現(xiàn)在的燈是開還是關(guān)。(因?yàn)殚_關(guān)和燈可能并不在一起)
      • 通過開關(guān)調(diào)節(jié)燈的亮度。
      • 在開關(guān)上顯示燈的耗電量、溫度、預(yù)期壽命等。
      • 定時開關(guān)。
      • 開關(guān)聲控、手勢控。
      • 開關(guān)權(quán)限控制。

      posted on 2012-08-18 16:20  南柯之石  閱讀(14463)  評論(30)    收藏  舉報

      導(dǎo)航

      主站蜘蛛池模板: 国产日韩精品视频无码| 国产精品美女一区二区三| 久章草在线毛片视频播放| 欧美成人精品三级网站| 亚洲国产日韩一区三区| 中文字幕av无码不卡| 亚洲日韩av无码| av无码精品一区二区三区| a在线观看视频在线播放| 久久99精品久久久大学生| 国产成人高清亚洲综合| 性欧美乱熟妇xxxx白浆| 成年在线观看免费人视频| 色欲av无码一区二区人妻| 日本狂喷奶水在线播放212| 不卡一区二区三区四区视频 | 久热伊人精品国产中文| 亚洲熟妇久久精品| 熟女人妻精品一区二区视频| 四虎永久在线精品免费看| japanese无码中文字幕| 国产精品成人va在线播放| 色偷偷亚洲女人天堂观看| 欧美乱码伦视频免费| 色综合久久中文综合网| 一区二区三区午夜福利院| 2019亚洲午夜无码天堂| 亚洲欧美日韩精品成人| 激情综合网一区二区三区| 国产精品 第一页第二页| 亚洲色最新高清AV网站| 久久国产精品老女人| 99久久亚洲综合精品成人| 性色av无码久久一区二区三区| 亚洲精品天堂在线观看| 人妻激情偷一区二区三区| 成人AV专区精品无码国产| 18禁无遮挡啪啪无码网站破解版| 欧美成人www免费全部网站| 国产老女人免费观看黄A∨片| 亚洲精品漫画一二三区|