Code Style Guide之正交设计浅析
前提:模塊化設(shè)計(jì)
為什么需要模塊化設(shè)計(jì)?
理論上可以只使用一個(gè)函數(shù)完成全部功能,但是太過(guò)復(fù)雜,超過(guò)人的掌控極限。因此必須要?jiǎng)澐珠_(kāi),對(duì)問(wèn)題進(jìn)行分解。(面向過(guò)程->面向?qū)ο?#xff09;
模塊化設(shè)計(jì)遇到的兩個(gè)問(wèn)題
- 如何劃分模塊?
- 模塊之間如何連接?
軟件設(shè)計(jì)
為何要做軟件設(shè)計(jì)?
軟件設(shè)計(jì)是為了讓軟件在長(zhǎng)期范圍內(nèi)容易應(yīng)對(duì)變化。即:盡量降低變化對(duì)軟件的影響。否則維護(hù)成本太大。
HOW?
高內(nèi)聚、低耦合原則
- 內(nèi)聚:一個(gè)單位內(nèi)部事物之間關(guān)聯(lián)的緊密程度。
- 高內(nèi)聚:將同類事物放在一起,只做一件事(單一職責(zé))。
- 耦合:不同單位之間關(guān)聯(lián)的緊密程度。
- 低耦合:不同單位之間盡量不要互相影響。
什么是好的模塊化設(shè)計(jì)?
模塊本身高內(nèi)聚,不同模塊間低耦合。
正交
什么是正交?
- 二維空間:兩條直線正交、正交分解
- 多維空間:向量正交
- 特點(diǎn):正交的兩個(gè)單位互不影響。
模塊化設(shè)計(jì) & 正交設(shè)計(jì)
- 模塊之間互不影響(正交)
- 模塊的劃分實(shí)際上是功能職責(zé)的劃分(如何劃分模塊)
- 每個(gè)模塊保證自己功能的實(shí)現(xiàn),不關(guān)心其他模塊如何實(shí)現(xiàn)
- 模塊之間聯(lián)系的橋梁是接口(API),接口是模塊之間的分界線(模塊之間如何連接)
通俗的說(shuō),只要接口不變,一個(gè)模塊對(duì)另一個(gè)模塊的影響就是0,無(wú)論提供接口的模塊內(nèi)部實(shí)現(xiàn)如何變動(dòng),對(duì)調(diào)用接口的模塊來(lái)說(shuō)都是透明的。
舉例:
例1:
1. 模塊A負(fù)責(zé)排序,提供接口sort
2. 模塊B在功能實(shí)現(xiàn)邏輯中需要得到排好序的數(shù)據(jù),調(diào)用了模塊A的sort接口,實(shí)現(xiàn)了自身的功能
- 模塊A不關(guān)心誰(shuí)來(lái)調(diào)用sort接口,不論是模塊B還是模塊C。外部的變動(dòng)對(duì)模塊自身無(wú)影響。
- 模塊B只需要知道調(diào)用模塊A的sort接口可以對(duì)數(shù)據(jù)進(jìn)行排序,而不關(guān)心模塊A內(nèi)部使用了什么排序算法。即使有一天模塊A的排序算法從冒泡排序變成了歸并排序,模塊B的實(shí)現(xiàn)代碼也不需要有任何改動(dòng)。
例2:設(shè)計(jì)一個(gè)結(jié)賬計(jì)算器
public Class Goods {private double price;public double getPrice() {return price;}public void setPrice(double price) {this.price = price;} }設(shè)計(jì)的問(wèn)題:不應(yīng)當(dāng)把物品自己的算賬細(xì)節(jié)與購(gòu)物車的算賬細(xì)節(jié)放到一起,模塊之間耦合度太高。
四個(gè)策略
策略一:消除重復(fù)
重復(fù)的代碼意味著相同的事物沒(méi)有被放到一起,即低內(nèi)聚。
舉例:有多個(gè)地方都需要對(duì)數(shù)據(jù)排序,應(yīng)當(dāng)寫一個(gè)工具類進(jìn)行數(shù)據(jù)排序,而不是在每個(gè)地方自己寫重復(fù)的排序代碼。
有重復(fù)的代碼意味著違反單一職責(zé)原則,他們做了不止一件事。
所以,每當(dāng)發(fā)現(xiàn)自己在寫重復(fù)的代碼時(shí),應(yīng)當(dāng)將重復(fù)的部分剝離出來(lái)。
策略二:分離不同的變化方向
如果總是因?yàn)橐恍╊愃频脑蛐薷哪K內(nèi)同一部分代碼,而其他部分的代碼不變,則應(yīng)該將變化的方向抽離出來(lái)。
舉例:一件商品有時(shí)候打五折,有時(shí)候打九折,有時(shí)候不打折,那么就應(yīng)當(dāng)將折扣抽離出來(lái),為商品增加“折扣”這個(gè)屬性。
策略三:縮小依賴范圍
兩個(gè)模塊之間依靠API進(jìn)行關(guān)聯(lián),因此API決定了模塊間的耦合度。
API設(shè)計(jì)的要點(diǎn):
1. API應(yīng)包含盡量少的知識(shí),因?yàn)槿魏我豁?xiàng)知識(shí)的變化都會(huì)導(dǎo)致雙方的變化;
2. API也應(yīng)該高內(nèi)聚,而不應(yīng)該強(qiáng)迫API的調(diào)用者依賴它不需要的東西。
舉例:
public interface Saleable {double checkout(); }public abstract Class Goods implements Saleable {private double price;//getter setter...@Overridepublic double checkout() {...} }public Class GoodsA {... }public double checkout (List<Goods> shoppingGoods) {double sum = 0d;for (Goods good : shoppingGoods) {sum += good.checkout();}return sum; }對(duì)于checkout方法來(lái)說(shuō),聲明了傳入的參數(shù)類型是List<Goods>,但是在方法內(nèi)部只調(diào)用了good.checkout()方法,而這個(gè)方法實(shí)際上是Saleable接口所聲明的。因此,這個(gè)函數(shù)的聲明應(yīng)當(dāng)是:
public double checkout (List<Saleable> saleableList) {double sum = 0d;for (Saleable saleable : saleableList) {sum += saleable.checkout();}return sum; }之前的設(shè)計(jì)中,API包含了多余的知識(shí),要求參數(shù)必須是List<Goods>,然而在實(shí)現(xiàn)細(xì)節(jié)中沒(méi)有用到Goods類的任何東西,它強(qiáng)迫這個(gè)API的調(diào)用者必須傳入List<Goods>類型的參數(shù)。然而實(shí)際上,如果有另一個(gè)實(shí)現(xiàn)了Saleable接口但沒(méi)有繼承Goods類的類,那么這個(gè)類是無(wú)法使用這個(gè)API的,即API的內(nèi)聚程度不夠高,它強(qiáng)迫API的調(diào)用者依賴它不需要的東西。
策略四:向著穩(wěn)定的方向依賴
兩個(gè)模塊之間如果有API的調(diào)用關(guān)系,那么這兩個(gè)模塊必然有一定程度的耦合。因此我們只能盡量降低耦合度而無(wú)法在存在API調(diào)用關(guān)系的情況下消除耦合。為了提高依賴方(API調(diào)用者)的穩(wěn)定性,我們應(yīng)當(dāng)努力使API穩(wěn)定。
如何使API穩(wěn)定?在設(shè)計(jì)API時(shí)應(yīng)當(dāng)站在API調(diào)用者而不是API提供者的角度,思考API的調(diào)用者需要什么,不關(guān)心什么,在這個(gè)原則上進(jìn)行封裝/信息隱藏。
總結(jié)
- 模塊化的正交設(shè)計(jì):高內(nèi)聚、低耦合。
- 四個(gè)策略:前兩個(gè)策略解決如何劃分模塊的問(wèn)題,后兩個(gè)策略解決模塊之間如何連接的問(wèn)題。
總結(jié)
以上是生活随笔為你收集整理的Code Style Guide之正交设计浅析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: ORACLE: LPAD 和 RPAD(
- 下一篇: ubuntu安装pr_技术|一步步安装U