偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

設(shè)計(jì)-直接不等于簡(jiǎn)單

開(kāi)發(fā)
本文來(lái)自hxfirefox,他是筆者在某國(guó)內(nèi)大型企業(yè)提供敏捷XP咨詢(xún)項(xiàng)目的內(nèi)部教練。本文也是由他交給筆者幫助review,同時(shí)也授權(quán)發(fā)布在筆者的博客中。

了解XP(極限編程)的人都知道,XP有一項(xiàng)實(shí)踐叫做簡(jiǎn)單設(shè)計(jì)(simple design),站在這項(xiàng)實(shí)踐對(duì)立面的是過(guò)度設(shè)計(jì)。當(dāng)我們從客戶(hù)價(jià)值的中心視角去審視那些我們遇到過(guò)的過(guò)度設(shè)計(jì),自然而然就會(huì)得出一個(gè)結(jié)論:

“又TM被那些美其名曰項(xiàng)目經(jīng)理和程序員的孫子們給忽悠了,這些功能我其實(shí)都用不到,但我還花了這么多冤枉錢(qián)去購(gòu)買(mǎi),下次議價(jià)時(shí)一定要砍掉80%的預(yù)算。”

img1

一旦得出這個(gè)結(jié)論,那么很快客戶(hù)和開(kāi)發(fā)團(tuán)隊(duì)將陷入無(wú)止境的撕逼狀態(tài),群體攻擊增強(qiáng)300%,單體理智降低80%,所以為了避免程序猿的世界被破壞,并從根本上保障碼農(nóng)群體可憐的經(jīng)濟(jì)來(lái)源,就應(yīng)當(dāng)想辦法給客戶(hù)這樣一種錯(cuò)覺(jué):

“你要的功能必須值這個(gè)價(jià),如果想要新增一個(gè)功能就應(yīng)該要額外收費(fèi)。”

對(duì)于開(kāi)發(fā)人員而言,想在這場(chǎng)博弈中獲勝的最佳方法就是砍掉那些完全只為滿(mǎn)足自我虛榮心(以此證明自己技藝是如何爐火純青)的多余設(shè)計(jì)和實(shí)現(xiàn),只完美地產(chǎn)出客戶(hù)真正需要和關(guān)心的功能,這就是簡(jiǎn)單設(shè)計(jì)。

似乎簡(jiǎn)單的直接設(shè)計(jì)

理論總是非常easy,但是,請(qǐng)注意這里的但是,由于漢字的博大精深和內(nèi)涵豐富,再遇上程序員這種伴隨二進(jìn)制進(jìn)化的只有0和1二個(gè)極端的特殊生物,“簡(jiǎn)單”一詞的含義被引申到了更廣的范圍,演化成了簡(jiǎn)單粗暴,出現(xiàn)了一種在編碼中隨處可見(jiàn)的風(fēng)景——我稱(chēng)之為直接設(shè)計(jì)(directly design)。

直接設(shè)計(jì)看上去像是一種“按圖索驥”的編程方法,開(kāi)發(fā)人員將流程圖上的處理及分支用直白的代碼表達(dá)出來(lái),比如最近在工作中遇到的一個(gè)例子:

設(shè)備對(duì)于端口的獲得信息默認(rèn)情況下需要進(jìn)行處理,當(dāng)端口被配置為A或B類(lèi)型時(shí),則該端口獲得的信息無(wú)需處理,轉(zhuǎn)化為流程圖如下。

[[144933]]

產(chǎn)生的代碼如下: 例1

@Override
public void onMsgRecvdFromPort(RecvMsg msg) {
    checkNotNull(msg);

    if (msg.getIn().getPortType() == InPortType.A) {
        doRecord();
    } else if (msg.getIn().getPortType() == InPortType.B) {
        doRecord();
    } else {
        handleMsg(msg);
    }
}

也許團(tuán)隊(duì)中有那么一兩個(gè)了解過(guò)clean code和重構(gòu)的人,那么這段代碼可能演變成如下: 例2

@Override
public void onMsgRecvdFromPort(RecvMsg msg) {
    checkNotNull(msg);

    if (msg.getIn().getPortType() == InPortType.A || msg.getIn().getPortType() == InPortType.B) {
        doRecord();
    } else {
        handleMsg(msg);
    }
}

但這還不夠,再改造一下: 例3

@Override
public void onMsgRecvdFromPort(RecvMsg msg) {
    checkNotNull(msg);

    if (!isPortTypeAOrB(msg)) {
        handleMsg(msg);
    }

    doRecord();
}

