面向?qū)ο缶幊痰牧笤瓌t
更好的可拓展性--迪米特原則
迪米特原則的英文全稱(chēng)是Law of Demeter抵乓,縮寫(xiě)是LOD扭吁,也稱(chēng)為最少知識(shí)原則萨驶。
定義是:一個(gè)對(duì)象應(yīng)該對(duì)其他對(duì)象有最少的了解寄悯。
直白的說(shuō),就是讓對(duì)象只與最直接的朋友進(jìn)行聯(lián)系叠殷。那么什么是最直接朋友呢雹洗?每個(gè)對(duì)象都必然會(huì)與其他類(lèi)有耦合關(guān)系筛璧,兩個(gè)對(duì)象之間互相耦合就為朋友關(guān)系,這種關(guān)系類(lèi)型有很多骡男,比如組合淆游,聚合,依賴(lài)隔盛。
下面犹菱,我們就通過(guò)舉例來(lái)說(shuō)明迪米特原則。
相信在外務(wù)工的朋友吮炕,都有租房的經(jīng)歷腊脱,一般情況下大家都會(huì)通過(guò)中介找房。我們通過(guò)租房進(jìn)行舉例龙亲,我們?cè)O(shè)定條件為:我只要求房子的租金和面積陕凹,中介負(fù)責(zé)將合適的房子介紹給我。以下是代碼實(shí)例鳄炉。
// 房子
public class House {
public float area;
public float price;
public House(float area, float price) {
this.area = area;
this.price = price;
}
@Override
public String toString() {
return "House{" +
"area=" + area +
", price=" + price +
'}';
}
}
// 中介
public class Agency {
List<House> mHouses = new ArrayList<>();
public Agency() {
for (int i = 0; i < 5; i++) {
mHouses.add(new House(100 + i, (100 + i) * 200));
}
}
public List<House> getHouses() {
return mHouses;
}
}
//租戶
public class Tenant {
public float houseArea;
public float housePrice;
public static final float diffPrice = 200.001f;
public static final float diffArea = 0.00001f;
public void rentHouse(Agency agency) {
List<House> mHouses = agency.getHouses();
for (House house : mHouses) {
if (isRightHouse(house)) {
System.out.println("找到合適的房子了杜耙!\n" + house.toString());
break;
}
}
}
private boolean isRightHouse(House house) {
return Math.abs(house.price - housePrice) < diffPrice
&& Math.abs(house.area - houseArea) < diffArea;
}
}
從上面的代碼我們可以看到,我們的租戶類(lèi)(Tenant)不僅依賴(lài)了中介類(lèi)(Agency)拂盯,還要頻繁地與我們的房子類(lèi)(House)打交道泥技。我們的需求是:我只要求房子的租金和面積,中介負(fù)責(zé)將合適的房子介紹給我。但是現(xiàn)在是我們拿到了一大堆房子數(shù)據(jù)然后自己進(jìn)行分析和計(jì)算珊豹,這樣一來(lái)簸呈,中介的功能就被弱化了,并且導(dǎo)致我們的租戶類(lèi)(Tenant)和房子類(lèi)(House)的耦合過(guò)高店茶,因?yàn)樽鳛樽鈶舳酝杀悖覀儽仨氈婪孔?House)的細(xì)節(jié)。而此時(shí)租戶類(lèi)(Tenant)又和中介類(lèi)(Agency)保持聯(lián)系贩幻。這個(gè)時(shí)候我們就有必要進(jìn)行一定的代碼調(diào)整轿腺,去區(qū)分清楚,究竟誰(shuí)是誰(shuí)的直接朋友丛楚,讓我們的代碼清晰起來(lái)族壳。
我們通過(guò)需求進(jìn)行分析,可以得出以下幾點(diǎn):
- 租戶只負(fù)責(zé)租房趣些,提出房子的大小要求和租金要求仿荆,其他的要交給中介來(lái)做。
- 租戶不參與房子的分析比對(duì)坏平。
- 中介負(fù)責(zé)與客戶進(jìn)行交流拢操,并且為客戶進(jìn)行房子的比對(duì)和篩選。
如此一來(lái)舶替,我們可以根據(jù)需求對(duì)代碼進(jìn)行如下調(diào)整:
public class Agency {
List<House> mHouses = new ArrayList<>();
public Agency() {
for (int i = 0; i < 5; i++) {
mHouses.add(new House(100 + i, (100 + i) * 200));
}
}
public House rentHouse(float area, float price) {
for (House house : mHouses) {
if (isRightHouse(area, price, house)) {
System.out.println("找到合適的房子了令境!\n" + house.toString());
return house;
}
}
return null;
}
private boolean isRightHouse(float area, float price, House house) {
return Math.abs(house.price - price) < Tenant.diffPrice
&& Math.abs(house.area - area) < Tenant.diffArea;
}
}
public class Tenant {
public float houseArea;
public float housePrice;
public static final float diffPrice = 200.001f;
public static final float diffArea = 0.00001f;
public void rentHouse(Agency agency) {
System.out.println("找到合適的房子了:" + agency.rentHouse(houseArea, housePrice));
}
}
此處我們經(jīng)過(guò)一些簡(jiǎn)單的調(diào)整,將對(duì)房子是否符合要求的判斷放到了中介里顾瞪,這其實(shí)本就應(yīng)該是中介的職責(zé)舔庶,根據(jù)用戶設(shè)定的條件去尋找房子,找到了就告訴用戶就可以了陈醒。而用戶也不需要知道有多少房源栖茉,每個(gè)房子是什么樣,他只需要詢(xún)問(wèn)中介孵延,中介告知結(jié)果即可吕漂。
從上述代碼我們可以看到,我們通過(guò)一次簡(jiǎn)單的代碼修改后尘应,代碼的耦合性已經(jīng)降低了很多惶凝,同時(shí),他們都達(dá)成了與最直接的朋友聯(lián)系的條件犬钢,將各個(gè)角色從復(fù)雜的關(guān)系中抽離出來(lái)苍鲜,使程序的耦合更低,穩(wěn)定性更好玷犹。
這里我們?cè)俅位貞浺幌挛覀僆mageLoader中的代碼混滔,就拿SD卡緩存來(lái)說(shuō),ImageCache就是用戶的直接朋友,而SD卡緩存內(nèi)部卻是使用了DiskCache實(shí)現(xiàn)坯屿,這個(gè)DiskCahce就不是用戶的直接朋友了油湖,因此,其實(shí)用戶完全不需要知道有這樣的緩存存在领跛,用戶只需要和ImageCache打交道就好了乏德,我們之前的代碼我再次優(yōu)化過(guò),使用了JakeWharton的DiskLruCache吠昭,最后代碼如下:
public void put(String url, Bitmap bitmap) {
DiskLruCache.Editor editor = null;
//如果沒(méi)有在內(nèi)存緩存和磁盤(pán)緩存中找到對(duì)對(duì)應(yīng)的緩存喊括,從網(wǎng)絡(luò)上請(qǐng)求數(shù)據(jù)
//寫(xiě)入緩存
editor = mDiskLruCache.edit(url);
try {
if (editor != null) {
OutputStream out = editor.newOutputStream(0);
if (writeBitmapToDisk(value, out)) {
//寫(xiě)入SD卡
editor.commit();
} else {
editor.abort();
}
}
} catch (IOException e) {
e.printStackTrace();
}
CloseUtils.colse(out);
}
用戶在使用SD卡緩存的時(shí)候,根本不知道DiskLruCache的實(shí)現(xiàn)矢棚,這就很好地對(duì)用戶隱藏了具體的代碼實(shí)現(xiàn)郑什。當(dāng)你能夠自己去寫(xiě)DiskLruCache的時(shí)候,你就可以隨意替換掉此處我們引用的JakeWharton大神的DiskLruCache了蒲肋。
總結(jié)#
一不小心蘑拯,六個(gè)章節(jié)的《走向面向?qū)ο蟮牧笤瓌t》就到此結(jié)束了,在這里感謝何紅輝肉津、關(guān)愛(ài)民先生所著的《Android設(shè)計(jì)模式》一書(shū),給我了很多啟發(fā)和靈感舱沧,并且示例上提供了很多借鑒妹沙。
時(shí)間總是很快,快到你都注意不到自己的胡渣子又冒出來(lái)了熟吏,在我們開(kāi)發(fā)的過(guò)程中距糖,最難的不是開(kāi)發(fā)出用戶所需要的功能,而是在后續(xù)的升級(jí)和維護(hù)過(guò)程中牵寺,讓系統(tǒng)能夠擁抱變化悍引,不因?yàn)榘姹镜牡露茐哪K的完整性和代碼的條理性,保證代碼的低耦合帽氓,高可拓展性趣斤,高內(nèi)聚,在經(jīng)理了N個(gè)版本的迭代以后黎休,軟件系統(tǒng)依舊能夠保持穩(wěn)健浓领,靈活,清晰的架構(gòu)势腮。
當(dāng)然啦联贩,這只是一個(gè)理想的情況,代碼迭代而引發(fā)的坑捎拯,我們爬的還少嗎泪幌?為了少爬坑,為了讓自己的代碼更漂亮,系統(tǒng)更穩(wěn)定祸泪,我們必須朝著這個(gè)方向去努力吗浩,遵循面向?qū)ο蟮牧笤瓌t,就是我們走出的浴滴,第一步拓萌。
明天的將會(huì)寫(xiě)關(guān)于單例模式的一些理解和示例,感謝大家的一直支持升略,我們共同進(jìn)步微王!
謝謝。