**要想設計一個良好的程序,建議采用SOLID原則,若考慮了SOLID,可以使程序在模塊內具有高內聚、而模塊間具有低耦合的特點。**
1.單一職責
簡單來說單一職責就是一個類只負責一個功能。更加具體的說就是對一個類而言,應該是一組相關性很高的函數、數據的封裝,是高內聚低耦合的,對外界而言應該僅有一個引起它變化的原因。
單一職責在項目中的使用:
1.項目中的新手引導變量的管理可以統一在各自的Modle中用單獨的類來管理
2.MVP模式P層生命周期與V層生命周期的同步可以用單獨的包裝類來實現,
3.各種基礎框架功能的定義,例如:圖片的加載、緩存、顯示等都應該在各自的類中去做。
下面以一個圖片加載庫的實現為例:成功加載一張圖片可分為:請求、緩存、加載三個步驟,那我們就按照單一職責去創建三個類分別實現這三個功能。
~~~
/**
* 圖片顯示
*/
public class ImageLoad {
public void displayImage(String url, ImageView imageView) {
}
}
/**
* 圖片緩存
*/
public class ImageCache {
public void put(String url,Bitmap bitmap){
}
public Bitmap get(String url){
}
}
/**
* 圖片加載
*/
public class ImageRequest {
public Bitmap downloadImage(String url){
}
}
~~~
上面的例子:ImageLoad只負責顯示圖片,ImageRequest負責從網絡下載圖片,ImageCache負責處理圖片緩存邏輯,這樣設計各自的類職責相當單一,假如我們需要修改緩存的邏輯,我們只需要修改ImageCache的邏輯,若項目升級我們需要升級網絡請求庫,那涉及到圖片部分我們也只需要修改ImageRequest類即可。
總之:單一職責所要表達的用意就是”單一”二字,但是如何劃分一個類、甚至是一個函數的職責,這就需要每一個開發者自己去設計。
2.開閉原則
開閉原則的英文全稱是Open Close Principle縮寫即OCP。開閉原則的定義是:軟件中的對象(類、模塊、函數等)應該對于擴展是開放的,但是對于修改是封閉的。在軟件的生命周期內,因為變化、升級和維護等原因需要對軟件的原有代碼進行修改時,可能會將錯誤的代碼引入,從而破壞原有系統。因此當軟件需求發生變化時,我們應該盡量通過擴展的方式 來實現變化,而不是通過修改已有的代碼。
開閉原則在項目中的使用:
1.基類與子類,子類可以繼承父類并擴展父類的功能
2.接口與實現類,接口定義功能,實現類按照各自的需求實現
繼續以上面的圖片加載框架為例:上面我們定義了三個類:ImageLoad,ImageRequest,ImageCache分別來做顯示、加載、緩存這三件事,很簡單的滿足了單一職責的原則,但是當我們不同情況下需要執行不同的緩存策略時是不是每次又要去修改ImageCache這個類?這是不是跟我們的開閉原則相違背?ImageRequest同樣如此。因此我們需要保證在添加新的緩存策略時不需要修改原來的類,只需要在此基礎上擴展就可以了,這樣就可以避免修改原來的類引起的未知的錯誤。
~~~
/**
* 圖片顯示
*/
public class ImageLoad {
public void displayImage(String url, ImageView imageView) {
}
}
/**
* 圖片緩存
*/
public interface ImageCache {
public void put(String url,Bitmap bitmap);
public Bitmap get(String url);
}
/**
* 圖片加載
*/
public interface ImageRequest {
public Bitmap downloadImage(String url);
}
~~~
注意:我們只是將ImageCache類和ImageRequest類改成了接口,這樣的好處就是我們需要添加新的緩存策略時只需要實現ImageCache接口,ImageRequest同樣如此。下面我們實現內存緩存和SD卡緩存
~~~
/**
* 內存緩存
*/
public class MemoryCache implements ImageCache {
@Override
public void put(String url, Bitmap bitmap) {
}
@Override
public Bitmap get(String url) {
return null;
}
}
/**
* SD卡緩存
*/
public class DiskCache implements ImageCache {
@Override
public void put(String url, Bitmap bitmap) {
}
@Override
public Bitmap get(String url) {
return null;
}
}
~~~
然后在我們的ImageLoad中可以動態注入ImageCahce實現:
~~~
/**
* 圖片顯示
*/
public class ImageLoad {
private ImageCache mCache = new MemoryCache();//默認緩存策略為內存緩存
/**
* 緩存策略注入
*/
public void setImageCache(ImageCache imageCache) {
this.mCache = imageCache;
}
public void displayImage(String url, ImageView imageView) {
}
}
~~~
在上面的例子中,通過setImageCache方法開發者可以對ImageLoad注入不同的緩存實現,這使得ImageLoad更加簡單、健壯、擴展性、靈活性更高,同時也使得ImageCache的添加簡單高效(只需要實現ImageCache接口然后通過setImageCache注入),并且這些擴展不會不會導致ImageLoad類修改。這正是開閉原則的核心思想:對擴展開發,對修改封閉。
3.里氏替換原則
里氏替換原則的定義:如果對每一個類型為S的對象O1,都有類型為T的對象O2,程序P在所有的對象O1都帶換成O2時,程序P的行為沒有發生變化,那么類型S是類型T的子類型換言之就是所有引用基類的地方必須能透明的使用其子類的對象。更通俗的講就是只要父類出現的地方子類就可以出現,而且替換為子類也不會產生任何的錯誤或者異常。
里氏替換原則的核心是抽象,而抽象又依賴于繼承這個特性,在OOP當中,繼承的優缺點都相當明顯。
優點:
1.代碼重用,減少創建類的成本,每個子類都擁有父類的方法和屬性
2.子類與父類基本相似,但又與父類有所區別
3.提高代碼的可擴展性
缺點:
1.繼承是侵入性的,只要繼承就必須擁有父類的方法和屬性
2.可能造成子類代碼冗余,靈活性降低,因為子類必須擁有父類的屬性和方法
在上面的例子中,我們通過ImageCache建立起了一套緩存的規范,在通過setImageCache注入不同的具體實現,保證了系統的擴展性和靈活性。因此開閉原則和里氏替換原則往往是生死相依,形影不離的,通過里氏替換原則來達到對擴展開放,對修改關閉的效果。
4.依賴倒置原則
依賴倒置原則指定了一種特定的解耦形式,使得高層次的模塊不依賴與低層次模塊的實現細節的目的,依賴模塊被顛倒了。依賴倒置原則有以下幾個關鍵點:
1.高層模塊不應該依賴于低層模塊,兩者都應該依賴其抽象
2.抽象不應該依賴于細節
3.細節應該依賴于抽象
在Java語言中,抽象就是指接口或者抽象類,二者都是不能夠被直接實例化的:細節就是實現類,實現接口或者抽象類而產生的類就是細節,其特點就是可以直接被實例化,也就是可以使用關鍵字new產生一個對象。高層模塊就是指調用端,底層模塊就是指具體的實現類。依賴倒置原則在Java語言中的表現就是:模塊間的依賴通過抽象產生,實現類之間不發生直接的依賴關系,其依賴關系是通過接口或者抽象類產生的。使用一句話概括就是:面向接口編程或者說是面向抽象編程。
如果類與類直接依賴細節,那么這幾個類之間就有直接的耦合,當具體的需求發生變化是,意味著同時修改依賴者的代碼。在上面圖片加載的例子中,ImageLoad這個類依賴于ImageCache這個接口,而具體的實現可以通過setImageCache注入,這樣當Cache策略需要升級時,只需要實現ImageCache接口,然后通過setImageCache注入到ImageLoad中。這樣就保證了細節與依賴的隔離。
5.接口隔離原則
接口隔離原則的定義是:客戶端不應該依賴于他不需要的接口。另一種定義是:類之間的依賴關系應該建立在最小的接口上。接口隔離原則將非常龐大,臃腫的接口拆分成更小的和更具體的接口,這樣客戶端將會值需要知道它們感興趣的方法。接口隔離原則的目的是系統解開耦合,從而容易重構、更改和部署。
在上面的IamgeLoad例子中,ImageCache指向ImageLoad提供了get和put方法,其他一概不管,這使得具體的緩存策略實現對ImageLoad隱藏,這就是使用最小化接口隔離了實現類的細節,也促使我們將更加龐大的接口拆分到更細粒度的接口當中,這同樣使得我們的系統具有更低的耦合性、更高的靈活性。
6.迪米特原則
迪米特原則:一個對象應該對其他對象有最少的了解,通俗的講,一個類應該對自己需要耦合或調用的類知道的最少,類的內部如何實現與調用者或者依賴者沒有關系,調用者或者依賴者只需要知道他需要的方法即可,其他的一概不管。類與類之間的關系越密切,耦合度越大,當一個類發生改變時,對另一個類的影響也越大。
在上面的圖片加載框架設計中,我們的IamgeLoad只依賴于ImageCache的get和put方法,對其內部實現細節一無所知,即使通過setImageCache改變注入的對象時,ImageLoad也是對其內部實現細節不關心的,ImageLoad只關心ImageCache提供的get和put方法。這樣使得系統具有更加低的耦合性和更好的擴展性。
小結:
在應用開發過程中,最難的不是完成應用的開發工作,而是在后續的升級、維護過程中讓應用系統能夠擁抱變化。擁抱變化也意味著在滿足需求而且不破壞系統穩定的前提下保持高可擴展性、高內聚、低耦合,在經歷了各版本的變更之后依然保持著清晰、靈活、穩定的系統架構。
- 序言
- 從業感悟
- 常用名詞
- HTML
- JS
- ES6新特性
- jquery和vue對比
- 徹底理解this
- JQuery添加自定義函數
- js的實現
- 原始值和引用值
- MYSQL
- 簡介
- 術語
- 特點
- 范式
- 數據類型1
- 數據類型2
- 編碼
- 權限管理
- 事務
- mvvc
- 引擎
- MyISAM與InnoDB區別
- 索引類型
- 鎖
- 死鎖
- 分層架構
- 執行計劃
- join原理
- 高可用
- 日志類型
- 分庫分表
- 中間件
- 服務器
- 操作系統
- 信號量 鎖 隊列
- PHP
- composer加載原理
- composer基礎知識
- 自動加載函數
- composer加載代碼
- composer 自動加載
- 內存管理
- PHP執行流程
- cgi,fastCgi,php-fpm
- HTTP
- 錯誤碼
- 跨域請求
- 面試
- 安全
- HTTP劫持
- 設計模式
- 如何正確的使用設計模式
- 單例模式
- 原型模式
- 簡單工廠模式
- 工廠方法模式
- 抽象工廠模式
- 建造者模式
- 設計原則
- 算法
- PHP短標簽