他們?cè)趯W(xué)校里不會(huì)教你的編程原則
前言
在大學(xué)的時(shí)候,學(xué)校一般只會(huì)教你你寫(xiě)編程語(yǔ)言,比如C、C++、JAVA等編程語(yǔ)言。但是當(dāng)你離開(kāi)大學(xué)進(jìn)入這個(gè)行業(yè)開(kāi)始工作時(shí),才知道編程不只是知道編程語(yǔ)言、語(yǔ)法等,要想寫(xiě)好代碼,必須還要了解一些編程原則才行。本文主要討論KISS、DRY和SOLID這些常見(jiàn)的編程原則,而且你會(huì)發(fā)現(xiàn)隨著工作時(shí)間越久,越能感受這些編程原則的精妙之處,歷久彌香。
KISS原則
Keep It Simple, Stupid!
你是不是有過(guò)接手同事的代碼感到十分頭疼的經(jīng)歷,明明可以有更加簡(jiǎn)單、明白的寫(xiě)法,非要繞來(lái)繞去,看不明白?
其實(shí),我們?cè)趯?xiě)代碼的時(shí)候應(yīng)該要遵守KISS原則,核心思想就是盡量保持簡(jiǎn)單。代碼的可讀性和可維護(hù)性是衡量代碼質(zhì)量非常重要的兩個(gè)標(biāo)準(zhǔn)。而 KISS 原則就是保持代碼可讀和可維護(hù)的重要手段。代碼足夠簡(jiǎn)單,也就意味著很容易讀懂,bug 比較難隱藏。即便出現(xiàn) bug,修復(fù)起來(lái)也比較簡(jiǎn)單。
我們寫(xiě)代碼的的時(shí)候要站在別人的角度出發(fā),就像馬丁·福勒說(shuō)的,我們寫(xiě)的代碼不是給機(jī)器看的,而是給人看的。
“任何傻瓜都可以編寫(xiě)計(jì)算機(jī)可以理解的代碼。優(yōu)秀的程序員編寫(xiě)出人類(lèi)可以理解的代碼。” — 馬丁·福勒
那么如何才能寫(xiě)出滿足KISS原則的代碼呢?
如何寫(xiě)出KISS原則的代碼?
我們直接上例子,下面的校驗(yàn)IP是否合法的3種實(shí)現(xiàn)方式,大家覺(jué)得哪個(gè)最KISS?
寫(xiě)法一
寫(xiě)法二
寫(xiě)法三
- 寫(xiě)法一代碼量最少,正則表達(dá)式本身是比較復(fù)雜的,寫(xiě)出完全沒(méi)有 bug 的正則表達(dá)本身就比較有挑戰(zhàn);另一方面,并不是每個(gè)程序員都精通正則表達(dá)式。對(duì)于不怎么懂正則表達(dá)式的同事來(lái)說(shuō),看懂并且維護(hù)這段正則表達(dá)式是比較困難的。這種實(shí)現(xiàn)方式會(huì)導(dǎo)致代碼的可讀性和可維護(hù)性變差,所以,從 KISS 原則的設(shè)計(jì)初衷上來(lái)講,這種實(shí)現(xiàn)方式并不符合 KISS 原則。
- 寫(xiě)法二使用了 StringUtils 類(lèi)、Integer 類(lèi)提供的一些現(xiàn)成的工具函數(shù),來(lái)處理 IP地址字符串,邏輯清晰,可讀性好。
- 寫(xiě)法三不使用任何工具函數(shù),而是通過(guò)逐一處理 IP 地址中的字符,來(lái)判斷是否合法,容易出bug,不好理解。
所以說(shuō),符合KISS原則的代碼并不是代碼越少越好,還要考慮代碼是否邏輯清晰、是否容易理解、是否夠穩(wěn)定。
總結(jié)以下如何寫(xiě)出KISS原則的代碼:
- 不要使用同事可能不懂的技術(shù)來(lái)實(shí)現(xiàn)代碼。比如前面例子中的正則表達(dá)式,還有一些編程語(yǔ)言中過(guò)于高級(jí)的語(yǔ)法等。
- 不要重復(fù)造輪子,要善于使用已經(jīng)有的工具類(lèi)庫(kù)。經(jīng)驗(yàn)證明,自己去實(shí)現(xiàn)這些類(lèi)庫(kù),出bug 的概率會(huì)更高,維護(hù)的成本也比較高。
- 不要過(guò)度優(yōu)化。不要過(guò)度使用一些奇技淫巧(比如,位運(yùn)算代替算術(shù)運(yùn)算、復(fù)雜的條件語(yǔ)句代替if-else、使用一些過(guò)于底層的函數(shù)等)來(lái)優(yōu)化代碼,犧牲代碼的可讀性。
- 主觀站在別人的角度上編寫(xiě)代碼。你在編寫(xiě)代碼的時(shí)候就要思考我這個(gè)同事看這段代碼是不是很快就能夠明白理解。
DRY原則
Don't Repeat Yourself
你是不是有過(guò)這樣的經(jīng)歷,項(xiàng)目中很多重復(fù)邏輯的代碼,然后修改一個(gè)地方,另外一個(gè)地方忘記修改,導(dǎo)致測(cè)試給你提了很多bug?
DRY原則,英文全稱(chēng)Don’t Repeat Yourself,直譯過(guò)來(lái)就是不要重復(fù)你自己。這里的重復(fù)不僅僅是代碼一模一樣,還包括實(shí)現(xiàn)邏輯重復(fù)、功能語(yǔ)義重復(fù)、代碼執(zhí)行重復(fù)等。我們不要偷懶,有責(zé)任把這些存在重復(fù)的地方識(shí)別出來(lái),然后優(yōu)化它們。
如何寫(xiě)出DRY原則的代碼呢?
我們直接上例子,代碼重復(fù)的我就不講了,很好理解,關(guān)于實(shí)現(xiàn)邏輯或者功能語(yǔ)義重復(fù)的我覺(jué)個(gè)例子。
還是上面校驗(yàn)IP的例子,團(tuán)隊(duì)中兩個(gè)同事由于不知道就有了兩種寫(xiě)法。
- 同事A寫(xiě)法
- 同事B寫(xiě)法
盡管兩段代碼的實(shí)現(xiàn)邏輯不重復(fù),但語(yǔ)義重復(fù),也就是功能重復(fù),我們認(rèn)為它違反了 DRY 原則。我們應(yīng)該在項(xiàng)目中,統(tǒng)一一種實(shí)現(xiàn)思路,所有用到判斷 IP 地址是否合法的地方,都統(tǒng)一調(diào)用同一個(gè)函數(shù)。不然哪天校驗(yàn)規(guī)則變了,很容易只改了其中一個(gè),另外一個(gè)漏改,就會(huì)出現(xiàn)莫名其妙的bug。
其他的比如邏輯重復(fù)的意思是雖然功能是不一致的,但是里面的邏輯都是一模一樣的。舉個(gè)例子,比如校驗(yàn)用戶(hù)名和校驗(yàn)密碼,雖然功能不一致,但是校驗(yàn)邏輯都是相似,判空、字符長(zhǎng)度等等,這種情況我們就需要把相似的邏輯抽取到一個(gè)方法中,不然也是不符合DRY原則。
那么我們平時(shí)寫(xiě)代碼注意些什么才是符合DRY原則呢?
- 使用現(xiàn)成的輪子,不輕易造輪子
其實(shí)最關(guān)鍵的就是寫(xiě)代碼帶腦子,用到一個(gè)方法先看看有沒(méi)有現(xiàn)成的,不要看看不看,就動(dòng)手在那里造輪子。
- 減少代碼耦合
對(duì)于高度耦合的代碼,當(dāng)我們希望復(fù)用其中的一個(gè)功能,想把這個(gè)功能的代碼抽取出來(lái)成為一個(gè)獨(dú)立的模塊、類(lèi)或者函數(shù)的時(shí)候,往往會(huì)發(fā)現(xiàn)牽一發(fā)而動(dòng)全身。移動(dòng)一點(diǎn)代碼,就要牽連到很多其他相關(guān)的代碼。所以,高度耦合的代碼會(huì)影響到代碼的復(fù)用性,我們要盡量減少代碼耦合。
- 滿足單一職責(zé)原則
我們前面講過(guò),如果職責(zé)不夠單一,模塊、類(lèi)設(shè)計(jì)得大而全,那依賴(lài)它的代碼或者它依賴(lài)的代碼就會(huì)比較多,進(jìn)而增加了代碼的耦合。根據(jù)上一點(diǎn),也就會(huì)影響到代碼的復(fù)用性。相反,越細(xì)粒度的代碼,代碼的通用性會(huì)越好,越容易被復(fù)用。
- 模塊化
這里的“模塊”,不單單指一組類(lèi)構(gòu)成的模塊,還可以理解為單個(gè)類(lèi)、函數(shù)。我們要善于將功能獨(dú)立的代碼,封裝成模塊。獨(dú)立的模塊就像一塊一塊的積木,更加容易復(fù)用,可以直接拿來(lái)搭建更加復(fù)雜的系統(tǒng)。
- 業(yè)務(wù)與非業(yè)務(wù)邏輯分離
越是跟業(yè)務(wù)無(wú)關(guān)的代碼越是容易復(fù)用,越是針對(duì)特定業(yè)務(wù)的代碼越難復(fù)用。所以,為了復(fù)用跟業(yè)務(wù)無(wú)關(guān)的代碼,我們將業(yè)務(wù)和非業(yè)務(wù)邏輯代碼分離,抽取成一些通用的框架、類(lèi)庫(kù)、組件等。
- 通用代碼下沉
從分層的角度來(lái)看,越底層的代碼越通用、會(huì)被越多的模塊調(diào)用,越應(yīng)該設(shè)計(jì)得足夠可復(fù)用。一般情況下,在代碼分層之后,為了避免交叉調(diào)用導(dǎo)致調(diào)用關(guān)系混亂,我們只允許上層代碼調(diào)用下層代碼及同層代碼之間的調(diào)用,杜絕下層代碼調(diào)用上層代碼。所以,通用的代碼我們盡量下沉到更下層。
- 繼承、多態(tài)、抽象、封裝
在講面向?qū)ο筇匦缘臅r(shí)候,我們講到,利用繼承,可以將公共的代碼抽取到父類(lèi),子類(lèi)復(fù)用父類(lèi)的屬性和方法。利用多態(tài),我們可以動(dòng)態(tài)地替換一段代碼的部分邏輯,讓這段代碼可復(fù)用。除此之外,抽象和封裝,從更加廣義的層面、而非狹義的面向?qū)ο筇匦缘膶用鎭?lái)理解的話,越抽象、越不依賴(lài)具體的實(shí)現(xiàn),越容易復(fù)用。代碼封裝成模塊,隱藏可變的細(xì)節(jié)、暴露不變的接口,就越容易復(fù)用。
- 應(yīng)用模板等設(shè)計(jì)模式
一些設(shè)計(jì)模式,也能提高代碼的復(fù)用性。比如,模板模式利用了多態(tài)來(lái)實(shí)現(xiàn),可以靈活地替換其中的部分代碼,整個(gè)流程模板代碼可復(fù)用。
SOLID原則
SOLID原則不是一個(gè)單一的原則,而是對(duì)軟件開(kāi)發(fā)至關(guān)重要的 5 條原則,遵循這些原則有助于我們寫(xiě)出高內(nèi)聚、低耦合、可擴(kuò)展、可維護(hù)性好的代碼。
S—單一職責(zé)原則
一個(gè)類(lèi)應(yīng)該有一個(gè),而且只有一個(gè)改變它的理由。
單一職責(zé)原則在我看來(lái)是最容易理解也是最重要的一個(gè)原則。它的核心思想就是一個(gè)模塊、類(lèi)或者方法只做一件事,只有一個(gè)職責(zé),千萬(wàn)不要越俎代庖。它可以帶來(lái)下面的好處:
- 可以讓代碼耦合度更低
- 使代碼更容易理解和維護(hù)
- 使代碼更易于測(cè)試和維護(hù),使軟件更易于實(shí)施,并有助于避免未來(lái)更改的意外副作用
舉個(gè)例子,我們有兩個(gè)類(lèi)Person和Account。 兩者都負(fù)有存儲(chǔ)其特定信息的單一責(zé)任。 如果要更改Person的狀態(tài),則無(wú)需修改類(lèi)Account,反之亦然, 不要把賬戶(hù)的行為比如修改賬戶(hù)名changeAcctName寫(xiě)在Person類(lèi)中。
public class Person {
private Long personId;
private String firstName;
private String lastName;
private String age;
private List<Account> accounts;
// 錯(cuò)誤做法
public void changeAcctName(Account account, String acctName) {
acccount.setAccountName(acctName);
// 更新到數(shù)據(jù)庫(kù)
}
}
public class Account {
private Long guid;
private String accountNumber;
private String accountName;
private String status;
private String type;
}
所以大家在編寫(xiě)代碼的時(shí)候,一定要停頓思考下這個(gè)段代碼真的寫(xiě)在這里嗎?另外很關(guān)鍵的一點(diǎn)是如果發(fā)現(xiàn)一個(gè)類(lèi)或者一個(gè)方法十分龐大,那么很有可能已經(jīng)違背單一職責(zé)原則了,后續(xù)維護(hù)可想而知十分痛苦。
O—開(kāi)閉原則
軟件實(shí)體(類(lèi)、模塊、函數(shù)等)應(yīng)該對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉。
對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉,什么意思?很簡(jiǎn)單,其實(shí)就是我們要盡量通過(guò)新增類(lèi)實(shí)現(xiàn)功能,而不是修改原有的類(lèi)或者邏輯。因?yàn)樾薷囊延写a很有可能對(duì)已有功能引入bug。
讓我們通過(guò)一個(gè)例子來(lái)理解這個(gè)原則,比如一個(gè)通知服務(wù)。
public class NotificationService {
public void sendOTP(String medium) {
if (medium.equals("email")) {
//email 發(fā)送
} else if (medium.equals("mobile")) {
// 手機(jī)發(fā)送
}
}
現(xiàn)在需要新增微信的方式通知,你要怎么做呢? 是在加一個(gè)if else嗎? 這樣就不符合開(kāi)閉原則了,我們看下開(kāi)閉原則該怎么寫(xiě)。
- 定義一個(gè)通知服務(wù)接口
public interface NotificationService {
public void sendOTP();
}
- E-mail方式通知類(lèi)EmailNotification
public class EmailNotification implements NotificationService{
public void sendOTP(){
// write Logic using JavaEmail api
}
}
- 手機(jī)方式通知類(lèi)MobileNotification
public class MobileNotification implements NotificationService{
public void sendOTP(){
// write Logic using Twilio SMS API
}
}
- 同樣可以添加微信通知服務(wù)的實(shí)現(xiàn)WechatNotification
public class WechatNotification implements NotificationService{
public void sendOTP(String medium){
// write Logic using wechat API
}
}
這樣的方式就是遵循開(kāi)閉原則的,你不用修改核心的業(yè)務(wù)邏輯,這樣可能帶來(lái)意向不到的后果,而是擴(kuò)展實(shí)現(xiàn)方式,由調(diào)用方根據(jù)他們的實(shí)際情況調(diào)用。
是不是想到了設(shè)計(jì)模式中的策略模式,其實(shí)設(shè)計(jì)模式就是指導(dǎo)我們寫(xiě)出高內(nèi)聚、低耦合的代碼。
L—里氏替換原則
派生類(lèi)或子類(lèi)必須可替代其基類(lèi)或父類(lèi)
這個(gè)原則稍微有點(diǎn)難以理解,它的核心思想是每個(gè)子類(lèi)或派生類(lèi)都應(yīng)該可以替代/等效于它們的基類(lèi)或父類(lèi)。這樣有一個(gè)好處,就是無(wú)論子類(lèi)是什么類(lèi)型,客戶(hù)端通過(guò)父類(lèi)調(diào)用都不會(huì)產(chǎn)生意外的后果。
理解不了?那我我們通過(guò)一個(gè)例子來(lái)理解一下。
讓我們考慮一下我有一個(gè)名為 SocialMedia 的抽象類(lèi),它支持所有社交媒體活動(dòng)供用戶(hù)娛樂(lè),如下所示:
package com.alvin.solid.lsp;
public abstract class SocialMedia {
public abstract void chatWithFriend();
public abstract void publishPost(Object post);
public abstract void sendPhotosAndVideos();
public abstract void groupVideoCall(String... users);
}
社交媒體可以有多個(gè)實(shí)現(xiàn)或可以有多個(gè)子類(lèi),如 Facebook、Wechat、Weibo 和 Twitter 等。
現(xiàn)在讓我們假設(shè) Facebook 想要使用這個(gè)特性或功能。
package com.alvin.solid.lsp;
public class Wechat extends SocialMedia {
public void chatWithFriend() {
//logic
}
public void publishPost(Object post) {
//logic
}
public void sendPhotosAndVideos() {
//logic
}
public void groupVideoCall(String... users) {
//logic
}
}
我們都知道Facebook都提供了所有上述的功能,所以這里我們可以認(rèn)為Facebook是SocialMedia類(lèi)的完全替代品,兩者都可以無(wú)中斷地替代。
現(xiàn)在讓我們討論 Weibo 類(lèi)
package com.alvin.solid.lsp;
public class Weibo extends SocialMedia {
public void chatWithFriend() {
//logic
}
public void publishPost(Object post) {
//logic
}
public void sendPhotosAndVideos() {
//logic
}
public void groupVideoCall(String... users) {
//不適用
}
}
我們都知道Weibo微博這個(gè)產(chǎn)品是沒(méi)有群視頻功能的,所以對(duì)于 groupVideoCall方法來(lái)說(shuō) Weibo 子類(lèi)不能替代父類(lèi) SocialMedia。所以我們認(rèn)為它是不符合里式替換原則。
如果強(qiáng)行這么做的話,會(huì)導(dǎo)致客戶(hù)端用父類(lèi)SocialMedia調(diào)用,但是實(shí)現(xiàn)類(lèi)注入的可能是個(gè)Weibo的實(shí)現(xiàn),調(diào)用groupVideoCall行為,產(chǎn)生意想不到的后果。
那有什么解決方案嗎?
那就把功能拆開(kāi)唄。
public interface SocialMedia {
public void chatWithFriend();
public void sendPhotosAndVideos()
}
public interface SocialPostAndMediaManager {
public void publishPost(Object post);
}
public interface VideoCallManager{
public void groupVideoCall(String... users);
}
現(xiàn)在,如果您觀察到我們將特定功能隔離到單獨(dú)的類(lèi)以遵循LSP。
現(xiàn)在由實(shí)現(xiàn)類(lèi)決定支持功能,根據(jù)他們所需的功能,他們可以使用各自的接口,例如 Weibo 不支持視頻通話功能,因此 Weibo 實(shí)現(xiàn)可以設(shè)計(jì)成這樣:
public class Instagram implements SocialMedia,SocialPostAndMediaManager{
public void chatWithFriend(){
//logic
}
public void sendPhotosAndVideos(){
//logic
}
public void publishPost(Object post){
//logic
}
}
這樣子就是符合里式替換原則LSP。
I—接口隔離原則
接口不應(yīng)該強(qiáng)迫他們的客戶(hù)依賴(lài)它不使用的方法。
大家可以看看自己的工程,是不是一個(gè)接口類(lèi)中有很多很多的接口,每次調(diào)用API方法的時(shí)候IDE工具給你彈出一大堆,十分的"臃腫肥胖"。所以該原則的核心思想要將你的接口拆小,拆細(xì),打破”胖接口“,不用強(qiáng)迫客戶(hù)端實(shí)現(xiàn)他們不需要的接口。是不是和單一職責(zé)原則有點(diǎn)像?
例如,假設(shè)有一個(gè)名為 UPIPayment 的接口,如下所示
public interface UPIPayments {
public void payMoney();
public void getScratchCard();
public void getCashBackAsCreditBalance();
}
現(xiàn)在讓我們談?wù)?nbsp;UPIPayments 的一些實(shí)現(xiàn),比如 Google Pay 和 AliPay。
Google Pay 支持這些功能所以他可以直接實(shí)現(xiàn)這個(gè) UPIPayments 但 AliPay 不支持 getCashBackAsCreditBalance()功能所以這里我們不應(yīng)該強(qiáng)制客戶(hù)端 AliPay 通過(guò)實(shí)現(xiàn) UPIPayments 來(lái)覆蓋這個(gè)方法。
我們需要根據(jù)客戶(hù)需要分離接口,所以為了滿足接口隔離原則,我們可以如下設(shè)計(jì):
- 創(chuàng)建一個(gè)單獨(dú)的接口來(lái)處理現(xiàn)金返還。
public interface CashbackManager{
public void getCashBackAsCreditBalance();
}
現(xiàn)在我們可以從 UPIPayments 接口中刪除getCashBackAsCreditBalance ,AliPay也不需要實(shí)現(xiàn)getCashBackAsCreditBalance()這個(gè)它沒(méi)有的方法了。
D—依賴(lài)倒置原則
高層模塊不應(yīng)該依賴(lài)低層模塊,兩者都應(yīng)該依賴(lài)于抽象(接口)。抽象不應(yīng)該依賴(lài)于細(xì)節(jié)(具體實(shí)現(xiàn)),細(xì)節(jié)應(yīng)該取決于抽象。
這個(gè)原則我覺(jué)得也不是很好理解,所謂高層模塊和低層模塊的劃分,簡(jiǎn)單來(lái)說(shuō)就是,在調(diào)用鏈上,調(diào)用者屬于高層,被調(diào)用者屬于低層。比如大家都知道的MVC模式,controller是調(diào)用service層接口這個(gè)抽象,而不是實(shí)現(xiàn)類(lèi)。這也是我們經(jīng)常說(shuō)的要面向接口編程,而非細(xì)節(jié)或者具體實(shí)現(xiàn),因?yàn)榻涌谝馕吨跫s,更加穩(wěn)定。
我們通過(guò)一個(gè)例子加深一下理解。
- 借記卡
public class DebitCard {
public void doTransaction(int amount){
System.out.println("tx done with DebitCard");
}
}
- 信用卡
public class CreditCard{
public void doTransaction(int amount){
System.out.println("tx done with CreditCard");
}
}
現(xiàn)在用這兩張卡你去購(gòu)物中心購(gòu)買(mǎi)了一些訂單并決定使用信用卡支付
public class ShoppingMall {
private DebitCard debitCard;
public ShoppingMall(DebitCard debitCard) {
this.debitCard = debitCard;
}
public void doPayment(Object order, int amount){
debitCard.doTransaction(amount);
}
public static void main(String[] args) {
DebitCard debitCard=new DebitCard();
ShoppingMall shoppingMall=new ShoppingMall(debitCard);
shoppingMall.doPayment("some order",5000);
}
}
上面的做法是一個(gè)錯(cuò)誤的方式,因?yàn)?nbsp;ShoppingMall 類(lèi)與 DebitCard 緊密耦合。
現(xiàn)在你的借記卡余額不足,想使用信用卡,那么這是不可能的,因?yàn)?nbsp;ShoppingMall 與借記卡緊密結(jié)合。
當(dāng)然你也可以這樣做,從構(gòu)造函數(shù)中刪除借記卡并注入信用卡。但這不是一個(gè)好的方式,它不符合依賴(lài)倒置原則。
那該如何正確設(shè)計(jì)呢?
- 定義依賴(lài)的抽象接口BankCard
public interface BankCard {
public void doTransaction(int amount);
}
- 現(xiàn)在 DebitCard 和 CreditCard 都實(shí)現(xiàn)BankCard
public class CreditCard implements BankCard{
public void doTransaction(int amount){
System.out.println("tx done with CreditCard");
}
}
public class DebitCard implements BankCard {
public void doTransaction(int amount){
System.out.println("tx done with DebitCard");
}
}
- 現(xiàn)在重新設(shè)計(jì)購(gòu)物中心這個(gè)高級(jí)類(lèi),他也是去依賴(lài)這個(gè)抽象,而不是直接低級(jí)模塊的實(shí)現(xiàn)類(lèi)
public class ShoppingMall {
private BankCard bankCard;
public ShoppingMall(BankCard bankCard) {
this.bankCard = bankCard;
}
public void doPayment(Object order, int amount){
bankCard.doTransaction(amount);
}
public static void main(String[] args) {
BankCard bankCard=new CreditCard();
ShoppingMall shoppingMall1=new ShoppingMall(bankCard);
shoppingMall1.doPayment("do some order", 10000);
}
}
我們還可以拿 Tomcat這個(gè) Servlet 容器作為例子來(lái)解釋一下。
Tomcat 是運(yùn)行Java Web應(yīng)用程序的容器。我們編寫(xiě)的 Web 應(yīng)用程序代碼只需要部署在Tomcat 容器下,便可以被 Tomcat 容器調(diào)用執(zhí)行。按照之前的劃分原則,Tomcat 就是高層模塊,我們編寫(xiě)的 Web 應(yīng)用程序代碼就是低層模塊。Tomcat 和應(yīng)用程序代碼之間并沒(méi)有直接的依賴(lài)關(guān)系,兩者都依賴(lài)同一個(gè)“抽象”,也就是 Sevlet 規(guī)范。Servlet 規(guī)范不依賴(lài)具體的 Tomcat 容器和應(yīng)用程序的實(shí)現(xiàn)細(xì)節(jié),而 Tomcat 容器和應(yīng)用程序依賴(lài) Servlet規(guī)范。
總結(jié)
本文總結(jié)了軟件編程中的黃金原則,KISS原則,DRY原則,SOLID原則。這些原則不僅僅適用于編程,也可以指導(dǎo)我們?cè)诩軜?gòu)設(shè)計(jì)上。雖然其中有些原則很抽象,但是大家多多實(shí)踐和思考,會(huì)體會(huì)到這些原則的精妙。