private boolean isPortTypeAOrB(RecvMsg msg) {
    return msg.getIn().getPortType() == InPortType.A || msg.getIn().getPortType() == InPortType.B;
}

現(xiàn)在看上去似乎舒服多了,代碼也好理解了,進(jìn)行到這一步代碼可以算是大的提升,但是這就結(jié)束了嗎?其實(shí)這只是轉(zhuǎn)嫁了問(wèn)題,問(wèn)題并沒(méi)有結(jié)束,因?yàn)楝F(xiàn)在 isPortTypeAOrB方法開(kāi)始變得復(fù)雜難懂起來(lái)。不論編碼資歷深淺,大多數(shù)開(kāi)發(fā)人員都寫(xiě)過(guò)類(lèi)似例1的代碼,這些直接設(shè)計(jì)總是自覺(jué)或不自覺(jué)地跑出 來(lái),像個(gè)幽靈一樣。那么這些直接設(shè)計(jì)從何而來(lái)?

審視自己的經(jīng)歷,直接設(shè)計(jì)代碼產(chǎn)生的原因有很多,歸結(jié)起來(lái)有以下幾種可能性:

  • 習(xí)慣于面向過(guò)程編程的開(kāi)發(fā)人員轉(zhuǎn)向面向?qū)ο?,慣性使然

  • 新手們被要求嚴(yán)格地按規(guī)劃的流程編碼,這是最快地讓新手熟練起來(lái)的方法

  • 開(kāi)發(fā)人員誤解了簡(jiǎn)單的含義,認(rèn)為簡(jiǎn)單就是直接,忽視了設(shè)計(jì),也即簡(jiǎn)單而不設(shè)計(jì)

人人都愛(ài)直接設(shè)計(jì),不只是開(kāi)發(fā)人員,因?yàn)槟菢硬毁M(fèi)腦力,有章可循,且按圖索驥后責(zé)任就變成了流程的設(shè)計(jì)人員,既可以輕輕松松,又能趨利避害,不這么 做似乎于情于理都很難說(shuō)過(guò)去。其實(shí)直接設(shè)計(jì)并不代表代碼質(zhì)量有問(wèn)題,相反只要意圖足夠清晰和簡(jiǎn)單,那么還是要推薦直接設(shè)計(jì),畢竟開(kāi)發(fā)人員都是這樣被教育出 來(lái)的。但是直接設(shè)計(jì)有一個(gè)很突出的缺陷——丑陋,因?yàn)榭偸菚?huì)把過(guò)多的細(xì)節(jié)暴露出來(lái),尤其是在分支處理上,就像上面的例1那樣。

也許有人覺(jué)得這樣直接挺清晰,挺容易理解,其實(shí)問(wèn)題也就在這里,現(xiàn)在這樣的分支只有兩個(gè),當(dāng)用戶(hù)覺(jué)得這樣的需求還不能滿(mǎn)足需要時(shí),就會(huì)要求更多,也許會(huì)有5個(gè),10個(gè)甚至近百個(gè)分支,那時(shí)對(duì)于開(kāi)發(fā)人員而言就要不斷地增加新的分支代碼,就像下面的代碼這樣。

@Override
public void onMsgRecvdFromPort(RecvMsg msg) {
    checkNotNull(msg);

    if (msg.getIn().getPortType() == InPortType.A) {
        doRecord();
    } else if (msg.getIn().getPortType() == InPortType.B) {
        doRecord();
    } else if (msg.getIn().getPortType() == InPortType.C) {
        doRecord();
    } else if (msg.getIn().getPortType() == InPortType.D) {
        doRecord();
    } else if (msg.getIn().getPortType() == InPortType.E) {
        doRecord();
    }
    ...
    ...
    else {
        handleMsg(msg);
    }
}

并且在新增分支時(shí)還要小心翼翼地考慮與原有分支的邏輯關(guān)系,嵌套分支看來(lái)是在所難免了,用不了幾個(gè)迭代,這些代碼就會(huì)變得一堆意大利面條。

[[144931]]

