<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                6.2 開閉原則的廬山真面目 開閉原則的定義已經非常明確地告訴我們:軟件實體應該對擴展開放,對修改關閉,其含義是說一個軟件實體應該通過擴展來實現變化,而不是通過修改已有的代碼來實現變化。那什么又是軟件實體呢?軟件實體包括以下幾個部分: ● 項目或軟件產品中按照一定的邏輯規則劃分的模塊。 ● 抽象和類。 ● 方法。 一個軟件產品只要在生命期內,都會發生變化,既然變化是一個既定的事實,我們就應該在設計時盡量適應這些變化,以提高項目的穩定性和靈活性,真正實現“擁抱變化”。開閉原則告訴我們應盡量通過擴展軟件實體的行為來實現變化,而不是通過修改已有的代碼來完成變化,它是為軟件實體的未來事件而制定的對現行開發設計進行約束的一個原則。我們舉例說明什么是開閉原則,以書店銷售書籍為例,其類圖如圖6-1所示。 ![](https://box.kancloud.cn/2016-08-14_57b0035f75cdc.jpg) 圖6-1 書店售書類圖 IBook定義了數據的三個屬性:名稱、價格和作者。小說類NovelBook是一個具體的實現類,是所有小說書籍的總稱,BookStore指的是書店,IBook接口如代碼清單6-1所示。 代碼清單6-1 書籍接口 public?interface?IBook?{??? ?????//書籍有名稱 ?????public?String?getName(); ?????//書籍有售價 ?????public?int?getPrice(); ?????//書籍有作者 ?????public?String?getAuthor(); } 目前書店只出售小說類書籍,小說類如代碼清單6-2所示。 代碼清單6-2 小說類 public?class?NovelBook?implements?IBook?{ ?????//書籍名稱 ?????private?String?name;??????? ?????//書籍的價格 ?????private?int?price;? ?????//書籍的作者 ?????private?String?author;????????????? ?????//通過構造函數傳遞書籍數據 ?????public?NovelBook(String?_name,int?_price,String?_author){ ?????????????this.name?=?_name; ?????????????this.price?=?_price; ?????????????this.author?=?_author; ?????}?? ?????//獲得作者是誰 ?????public?String?getAuthor()?{ ?????????????return?this.author; ?????} ?????//書籍叫什么名字 ?????public?String?getName()?{ ?????????????return?this.name; ?????} ?????//獲得書籍的價格 ?????public?int?getPrice()?{ ?????????????return?this.price; ?????} } 注意 我們把價格定義為int類型并不是錯誤,在非金融類項目中對貨幣處理時,一般取2位精度,通常的設計方法是在運算過程中擴大100倍,在需要展示時再縮小100倍,減少精度帶來的誤差。 書店售書的過程如代碼清單6-3所示。 代碼清單6-3 書店售書類 public?class?BookStore?{ ?????private?final?static?ArrayList<IBook>?bookList?=?new?ArrayList<IBook>(); ?????//static靜態模塊初始化數據,實際項目中一般是由持久層完成 ?????static{ ?????????????bookList.add(new?NovelBook("天龍八部",3200,"金庸")); ?????????????bookList.add(new?NovelBook("巴黎圣母院",5600,"雨果")); ?????????????bookList.add(new?NovelBook("悲慘世界",3500,"雨果")); ?????????????bookList.add(new?NovelBook("金瓶梅",4300,"蘭陵笑笑生")); ?????} ?????//模擬書店買書 ?????public?static?void?main(String[]?args)?{ ?????????????NumberFormat?formatter?=?NumberFormat.getCurrencyInstance(); ?????????????formatter.setMaximumFractionDigits(2); ?????????????System.out.println("-----------書店賣出去的書籍記錄如下:-----------"); ?????????????for(IBook?book:bookList){ ?????????????????????System.out.println("書籍名稱:"?+?book.getName()+"\t書籍作者:"?+ ????????book.getAuthor()+"\t書籍價格:"+?formatter.format?(book.getPrice()/ ????????100.0)+"元"); ?????????????} ?????} } 在BookStore中聲明了一個靜態模塊,實現了數據的初始化,這部分應該是從持久層產生的,由持久層框架進行管理,運行結果如下: -----------------書店賣出去的書籍記錄如下:-------------- 書籍名稱:天龍八部  書籍作者:金庸    書籍價格:¥25.60元 書籍名稱:巴黎圣母院 書籍作者:雨果    書籍價格:¥50.40元 書籍名稱:悲慘世界  書籍作者:雨果    書籍價格:¥28.00元 書籍名稱:金瓶梅    書籍作者:蘭陵笑笑生 書籍價格:¥38.70元 項目投產了,書籍正常銷售出去,書店也贏利了。從2008年開始,全球經濟開始下滑,對零售業影響比較大,書店為了生存開始打折銷售:所有40元以上的書籍9折銷售,其他的8折銷售。對已經投產的項目來說,這就是一個變化,我們應該如何應對這樣一個需求變化?有如下三種方法可以解決這個問題: ● 修改接口 在IBook上新增加一個方法getOffPrice(),專門用于進行打折處理,所有的實現類實現該方法。但是這樣修改的后果就是,實現類NovelBook要修改,BookStore中的main方法也修改,同時IBook作為接口應該是穩定且可靠的,不應該經常發生變化,否則接口作為契約的作用就失去了效能。因此,該方案否定。 ● 修改實現類 修改NovelBook類中的方法,直接在getPrice()中實現打折處理,好辦法,我相信大家在項目中經常使用的就是這樣的辦法,通過class文件替換的方式可以完成部分業務變化(或是缺陷修復)。該方法在項目有明確的章程(團隊內約束)或優良的架構設計時,是一個非常優秀的方法,但是該方法還是有缺陷的。例如采購書籍人員也是要看價格的,由于該方法已經實現了打折處理價格,因此采購人員看到的也是打折后的價格,會因信息不對稱而出現決策失誤的情況。因此,該方案也不是一個最優的方案。 ● 通過擴展實現變化 增加一個子類OffNovelBook,覆寫getPrice方法,高層次的模塊(也就是static靜態模塊區)通過OffNovelBook類產生新的對象,完成業務變化對系統的最小化開發。好辦法,修改也少,風險也小,修改后的類圖如圖6-2所示。 ![](https://box.kancloud.cn/2016-08-14_57b0035fb6f4f.jpg) 圖6-2 擴展后的書店售書類圖 OffNovelBook類繼承了NovelBook,并覆寫了getPrice方法,不修改原有的代碼。新增加的子類OffNovelBook如代碼清單6-4所示。 代碼清單6-4 打折銷售的小說類 public?class?OffNovelBook?extends?NovelBook?{ ?????public?OffNovelBook(String?_name,int?_price,String?_author){ ?????????????super(_name,_price,_author); ?????} ?????//覆寫銷售價格 ?????@Override ?????public?int?getPrice(){ ?????????????//原價 ?????????????int?selfPrice?=?super.getPrice(); ?????????????int?offPrice=0; ?????????????if(selfPrice>4000){??//原價大于40元,則打9折 ?????????????????????offPrice?=?selfPrice?*?90?/100; ?????????????}else{ ?????????????????????offPrice?=?selfPrice?*?80?/100; ?????????????} ?????????????return?offPrice; ?????} } 很簡單,僅僅覆寫了getPrice方法,通過擴展完成了新增加的業務。書店類BookStore需要依賴子類,代碼稍作修改,如代碼清單6-5所示。 代碼清單6-5 書店打折銷售類 public?class?BookStore?{ ?????private?final?static?ArrayList<IBook>?bookList?=?new?ArrayList<IBook>(); ?????//static靜態模塊初始化數據,實際項目中一般是由持久層完成 ?????static{ ?????????????bookList.add(new?OffNovelBook("天龍八部",3200,"金庸")); ?????????????bookList.add(new?OffNovelBook("巴黎圣母院",5600,"雨果")); ?????????????bookList.add(new?OffNovelBook("悲慘世界",3500,"雨果")); ?????????????bookList.add(new?OffNovelBook("金瓶梅",4300,"蘭陵笑笑生")); ?????}?? ?????//模擬書店買書 ?????public?static?void?main(String[]?args)?{ ?????????????NumberFormat?formatter?=?NumberFormat.getCurrencyInstance(); ?????????????formatter.setMaximumFractionDigits(2); ?????????????System.out.println("-----------書店賣出去的書籍記錄如下:-----------"); ?????????????for(IBook?book:bookList){ ??????????????????????System.out.println("書籍名稱:"?+?book.getName()+"\t書籍作者:"?+?book.getAuthor()+?"\t書籍價格:"?+?formatter.format?(book.getPrice()/100.0)+"元"); ?????????????} ?????} } 我們只修改了粗體部分,其他的部分沒有任何改動,運行結果如下所示。 ----------------------書店賣出去的書籍記錄如下:--------------------- 書籍名稱:天龍八部  書籍作者:金庸    書籍價格:¥25.60元 書籍名稱:巴黎圣母院 書籍作者:雨果    書籍價格:¥50.40元 書籍名稱:悲慘世界  書籍作者:雨果    書籍價格:¥28.00元 書籍名稱:金瓶梅   書籍作者:蘭陵笑笑生 書籍價格:¥38.70元 OK,打折銷售開發完成了。看到這里,各位可能有想法了:增加了一個OffNoveBook類后,你的業務邏輯還是修改了,你修改了static靜態模塊區域。這部分確實修改了,該部分屬于高層次的模塊,是由持久層產生的,在業務規則改變的情況下高層模塊必須有部分改變以適應新業務,改變要盡量地少,防止變化風險的擴散。 注意 開閉原則對擴展開放,對修改關閉,并不意味著不做任何修改,低層模塊的變更,必然要有高層模塊進行耦合,否則就是一個孤立無意義的代碼片段。 我們可以把變化歸納為以下三種類型: ● 邏輯變化 只變化一個邏輯,而不涉及其他模塊,比如原有的一個算法是a*b+c,現在需要修改為a*b*c,可以通過修改原有類中的方法的方式來完成,前提條件是所有依賴或關聯類都按照相同的邏輯處理。 ● 子模塊變化 一個模塊變化,會對其他的模塊產生影響,特別是一個低層次的模塊變化必然引起高層模塊的變化,因此在通過擴展完成變化時,高層次的模塊修改是必然的,剛剛的書籍打折處理就是類似的處理模塊,該部分的變化甚至會引起界面的變化。 ● 可見視圖變化 可見視圖是提供給客戶使用的界面,如JSP程序、Swing界面等,該部分的變化一般會引起連鎖反應(特別是在國內做項目,做歐美的外包項目一般不會影響太大)。如果僅僅是界面上按鈕、文字的重新排布倒是簡單,最司空見慣的是業務耦合變化,什么意思呢?一個展示數據的列表,按照原有的需求是6列,突然有一天要增加1列,而且這一列要跨N張表,處理M個邏輯才能展現出來,這樣的變化是比較恐怖的,但還是可以通過擴展來完成變化,這就要看我們原有的設計是否靈活。 我們再來回顧一下書店銷售書籍的程序,首先是我們有一個還算靈活的設計(不靈活是什么樣子?BookStore中所有使用到IBook的地方全部修改為實現類,然后再擴展一個ComputerBook書籍,你就知道什么是不靈活了);然后有一個需求變化,我們通過擴展一個子類擁抱了變化;最后把子類投入運行環境中,新邏輯正式投產。通過分析,我們發現并沒有修改原有的模塊代碼,IBook接口沒有改變,NovelBook類沒有改變,這屬于已有的業務代碼,我們保持了歷史的純潔性。放棄修改歷史的想法吧,一個項目的基本路徑應該是這樣的:項目開發、重構、測試、投產、運維,其中的重構可以對原有的設計和代碼進行修改,運維盡量減少對原有代碼的修改,保持歷史代碼的純潔性,提高系統的穩定性。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看