簡述面向?qū)ο缶幊蹋╫op)的六大原則

SOLID+迪米特原則

在應(yīng)用開發(fā)的過程中,最難的往往不是開發(fā)的工作,而是在后續(xù)的升級維護過程中讓應(yīng)用系統(tǒng)能夠靈活地變化!也就是我們經(jīng)常強調(diào)的代碼健壯性和穩(wěn)定性铅檩!在滿足用戶需求的前提下,不破壞系統(tǒng)穩(wěn)定性的前提下保持高度可擴展性莽鸿,高內(nèi)聚低耦合昧旨,在經(jīng)歷各個版本后依舊保持清晰靈活穩(wěn)定的系統(tǒng)架構(gòu),那么編程的時候應(yīng)當(dāng)盡量根據(jù)面向?qū)ο笞兂傻牧笤瓌t來做祥得。

<h3>S 單一職責(zé)原則(Single Responsibility Principle)</h3>
解釋:
就一個類而言兔沃,應(yīng)該僅有一個引起他變化的原因,簡單來說级及,一個類中應(yīng)該是一組相關(guān)性很高的函數(shù)乒疏、數(shù)據(jù)的封裝!
<pre>
我們來寫一個很簡單的類吧饮焦!寫一個緩存圖片的類
public class ImageLoader{
LurCache<String,Bitmap> caches;
ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
public ImageLoader(){
initImageCache();
}

//顯示圖片
public void display(String url,ImageView imageview){
    Bitmap bm = caches.get(url);
    if(bm!=null){
        imageview.setImageBitmap(bm);
        return;
    }

    imageview.setTag(url);
    mExecutorService.submit(new Runnable() {
        @Override
        public void run() {
            Bitmap bm = downloadImage(url);
            if (bitmap == null) {
                return;
            }

            if (imageView.getTag().equals(url)) {
                imageView.setImageBitmap(bm);
            }

            cache.putBitmapToCache(url, bitmap);
        }
    });
}
//初始化緩存參數(shù)
public void initImageCache(){
    long maxMemory = Runtime.getRuntime().maxMemory()/1024;
    int cacheSize = (int) maxMemory/4;
    mImageCache = new LruCache<String,Bitmap>(cacheSize){
        @Override
        protected int sizeOf(String key, Bitmap value) {
            return value.getRowBytes()*value.getHeight()/1024;
        }
    };
}
//獲取圖片
public Bitmap get(String url){
   return caches.get(url);
}
//存儲圖片
public void put(String url,Bitmap bm){
    if(cache!=null){
        cache.put(url,bm);
    }
}
//下載圖片
public Bitmap downloadImage(String imageUrl) {
    Bitmap bm = null;
    try {
        URL url = new URL(imageUrl);
        URLConnection connection = url.openConnection();
        bm = BitmapFactory.decodeStream(connection.getInputStream());
        return bm;
    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return bm;
}

}
</pre>
上面這個類怕吴,有三個功能,獲取網(wǎng)絡(luò)圖片县踢,顯示圖片转绷,緩存處理,單一職責(zé)就是要一個類實現(xiàn)一個功能硼啤,那么需要把兩個功能給分開暇咆。那就變成了
<pre>
public class ImageDownloader{
//下載圖片
public static Bitmap downloadImage(String imageUrl) {
Bitmap bm = null;
try {
URL url = new URL(imageUrl);
URLConnection connection = url.openConnection();
bm = BitmapFactory.decodeStream(connection.getInputStream());
return bm;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return bm;
}
}
</pre>

<pre>
public class ImageCache{
LrcCache<String,Bitmap> caches;
public ImageCache(){initImageCache();
public void initImageCache(){
long maxMemory = Runtime.getRuntime().maxMemory()/1024;
int cacheSize = (int) maxMemory/4;
mImageCache = new LruCache<String,Bitmap>(cacheSize){
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes()*value.getHeight()/1024;
}
};
}
//獲取圖片
public Bitmap get(String url){
return caches.get(url);
}
//存儲圖片
public void put(String url,Bitmap bm){
if(cache!=null){
cache.put(url,bm);
}
}
}
</pre>

<pre>
public class ImageLoader{
ImageCache cache = new ImageCache();
ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
//加載圖片
public void display(final String url, final ImageView imageView) {
final Bitmap bitmap = cache.getBitmapFromCache(url);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
return;
}
imageView.setTag(url);
mExecutorService.submit(new Runnable() {
@Override
public void run() {
Bitmap bm = ImageDownloader.downloadImage(url);
if (bitmap == null) {
return;
}

            if (imageView.getTag().equals(url)) {
                imageView.setImageBitmap(bm);
            }

            cache.putBitmapToCache(url, bitmap);
        }
    });
}

}
</pre>

<h3>O 開閉原則(Open Close Principle)</h3>
解釋:
軟件中的對象(類,模塊丙曙,函數(shù)等)對擴展來說是開放的,對修改是封閉的其骄。
在軟件的生命周期內(nèi)亏镰,因為變化、升級和維護等原因需要對軟件原有代碼進行修改時拯爽,可能會將錯誤引入原本已經(jīng)經(jīng)過測試的就代碼中索抓,破壞原有系統(tǒng)。
當(dāng)軟件需要變化時毯炮,我們應(yīng)該盡量通過擴展的方式來實現(xiàn)變化逼肯,而不是通過修改該已有的代碼來實現(xiàn)。
<pre>
還是用回上一個例子擴展代碼桃煎,而不修改代碼篮幢,要做到這樣應(yīng)該怎么改之前的代碼呢,例如加上一個本地圖片的功能還有設(shè)置允許緩存为迈,那我們需要新建一個DiskCache類和一個DoubleCache類三椿,Cache類都需要get和put房方法缺菌,所以我們只要將ImageCache作為接口讓所有緩存的類都繼承這個類就可以了
//接口實現(xiàn)類
public class DiskCache extends ImageCache {
String cachePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getPath();
@Override
public void put(String fileUrl, Bitmap bm) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(cachePath + File.separator + fileUrl);
bm.compress(Bitmap.CompressFormat.PNG, 100, fos);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (fos!=null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Override
public Bitmap get(String url) {
return BitmapFactory.decodeFile(cachePath + File.separator + url);
}
}
//接口類
public abstract class ImageCache {
public abstract void put(String url, Bitmap bm);
public abstract Bitmap get(String url);
}
//接口實現(xiàn)類
public class DoubleCache extends ImageCache {
MemoryCache mImageCache = new MemoryCache();
DiskCache dImageCache = new DiskCache();
@Override
public void put(String url, Bitmap bm) {
mImageCache.put(url, bm);
mImageCache.put(url, bm);
}
@Override
public Bitmap get(String url) {
Bitmap bm = mImageCache.get(url);
if (bm == null) {
return dImageCache.get(url);
}
return bm;
}
}
最后在ImageLoader里面添加一個依賴注入方法
public void setCache(ImageCache cache) {this.cache = cache;}
就實現(xiàn)了
</pre>
<h3>L 里氏替換原則(Liskov Substitution Principle)</h3>
解釋:
所有引用基類的地方必須能透明地使用其子類的對象。
只要父類能出現(xiàn)的地方子類就可以出現(xiàn)搜锰,而且替換成子類也不會產(chǎn)生任何錯誤或異常

里氏替換原則的核心原理是抽象伴郁,抽象又依賴于繼承這個特性,在OOP當(dāng)中蛋叼,繼承的優(yōu)缺點都相當(dāng)明顯:
優(yōu)點:

  1. 代碼重用焊傅,減少創(chuàng)建類的成本,每個子類都擁有父類的方法和屬性
  2. 子類與父類基本想死狈涮,但又與父類有所區(qū)別
  3. 提高代碼的可擴展性

缺點:

  1. 繼承是侵入性的狐胎,只要繼承就必須擁有父類所有屬性和方法**
  2. 可能造成子類代碼冗余,靈活度降低**
    <pre>
    那么父類和子類在上面的代碼哪里顯示了薯嗤?就是一下這句依賴注入
    public void setCache(ImageCache cache) { this.cache = cache;}
    作為ImageCache的子類顽爹,DiskCache和DoubleCache都可以帶入,而不會影響到原本的結(jié)果
    </pre>
    <h3>I 接口隔離原則 (Interface Segregation Principle)</h3>
    解釋:
    客戶端不應(yīng)該依賴他不需要的接口
    類的依賴關(guān)系應(yīng)該建立在最小的接口上
    讓客戶端依賴的接口盡可能小
    <pre>
    接口隔離原則骆姐,其實相比來說是更簡單镜粤,當(dāng)前類如果功能上無相關(guān),則可以新建一個類專門處理該接口玻褪。
    例如上面代碼的這部分:
    try {
    fos = new FileOutputStream(cachePath + File.separator + fileUrl);
    bm.compress(Bitmap.CompressFormat.PNG, 100, fos);
    } catch (FileNotFoundException e) {
    e.printStackTrace();
    } finally {
    if (fos!=null){
    try {
    fos.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
    </pre>
    FileOutputStream的close方式所導(dǎo)致的IOException肉渴,在這里顯得多余,所以把它隔離開新增個類來封裝好
    <pre>public class CloseUtils {
    public static void closeQuietly(Closeable closeable) {
    if (null != closeable) {
    try {
    closeable.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }}}
    變成以下代碼
    try {
    fos = new FileOutputStream(cachePath + File.separator + fileUrl);
    bm.compress(Bitmap.CompressFormat.PNG, 100, fos);
    } catch (FileNotFoundException e) {
    e.printStackTrace();
    } finally {
    CloseUtils.closeQuietly(fos);//接口隔離带射,在另一個實現(xiàn)類里面負責(zé)
    }
    </pre>
    <h3>D 依賴倒置原則(Dependence Inversion Principle)</h3>
    解釋:
    高層模塊不應(yīng)該依賴低層模塊同规,兩者之間都應(yīng)該依賴抽象
    抽象不應(yīng)該依賴細節(jié)(實現(xiàn)類),細節(jié)應(yīng)該依賴抽象

模塊間的依賴關(guān)系通過抽象發(fā)生窟社,實現(xiàn)類之間不發(fā)生直接的依賴關(guān)系券勺,其依賴關(guān)系是通過接口或抽象類產(chǎn)生的。

for ex:
如果是直接實現(xiàn)類與實現(xiàn)類之間發(fā)生聯(lián)系灿里,那么MemoryCache和ImageLoader关炼,就會有沖突,所以需要一個setCache方法來設(shè)置依賴,以后如果需要修改的話匣吊,寫類直接繼承ImageCache或其子類儒拂,就可以了

<pre>
實體類與實體類之間,如果發(fā)生改變色鸳,通過依賴注入的方式更改
ImageLoader.setCache(ImageCache cache);
</pre>
<h3>最少知識原則 (迪米特原則)(Dependence Inversion Principle)</h3>
解釋:
一個對象應(yīng)該對其它對象有最小的了解社痛。或者一個類應(yīng)該對自己需要耦合或調(diào)用的類知道得最少命雀,類的內(nèi)部如何實現(xiàn)與調(diào)用者或者依賴者關(guān)系都沒關(guān)系蒜哀,調(diào)用者只需要它需要的方法就可以了

迪米特原則,是我覺得最為容易理解的咏雌!一個類凡怎,負責(zé)一個功能校焦,那么盡量地它必須要和其它的類之間的關(guān)系少一點!只和直接接觸的類進行聯(lián)系统倒,例如寨典,找房子,租戶的條件需要篩選價格地區(qū)房匆,租戶從中介中找到房源耸成,然后查看哪家好,下結(jié)論浴鸿,這怎么可能井氢!我們都找中介了,那么要把自己的條件和中介說岳链,說好了再看花竞,那么我們的直接關(guān)系對象就是中介。所以租戶其實是沒必要和房間數(shù)據(jù)做交流掸哑,不過現(xiàn)實我們還是要去看房子约急。

第二個例子,上面寫的ImageCache及其子類苗分,ImageLoader.setCache(ImageCache cache),用戶根本不需要知道ImageCache里面具體的實現(xiàn)是啥厌蔽,我們只需要知道ImageCache是怎么使用的,就是里面的put和get摔癣!因為ImageCache才是和ImageLoader直接打交道的類奴饮,DiskCache和DoubleCache是ImageCache具體的實現(xiàn)類,But who care择浊,用戶又不關(guān)心這個戴卜。

<h3>總結(jié)</h3>
面向?qū)ο笳Z言的三大特點:繼承、封裝琢岩,多態(tài)叉瘩,而程序擴展性取決于代碼的耦合度,保持一個程序高類聚低耦合是我們寫代碼需要注意的事情粘捎。當(dāng)然,這只是個理想的狀態(tài)危彩。沒有百分百攒磨,不過,靈活使用接口汤徽,盡量做到這幾個原則娩缰,未免以后太多Bug的出現(xiàn)。我在寫這篇文章的時候谒府,在想面向?qū)ο缶幊毯孟窈芏喽寂c接口相關(guān)拼坎,利用接口作為父類(里氏替換浮毯,開閉原則),添加依賴注入(依賴倒置)泰鸡,然后代碼功能分離(單一職責(zé)债蓝,接口隔離),數(shù)據(jù)邏輯和頁面分離(迪米特原則)盛龄,好像也可以這樣解釋饰迹,當(dāng)然,畢竟是設(shè)計思想余舶,看自己怎么去理解

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末啊鸭,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子匿值,更是在濱河造成了極大的恐慌赠制,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挟憔,死亡現(xiàn)場離奇詭異钟些,居然都是意外死亡,警方通過查閱死者的電腦和手機曲楚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門厘唾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人龙誊,你說我怎么就攤上這事抚垃。” “怎么了趟大?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵鹤树,是天一觀的道長。 經(jīng)常有香客問我逊朽,道長罕伯,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任叽讳,我火速辦了婚禮追他,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘岛蚤。我一直安慰自己邑狸,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布涤妒。 她就那樣靜靜地躺著单雾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上硅堆,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天屿储,我揣著相機與錄音,去河邊找鬼渐逃。 笑死够掠,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的朴乖。 我是一名探鬼主播祖屏,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼买羞!你這毒婦竟也來了袁勺?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤畜普,失蹤者是張志新(化名)和其女友劉穎期丰,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吃挑,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡钝荡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了舶衬。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片埠通。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖逛犹,靈堂內(nèi)的尸體忽然破棺而出端辱,到底是詐尸還是另有隱情,我是刑警寧澤虽画,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布舞蔽,位于F島的核電站,受9級特大地震影響码撰,放射性物質(zhì)發(fā)生泄漏渗柿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一脖岛、第九天 我趴在偏房一處隱蔽的房頂上張望朵栖。 院中可真熱鬧,春花似錦柴梆、人聲如沸混槐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春揣苏,著一層夾襖步出監(jiān)牢的瞬間悯嗓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工卸察, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留脯厨,地道東北人。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓坑质,卻偏偏與公主長得像合武,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子涡扼,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 單一職責(zé)原則 (SRP) 全稱 SRP , Single Responsibility Principle 單一職...
    米莉_L閱讀 1,765評論 2 5
  • 本文出自《Android源碼設(shè)計模式解析與實戰(zhàn)》中的第一章稼跳。 1、優(yōu)化代碼的第一步——單一職責(zé)原則 單一職責(zé)原則的...
    MrSimp1e0閱讀 1,770評論 1 13
  • 導(dǎo)語 讓你的代碼更加優(yōu)美吃沪。 主要內(nèi)容 單一職責(zé)原則——優(yōu)化代碼的第一步 開閉原則——讓程序更穩(wěn)定靈活 里氏替換原則...
    一個有故事的程序員閱讀 1,153評論 5 16
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法汤善,類相關(guān)的語法,內(nèi)部類的語法票彪,繼承相關(guān)的語法红淡,異常的語法,線程的語...
    子非魚_t_閱讀 31,631評論 18 399
  • 最新在閱讀《Android源碼設(shè)計模式解析與實戰(zhàn)》一書降铸,我覺得寫的很清晰在旱,每一個知識點都有示例,通過示例更加容易理...
    慕涵盛華閱讀 1,945評論 0 3