也許,萬(wàn)幸的是,功能都實(shí)現(xiàn),你幸福地點(diǎn)上一根煙,滿(mǎn)足地看著自己的杰作,突然,有個(gè)新手菜鳥(niǎo)心懷崇敬地問(wèn)你:“大牛,這段代碼是什么意思?”,你 盯著代碼半天心里嘀咕著,這TM是什么鬼,我怎么也看不懂了,然后只好敷衍地回答一句“這個(gè)不明白嗎?回去看看設(shè)計(jì)文檔!”,好不容易打發(fā)走了這個(gè)新手, 項(xiàng)目經(jīng)理找到了你,告訴了你一個(gè)晴天霹靂,客戶(hù)又改需求了,可能又要新增十幾個(gè)分支,你眼前一黑,感嘆一聲又要加班了,但又不得不重新重頭解讀一遍自己創(chuàng) 作的一切,看看哪里能夠插入一個(gè)新需求,于是加班又開(kāi)始了。

bad_condition

簡(jiǎn)單設(shè)計(jì)需要設(shè)計(jì)

直截了當(dāng)?shù)卦O(shè)計(jì)過(guò)多地暴露細(xì)節(jié)造成擴(kuò)展性和維護(hù)性也直截了當(dāng)?shù)叵陆?,這種結(jié)局是所有開(kāi)發(fā)人員都努力想避免的,如此看來(lái)簡(jiǎn)單設(shè)計(jì)并不簡(jiǎn)單,關(guān)鍵是設(shè)計(jì),因?yàn)楹?jiǎn)單設(shè)計(jì)更需要設(shè)計(jì),套用一句經(jīng)典的廣告語(yǔ):簡(jiǎn)約而不簡(jiǎn)單,這才是簡(jiǎn)單設(shè)計(jì)想到達(dá)到的目的?,F(xiàn)在試著重新解讀簡(jiǎn)單設(shè)計(jì),個(gè)人認(rèn)為簡(jiǎn)單設(shè)計(jì)原則可以分成三個(gè)層次:

  • 實(shí)現(xiàn)具有用戶(hù)價(jià)值的需求,簡(jiǎn)單的說(shuō)就是用戶(hù)要什么你就給他什么

  • 代碼設(shè)計(jì)應(yīng)當(dāng)職責(zé)簡(jiǎn)單,簡(jiǎn)單地說(shuō)就是做好一件事

  • 設(shè)計(jì)應(yīng)盡可能針對(duì)一到兩個(gè)問(wèn)題展開(kāi),做到即設(shè)計(jì)要簡(jiǎn)單,足夠針對(duì)性的解決問(wèn)題即可

讓我們看看從上面角度怎么來(lái)設(shè)計(jì),仍然以上面的例子為例。根據(jù)這個(gè)原則,將上述需求實(shí)例化,可以得到:

  • when port type == A, it should not handle message

  • when port type == B, it should not handle message

  • when port type != A && != B, it should handle message

將端口類(lèi)型進(jìn)行歸納,可以發(fā)現(xiàn)其實(shí)端口是否處理消息由端口類(lèi)型決定,一種端口類(lèi)型是不需要處理消息類(lèi)型,而另一種則是需要處理類(lèi)型,因此端口消息處 理只需要關(guān)心哪些端口是屬于需要處理的類(lèi)型即可。從這點(diǎn)出發(fā)可以看出例1做了太多可以委托他人去做的事情,因此設(shè)計(jì)上需要考慮將功能分離,特別是判斷邏輯 與功能主體剝離,使得單個(gè)主體的功能盡量簡(jiǎn)單來(lái)滿(mǎn)足簡(jiǎn)單設(shè)計(jì)的第二條原則,按照上述思路,轉(zhuǎn)化為如下代碼:

@Override
public void onMsgRecvdFromPort(RecvMsg msg) {
    checkNotNull(msg);
    ParseMsg(msg);
}

private void ParseMsg(RecvMsg msg) {
    if (!filter(msg)) { // only ports not in disabled list could be parsed
        handleMsg(msg);
    }
    doRecord();
}

private boolean filter(RecvMsg msg) {
    return DisabledPortFilter.getInstance().contains(msg.getIn());
}

而DisabledPortFilter負(fù)責(zé)管理禁用端口,提供注冊(cè)及過(guò)濾功能,如下:

public class DisabledPortFilter {
    // FilterRule in HashMap means rule for filting with port
    // Sometimes you need to composite multi-conditions to filting, not only type of port
    // FilterRule is an interface, so any one wants to use filter should offer an implementation
    private HashMap<InPort, FilterRule> disableHandleList = Maps.newHashMap();
    private static DisabledPortFilter portFilter = new DisabledPortFilter();

    private DisabledPortFilter() {
    }

    public static DisabledPortFilter getInstance() {
        return portFilter;
    }

