前言
當(dāng)自己的編碼時(shí)間久了之后涩赢,會(huì)發(fā)現(xiàn)優(yōu)秀的代碼,往往是遵循合理的設(shè)計(jì)模式進(jìn)行開發(fā)的轰胁,這些代碼具備高內(nèi)聚谒主、低耦合的特性,能夠在隨時(shí)變化的需求中赃阀,保持穩(wěn)定性霎肯、靈活性。
本文榛斯,是在 Android 代碼中去尋找「設(shè)計(jì)模式」的影子观游,并不會(huì)很詳細(xì)地展開各個(gè)模式的定義與應(yīng)用。文中選取的例子都是盡量簡(jiǎn)單易懂的驮俗,主要讓大家知道平時(shí)原來(lái)自己也是用著各種設(shè)計(jì)模式懂缕,只是不知道名字而已,開始盤它王凑!
(篇幅有限且網(wǎng)上優(yōu)秀的書籍多搪柑,所以不要想著在這一篇文章弄清楚它們聋丝。注:本人水平有限,不對(duì)的地方工碾,還請(qǐng)指出修正)弱睦。
一、單例模式
記得曾經(jīng)筆試時(shí)就考過寫出單例模式的實(shí)現(xiàn)方式:1渊额、懶漢式(線程安全)况木;2、餓漢式(DCL)旬迹;3火惊、靜態(tài)內(nèi)部類;4奔垦、枚舉實(shí)現(xiàn)(最佳實(shí)現(xiàn))屹耐。
當(dāng)某個(gè)對(duì)象的創(chuàng)建是比較耗時(shí)的,如果頻繁的創(chuàng)建與銷毀的話椿猎,對(duì)性能影響又大张症,既然沒有好的辦法優(yōu)化,那就在內(nèi)存中持有這個(gè)對(duì)象的唯一實(shí)例鸵贬,減少內(nèi)存占用俗他。
值得注意的是:1、單例對(duì)象創(chuàng)建的線程安全問題阔逼;2兆衅、Android 中創(chuàng)建單例時(shí),如果持有 Context 容易導(dǎo)致內(nèi)存泄露嗜浮,盡量使用 Application Context羡亩。
例如 Glide
、ImageLoader
圖片加載框架危融,正是采用單例模式畏铆,來(lái)獲取實(shí)例對(duì)象的。
1吉殃、Glide.with(context).load(imageUrl).into(imageView);
2辞居、ImageLoader.getInstance().displayImage(imageUrl,imageView);
二、Builder 模式
構(gòu)建者模式把一個(gè)對(duì)象的創(chuàng)建與表示分離開了蛋勺,也就是說構(gòu)建的過程不同瓦灶,會(huì)生產(chǎn)出不同的對(duì)象出來(lái)。同時(shí) Builder 類抱完,把具體產(chǎn)品的創(chuàng)建細(xì)節(jié)隱藏了贼陶,使得我們不用關(guān)注產(chǎn)品具體是怎么實(shí)現(xiàn)的。例如:我們無(wú)需關(guān)注調(diào)用方法的順序,因?yàn)?Builder 類已經(jīng)封裝好了調(diào)用的順序了碉怔。還有這種鏈?zhǔn)秸{(diào)用寫起來(lái)真的很爽烘贴!
最常見的就是我們 Android 里面的對(duì)話框的創(chuàng)建過程了,我們通過 AlertDialog.Builder()
構(gòu)建的過程中撮胧,有沒有設(shè)置按鈕庙楚、標(biāo)題、提示等趴樱,其實(shí)創(chuàng)建出來(lái)的對(duì)話框風(fēng)格是不一樣的。
1酪捡、new AlertDialog.Builder()
.setTitle("title")
.setMessage("message")
.setPositiveButton("ok", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
// do something
}
}).create();
2叁征、new StringBuilder().append("A").append("B").append("C").toString();
這里介紹一個(gè) Android Studio
的插件 —— Builder Generator
,這個(gè)插件可以省去手寫 Builder
的煩惱逛薇。
三捺疼、工廠模式
有以下幾種:
- 簡(jiǎn)單工廠:含靜態(tài)方法,也叫靜態(tài)工廠永罚,多用 if...else 來(lái)做分支啤呼,去創(chuàng)建各個(gè)實(shí)例,方法內(nèi)部如果創(chuàng)建的對(duì)象多的呢袱,會(huì)略顯臃腫
- 工廠模式:含抽象工廠官扣、具體工廠、抽象產(chǎn)品羞福、具體產(chǎn)品之分惕蹄,符合“開閉原則”
- 抽象工廠模式:與工廠模式的區(qū)別,工廠模式創(chuàng)建單一產(chǎn)品治专,抽象工廠能創(chuàng)建多種產(chǎn)品卖陵,符合“開閉原則”
工廠類封裝好類的實(shí)例化過程,隱藏了對(duì)象實(shí)例化的具體參數(shù)张峰,只需傳入要?jiǎng)?chuàng)建類的唯一標(biāo)識(shí)泪蔫,工廠類就能創(chuàng)建出指定的類。換句話說:我只告訴工廠我要什么喘批,工廠只負(fù)責(zé)生產(chǎn)撩荣,我負(fù)責(zé)使用,具體工廠怎么生產(chǎn)饶深,我就不管啦~
public class ConcreteFactory extends Factory {
public <T extends Product> T createProduct(Class<T> c){
Product product=null;
try {
product = (Product)Class.forName(c.getName()).newInstance();
} catch (Exception e) {
//異常處理
}
return (T)product;
}
}
如 Retrofit
可添加 Gson
轉(zhuǎn)換(這里體現(xiàn)了 Builder 模式 和工廠模式)
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
四婿滓、策略模式
將算法單獨(dú)封裝起來(lái),使之替換時(shí)粥喜,互不影響凸主。
之前看過一篇文章說怎么消除項(xiàng)目代碼中的 if...else ,運(yùn)用策略模式额湘,將每個(gè) if...else 里面的方法各自封裝卿吐,來(lái)解決因?yàn)榇罅康?if...else 導(dǎo)致的類臃腫旁舰。
例如:android 中的設(shè)置動(dòng)畫的插值器,替換不同插值器嗡官,各不影響箭窜,而且效果不同
Animation animation = new AlphaAnimation(1,0);
animation.setInterpolator(new AccelerateDecelerateInterpolator());
imageView.setAnimation(animation);
animation.start();
五、模版模式
在抽象類中衍腥,定義模版(抽象)方法磺樱,并在子類做具體的實(shí)現(xiàn)。其實(shí)就是我們經(jīng)常用的 BaseActivity
婆咸、BaseFragment
那套東西竹捉,show code~
在 BaseActivity
里面保證模版方法,按照順序執(zhí)行尚骄,同時(shí)子類必須實(shí)現(xiàn)父類定義的抽象方法块差,提高了復(fù)用性以及擴(kuò)展性。
public abstract class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 1倔丈、獲取布局id
setContentView(getLayoutId());
// 2憨闰、初始化 view
initView();
// 3、初始化數(shù)據(jù)
initData();
}
public abstract int getLayoutId();
public abstract void initView();
public abstract void initData();
}
五需五、適配器模式
適配器模式鹉动,可以解決接口不兼容的問題,使得本不兼容的接口一起工作宏邮。
舉例:港版 iPhone 的充電器是三孔插頭训裆,可是現(xiàn)在房間只有二孔插頭,所以我得網(wǎng)上買個(gè)三孔轉(zhuǎn)二孔的轉(zhuǎn)換器(相當(dāng)于適配器)蜀铲,這樣我的三孔充電器就能在二孔插座使用了边琉。
我們常用 ListView
使用的 Adapter
,用的就是適配器模式记劝,Google 開發(fā)工程師变姨,設(shè)計(jì)代碼的時(shí)候,考慮到 ListView
每個(gè) ItemView
有不同 UI
厌丑。為了應(yīng)對(duì)這種可變性定欧,BaseAdapter 提供 getView() 方法,以保證最后輸出的統(tǒng)一為 View怒竿。
public class MyAdapter extends BaseAdapter {
@Override
public int getCount() {
return 0;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return null;
}
}
六砍鸠、觀察者模式
觀察者模式,多以一對(duì)多的形式依賴存在耕驰。多個(gè)觀察者同時(shí)監(jiān)聽著被監(jiān)聽的對(duì)象時(shí)爷辱,當(dāng)被監(jiān)聽的對(duì)象發(fā)生狀態(tài)變化時(shí)候,會(huì)通知所有觀察者更新。
如:郵件的訂閱功能饭弓,訂閱某個(gè)模塊双饥,當(dāng)這個(gè)模塊有新的內(nèi)容更新,會(huì)給所有訂閱者發(fā)送郵件弟断。
Android 中咏花,觀察者模式使用的是比較頻繁的,例如:EventBus
阀趴、RxJava
等昏翰。最熟悉的就是 Adapter
的 notifyDataSetChanged()
方法了,大家可以點(diǎn)進(jìn)去看源碼刘急,當(dāng)數(shù)據(jù)發(fā)生改變的時(shí)候棚菊,通知 itemView
重新布局。
/**
* 觀察者集合
*/
private final DataSetObservable mDataSetObservable = new DataSetObservable();
//此處排霉,省略很多代碼...
/**
* Notifies the attached observers that the underlying data has been changed
* and any View reflecting the data set should refresh itself.
*/
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}