    public void registDisabledPort(InPort inPort, FilterRule rule) {
        disableHandleList.put(inPort, rule);
    }

    public void unregistDisabeldPort(InPort inPort) {
        disableHandleList.remove(inPort);
    }

    public boolean contains(InPort in) {
        return !disableHandleList.get(in).matchFilter(in);
    }
}

FilterRule定義如下:

public interface FilterRule {
    public boolean matchFilter(InPort inPort);
}

將例1中在一個(gè)方法中執(zhí)行的過(guò)程分解到多個(gè)類(lèi)中,每個(gè)類(lèi)的職責(zé)更為單一,將復(fù)雜的過(guò)濾邏輯通過(guò)轉(zhuǎn)化放在各個(gè)實(shí)現(xiàn)類(lèi)中,也可以幫助開(kāi)發(fā)者及維護(hù)者能夠 在某一時(shí)間點(diǎn)只關(guān)注其中某一中過(guò)濾規(guī)則。完成上述轉(zhuǎn)化后,原來(lái)可能冗余繁復(fù)的分支處理消失了,取而代之的是短短的幾行簡(jiǎn)單易懂的代碼。并且轉(zhuǎn)化后還帶來(lái)了 維護(hù)上的便利與代碼擴(kuò)展性的提升,當(dāng)客戶(hù)新增需求時(shí),只需要增加對(duì)應(yīng)的FilterRule實(shí)現(xiàn),并注冊(cè)到DisabledPortFilter中就可 以,而不用去修改原有代碼,不知不覺(jué)中又契合了OCP原則。 對(duì)照前后例子,發(fā)生變化原因是針對(duì)邏輯判斷與功能主體分離這一點(diǎn)問(wèn)題進(jìn)行了設(shè)計(jì),后面的設(shè)計(jì)都是在此基礎(chǔ)上展開(kāi),一次只設(shè)計(jì)一個(gè)切入點(diǎn)使得開(kāi)發(fā)人員更容易 控制開(kāi)發(fā)思路,而不至于過(guò)多復(fù)雜的設(shè)計(jì)帶來(lái)的思維混亂,因此簡(jiǎn)單設(shè)計(jì)原則中的第三條顯得尤為重要,很多時(shí)候是我們自己想的太多而導(dǎo)致停滯不前,舉步維艱。

簡(jiǎn)單設(shè)計(jì)之路

簡(jiǎn)單設(shè)計(jì)是一條光明大道,但通向簡(jiǎn)單設(shè)計(jì)的路卻并不簡(jiǎn)單,布滿(mǎn)荊棘,很多時(shí)候并非我們不知道簡(jiǎn)單設(shè)計(jì),而是在一次次與時(shí)間、進(jìn)度博弈的過(guò)程中自覺(jué)或 不自覺(jué)地放棄了簡(jiǎn)單設(shè)計(jì),不少簡(jiǎn)單設(shè)計(jì)只需要我們?cè)俣嘞肽敲匆稽c(diǎn)點(diǎn),捅破這層窗戶(hù)紙并不難,要做的只是多想一點(diǎn),多看一眼,往往這片刻的思考就會(huì)對(duì)我們的 編碼產(chǎn)生巨大的影響,這也正是通向簡(jiǎn)單設(shè)計(jì)道路上唯一可以依靠的工具,你要做的只是多想一點(diǎn),多看一眼。

責(zé)任編輯:王雪燕 來(lái)源: hxfirefox/blog
相關(guān)推薦

2012-02-03 14:39:12

Java

2015-12-01 10:42:07

2021-09-06 15:29:16

大數(shù)據(jù)防疫信息安全

2010-04-28 14:38:26

云計(jì)算

2010-10-18 10:51:00

蘋(píng)果

2017-06-29 08:45:06

MySQLNOT INNOT EXISTS

2025-06-04 03:25:00

Java浮點(diǎn)數(shù)數(shù)學(xué)缺陷

2011-08-08 09:59:35

Android

2023-03-07 07:45:28

2023-06-02 13:53:56

2010-07-19 11:12:43

Perl 不等于

2013-11-26 09:55:12

2012-11-14 10:04:17

2012-11-14 09:54:14

2009-03-11 11:36:31

技術(shù)人員角色感想

2012-11-12 14:27:56

2012-04-17 09:27:21

工信部

2013-08-15 09:15:23

云計(jì)算CIO信息化

2010-01-28 10:25:12

2023-09-28 12:13:36

自然語(yǔ)言模型
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)