(一)什么是設(shè)計模式
1. 基本定義:設(shè)計模式(Design pattern)是一套被反復(fù)使用的代碼設(shè)計經(jīng)驗(yàn)的總結(jié)缸废。使用設(shè)計模式的目的是為了可重用代碼讓代碼更容易被他人理解。設(shè)計模式是是軟件工程的基石脈絡(luò)驶社,如大廈的結(jié)構(gòu)一樣呆奕。
2. Design pattern的四大要素:模式名(Name),問題(Question)衬吆,解決方案(Solution)梁钾,效果(Efftive)。
3. OO(面向?qū)ο螅┑牧笤瓌t:單一職責(zé)原則逊抡,開閉原則姆泻,里氏替換原則,依賴倒置原則冒嫡,接口隔離原則拇勃,迪米特原則。
單一職責(zé)原則:一個類中應(yīng)該是一組相關(guān)性很高的函數(shù)孝凌,數(shù)據(jù)的封裝方咆。兩個完全不一樣的功能就不應(yīng)該放在一個類中。
開閉原則:對修改封閉蟀架,對擴(kuò)展放開瓣赂。里氏替換原則:抽象和繼承榆骚;所有引用基類的地方必須能透明的使用其子類的對象。
里氏替換原則:抽象和繼承煌集;所有引用基類的地方必須能透明的使用其子類的對象妓肢。
依賴倒置原則:抽象不應(yīng)該依賴細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴抽象苫纤。
接口隔離原則:將大接口改成多個小接口碉钠。
迪米特原則:也稱為最少知識原則,一個對象應(yīng)該對另一個對象有最少的了解卷拘。
(二)設(shè)計模式的分類
(1)創(chuàng)建型模式5種:
單例模式喊废,抽象工廠模式,工廠模式栗弟,原型模式污筷,建造者模式。(口訣:單原建造者横腿,東西二廠)
(2)結(jié)構(gòu)型模式7種:
適配器模式颓屑,橋接模式,裝飾模式耿焊,組合模式揪惦,外觀模式,享元模式,代理模式。(口訣:一器一橋一代理状勤;裝飾組合外觀)
(3)行為型模式11種:
觀察者模式谒府,中介者模式敦间,訪問者模式,解釋器模式,迭代器模式,備忘錄模式措左,責(zé)任鏈模式,狀態(tài)模式避除,策略模式怎披,命令模式,模板模式瓶摆。(口訣:三者兩器凉逛、一錄一鏈一模板,狀態(tài)策略命令)
單利模式
單利模式有一下特點(diǎn):
1. 單例類只能有一個實(shí)例群井。
2. 單例類必須自己創(chuàng)建自己的唯一實(shí)例状飞。
3. 單例類必須給所有其他對象提供這一實(shí)例。
首先說一下經(jīng)常用的
1.餓漢式
public class Singleton {
? ? private Singleton() {
? ? ? ? //構(gòu)造方法為private,防止外部代碼直接通過new來構(gòu)造多個對象
? ? }
? ? //在類初始化時,已經(jīng)自行實(shí)例化,所以是線程安全的诬辈。
? ? private static final Singleton single? = new Singleton();
? ? //通過getInstance()方法獲取實(shí)例對象
? ? public static Singleton getInstance() {
? ? ? ? return single;
? ? }
}
優(yōu)點(diǎn):寫法簡單酵使,線程安全
缺點(diǎn):沒有懶加載效果,如果沒有使用過的話會造成內(nèi)存浪費(fèi)
2.懶漢式(線程不安全)
public class Singleton {
? ? private static Singleton singleton = null;
? ? public static Singleton getInstance() {
? ? ? ? if (singleton == null){
? ? ? ? ? ? //在第一次調(diào)用getInstance()時才實(shí)例化自晰,實(shí)現(xiàn)懶加載,所以叫懶漢式
? ? ? ? ? ? singleton = new Singleton();
? ? ? ? }
? ? ? ? return singleton;
? ? }
? ? private Singleton() {
? ? }
}
優(yōu)點(diǎn):實(shí)現(xiàn)了懶加載效果
缺點(diǎn):線程不安全
3.懶漢式(線程安全)
public class Singleton {
? ? private static Singleton singleton = null;
? ? //加上synchronized同步
? ? public static synchronized Singleton getInstance() {
? ? ? ? if (singleton == null){
? ? ? ? ? ? //在第一次調(diào)用getInstance()時才實(shí)例化凝化,實(shí)現(xiàn)懶加載,所以叫懶漢式
? ? ? ? ? ? singleton = new Singleton();
? ? ? ? }
? ? ? ? return singleton;
? ? }
? ? private Singleton() {
? ? }
}
優(yōu)點(diǎn):實(shí)現(xiàn)了懶加載稍坯,線程也安全了
缺點(diǎn):使用了synchronized會造成不必要的開銷酬荞,而且大部分時候我們是用不到同步的
4.雙重檢查鎖定(DCL)
public class Singleton {
? ? //volatile 能夠防止代碼的重排序,保證得到的對象是初始化過
? ? private volatile static Singleton singleton = null;
? ? private Singleton() {
? ? }
? ? public static Singleton getInstance() {
? ? ? ? //第一次檢查瞧哟,避免不必要的同步
? ? ? ? if (singleton == null){
? ? ? ? ? ? //同步
? ? ? ? ? ? synchronized (Singleton.class){
? ? ? ? ? ? ? ? //第二次檢查混巧,為null時才創(chuàng)建
? ? ? ? ? ? ? ? if (singleton == null){
? ? ? ? ? ? ? ? ? ? singleton = new Singleton();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return singleton;
? ? }
}
優(yōu)點(diǎn):懶加載,線程安全勤揩,效率高
缺點(diǎn):volatile影響一點(diǎn)性能咧党,高并發(fā)下有一定的缺陷,某些情況下DCL會失效陨亡,雖然概率小
5.靜態(tài)內(nèi)部類
public class Singleton {
? ? private Singleton() {
? ? }
? ? public static Singleton getInstance() {
? ? ? ? //第一次調(diào)用getInstance方法時才加載SingletonHolder并初始化sInstance
? ? ? ? return SingletonHolder.sInsstance;
? ? }
? ? //靜態(tài)內(nèi)部類
? ? private static class SingletonHolder{
? ? ? ? private static final Singleton sInsstance = new Singleton();
? ? }
}
優(yōu)點(diǎn):懶加載傍衡,線程安全,推薦使用
缺點(diǎn):個人還沒發(fā)現(xiàn)
6.枚舉單例
public enum Singleton {
? ? //定義一個枚舉的元素负蠕,它就是Singleton的一個實(shí)例
? ? INSTANCE;
? ? public void doSomething(){
? ? }
}
優(yōu)點(diǎn):線程安全蛙埂,寫法簡單,能防止反序列化重新創(chuàng)建對象
缺點(diǎn):可讀性不高遮糖,枚舉會比靜態(tài)常量多那么一丁點(diǎn)的內(nèi)存
7.使用容器實(shí)現(xiàn)單例模式
//單例管理類
public class SingletonManager {
? ? private static Map<String,Object> objectMap = new HashMap<String, Object>();
? ? public static void registerService(String key,Object instance){
? ? ? ? if (!objectMap.containsKey(key)){
? ? ? ? ? ? objectMap.put(key,instance);//添加單例
? ? ? ? }
? ? }
? ? public static Object getService(String key){
? ? ? ? return objectMap.get(key);//獲取單例
? ? }
}
優(yōu)點(diǎn):方便管理
缺點(diǎn):寫法稍微復(fù)雜
注意事項(xiàng)
1.使用反射會破壞單例模式绣的,所以應(yīng)該慎用反射
Constructor con = Singleton.class.getDeclaredConstructor();
con.setAccessible(true);
Singleton singleton1 = (Singleton) con.newInstance();
Singleton singleton2 = (Singleton) con.newInstance();
//結(jié)果為false,singeton1和singeton2將是兩個不同的實(shí)例
System.out.println(singleton1 == singleton2);
可以通過當(dāng)?shù)诙握{(diào)用構(gòu)造函數(shù)是拋出異常來防止反射破壞單例,以懶漢式為例:
public class Singleton {
? ? private static boolean flag = true;
? ? private static Singleton single = null;
? ? private Singleton() {
? ? ? ? if (flag){
? ? ? ? ? ? flag = ! flag;
? ? ? ? }else {
? ? ? ? ? ? throw new RuntimeException("單例模式被破壞");
? ? ? ? }
? ? }
? ? public static Singleton getInstance(){
? ? ? ? if (single == null){
? ? ? ? ? ? single = new Singleton();
? ? ? ? }
? ? ? ? return single;
? ? }
}
2.反序列化時也能破壞單例模式欲账,可以重寫readResolve方法避免屡江,以餓漢式為例:
public class Singleton implements Serializable {
? ? private Singleton() {
? ? }
? ? private static final Singleton single = new Singleton();
? ? public static Singleton getInstance() {
? ? ? ? return single;
? ? }
? ? //重寫readResolve()
? ? private Object readResolve() throws ObjectStreamException {
? ? ? ? return single;
? ? }
}
應(yīng)用場景
1.頻繁訪問數(shù)據(jù)庫或文件對象。
2.工具類對象
3.創(chuàng)建對象時耗時過多或耗費(fèi)資源過多赛不,但經(jīng)常用到的對象
優(yōu)點(diǎn)
1.內(nèi)存中只存在一個對象惩嘉,節(jié)省了系統(tǒng)資源
2.避免對資源的多重占用,例如一個文件操作踢故,由于只有一個實(shí)例存在內(nèi)存中文黎,避免對統(tǒng)一資源文件的同時操作
缺點(diǎn)
1.獲取對象時不能用new
2.單例對象如果持有Context,那么很容易引發(fā)內(nèi)存泄露畴椰。
3.單例模式一般沒有接口臊诊,擴(kuò)展很困難,若要擴(kuò)展斜脂,只能修改代碼來實(shí)現(xiàn)
建造者模式
定義
將一個復(fù)雜對象的構(gòu)建與它的表示分離抓艳,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示
介紹
1.建造者模式屬于創(chuàng)建型模式
2.建造者模式主要用來創(chuàng)建復(fù)雜的對象,用戶可以不用關(guān)心其建造過程和細(xì)節(jié)
3.例如:當(dāng)要組裝一臺電腦時帚戳,我們選擇好CPU玷或、內(nèi)存儡首、裝機(jī)師傅就把電腦給組裝起來,我們不需要關(guān)心是怎么拼裝起來的
UML類圖
角色說明
Product(產(chǎn)品類) :要創(chuàng)建的復(fù)雜對象偏友。在本類圖中蔬胯,產(chǎn)品類是一個具體的類,而非抽象類位他。實(shí)際編程中氛濒,產(chǎn)品類可以是由一個抽象類與它的不同實(shí)現(xiàn)組成,也可以是由多個抽象類與他們的實(shí)現(xiàn)組成鹅髓,也是可以由多個抽象類與他們的實(shí)現(xiàn)組成舞竿。
Builder(抽象建造者):創(chuàng)建產(chǎn)品的抽象接口,一般至少有一個創(chuàng)建產(chǎn)品的抽象方法和一個返回產(chǎn)品的抽象方法窿冯。引入抽象類骗奖,是為了更容易擴(kuò)展。
ConcreteBuilder(實(shí)際的建造者):繼承Builder類醒串,實(shí)現(xiàn)抽象類的所以抽象方法执桌。實(shí)現(xiàn)具體的建造過程和細(xì)節(jié)。
Director(指揮者類):分配不同的建造者來創(chuàng)建產(chǎn)品芜赌,統(tǒng)一組裝流程仰挣。
實(shí)現(xiàn)
1.定義具體的產(chǎn)品(Product):電腦
public class Computer {
? ? private String mCPU;
? ? private String mMemory;
? ? private String mHD;
? ? public void setmCPU(String mCPU) {
? ? ? ? mCPU = mCPU;
? ? }
? ? public void setmMemory(String mMemory) {
? ? ? ? mMemory = mMemory;
? ? }
? ? public void setmHD(String mHD) {
? ? ? ? mHD = mHD;
? ? }
}
2.定義抽象建造者(Builder):組裝電腦的過程
public abstract class Builder {
? ? //組裝CPU
? ? public abstract void buildCUP(String cpu);
? ? //組裝內(nèi)存
? ? public abstract void buildMemory(String memory);
? ? //組裝硬盤
? ? public abstract void buildeHD(String hd);
? ? //返回組裝好的電腦
? ? public abstract Computer create();
}
3.創(chuàng)建具體的建造者(ConcreteBuilder):裝機(jī)人員
public class ConcreteBuilder extends Builder {
? ? //創(chuàng)建產(chǎn)品實(shí)例
? ? private Computer mComputer = new Computer();
? ? //組裝CPU
? ? @Override
? ? public void buildCUP(String cpu) {
? ? ? ? mComputer.setmCPU(cpu);
? ? }
? ? //組裝內(nèi)存
? ? @Override
? ? public void buildMemory(String memory) {
? ? ? ? mComputer.setmMemory(memory);
? ? }
? ? //組裝硬盤
? ? @Override
? ? public void buildeHD(String hd) {
? ? ? ? mComputer.setmHD(hd);
? ? }
? ? //返回組裝好的電腦
? ? @Override
? ? public Computer create() {
? ? ? ? return mComputer;
? ? }
}
4.定義指揮者類(Director):老板委派任務(wù)給裝機(jī)人員
public class Director {
? ? private Builder mBuilder = null;
? ? public Director(Builder mBuilder) {
? ? ? ? this.mBuilder = mBuilder;
? ? }
? ? //指揮裝機(jī)人員組裝電腦
? ? public void Construct(String cpu, String memory, String hd){
? ? ? ? mBuilder.buildCUP(cpu);
? ? ? ? mBuilder.buildMemory(memory);
? ? ? ? mBuilder.buildeHD(hd);
? ? }
}
5.測試方法
public void CreatComputer() {
? ? Builder builder = new ConcreteBuilder();
? ? Director director = new Director(builder);
? ? director.Construct("i7-6700","三星DDR4","希捷1T");
}
應(yīng)用場景
1.創(chuàng)建一些復(fù)雜的對象時,對象內(nèi)部的構(gòu)鍵過程存在復(fù)雜變化较鼓。
2.相同的構(gòu)建過程椎木,不同的執(zhí)行順序,產(chǎn)生不同結(jié)果時博烂。
3.不同配置的構(gòu)建對象香椎,產(chǎn)生不同結(jié)果時。
優(yōu)點(diǎn)
1.封裝性良好禽篱,隱藏內(nèi)部構(gòu)建細(xì)節(jié)畜伐。
2.易于解耦,將產(chǎn)品本身于產(chǎn)品創(chuàng)建過程進(jìn)行解耦躺率,可以使用相同的創(chuàng)建過程來得到不同的產(chǎn)品玛界,也就說細(xì)節(jié)依賴抽象。
3.易于擴(kuò)展悼吱,具體的建造者類之間相互獨(dú)立慎框,增加新的具體建造者無需修改原有類庫的代碼。
4.易于精確控制對象的創(chuàng)建后添,由于具體的建造者是獨(dú)立的笨枯,因此可以對建造過程逐步細(xì)化,而不對其他的模塊產(chǎn)生任何影響。
缺點(diǎn)
1.產(chǎn)生多余的Build對象以及Dirextor類馅精。
2.建造者模式所創(chuàng)建的產(chǎn)品一般具有較多的共同點(diǎn)严嗜,其組成部分相似;如果產(chǎn)品之間的差異性很大洲敢,則不適合使用建造者模式漫玄,因此其使用范圍受到一定的限制。
3.如果產(chǎn)品的內(nèi)部復(fù)雜压彭,可能會導(dǎo)致需要定義很多具體建造者來實(shí)現(xiàn)這種變化睦优,導(dǎo)致系統(tǒng)變得很龐大。
Android中的源碼分析
Android中的AlertDialog.Builder就是使用了Builder模式來構(gòu)建AlertDialog的哮塞。
AlertDialog.Builder的簡單用法
//創(chuàng)建一個builder對象
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setIcon(R.drawable.aa);
builder.setTitle("標(biāo)題");
builder.setMessage("信息");
builder.setPositiveButton("確定", new DialogInterface.OnClickListener() {
? ? @Override
? ? public void onClick(DialogInterface dialog, int which) {
? ? }
});
//創(chuàng)建AlertDialog對象
AlertDialog alertDialog = builder.create();
//展示AlertDialog
alertDialog.show();
通過Builder對象來構(gòu)建lcon刨秆、Title凳谦、Message等忆畅,講AlertDialog的構(gòu)建過程和細(xì)節(jié)隱藏了起來
AlertDialog相關(guān)源碼分析
public class AlertDialog extends AppCompatDialog implements DialogInterface {
final AlertController mAlert;
protected AlertDialog(@NonNull Context context, @StyleRes int themeResId) {
? ? super(context, resolveDialogTheme(context, themeResId));
//創(chuàng)建AlertController對象
? ? this.mAlert = new AlertController(this.getContext(), this, this.getWindow());
? }
//設(shè)置Title
public void setTitle(CharSequence title) {
? ? super.setTitle(title);
//保存在AlertController對象中
? ? this.mAlert.setTitle(title);
}
//設(shè)置Message
public void setMessage(CharSequence message) {
//保存在AlertController對象中
? ? this.mAlert.setMessage(message);
}
//設(shè)置Icon
public void setIcon(int resId) {
//保存在AlertController對象中
? ? this.mAlert.setIcon(resId);
}
protected void onCreate(Bundle savedInstanceState) {
? ? super.onCreate(savedInstanceState);
//安裝AlertDialog的內(nèi)容
? ? this.mAlert.installContent();
}
//AlertDialog其他代碼略
public static class Builder {
//構(gòu)建AlertDialog對象所需要的參數(shù)都存放在P中
private final AlertParams P;
public Builder(@NonNull Context context) {
? ? this(context, AlertDialog.resolveDialogTheme(context, 0));
}
public Builder(@NonNull Context context, @StyleRes int themeResId) {
//初始化AlertParams對象
? ? this.P = new AlertParams(new ContextThemeWrapper(context, AlertDialog.resolveDialogTheme(context, themeResId)));
? ? this.mTheme = themeResId;
}
public Context getContext() {
? ? return this.P.mContext;
}
public AlertDialog.Builder setTitle(@Nullable CharSequence title) {
//保存title到P中
? ? this.P.mTitle = title;
? ? return this;
}
public AlertDialog.Builder setMessage(@Nullable CharSequence message) {
//保存message
? ? this.P.mMessage = message;
? ? return this;
}
public AlertDialog.Builder setIcon(@DrawableRes int iconId) {
//保存IconId
? ? this.P.mIconId = iconId;
? ? return this;
}
//Builder其他代碼略
public AlertDialog create() {//構(gòu)建AlertDialog
AlertDialog dialog = new AlertDialog(this.P.mContext, this.mTheme);
//將P中的參數(shù)設(shè)置到AlertController中
this.P.apply(dialog.mAlert);
//其他設(shè)置代碼略
return dialog;
}
}
}
//Dialog源碼
public class Dialog implements DialogInterface, Window.Callback, KeyEvent.Callback, View.OnCreateContextMenuListener, Window.OnWindowDismissedCallback {
? ? ? ? //其他代碼略
? ? ? ? public void show() {
? ? ? ? ? ? //前面代碼略
? ? ? ? ? ? if (!mCreated) {
//分發(fā)onCreate
? ? ? ? ? ? ? ? dispatchOnCreate(null);
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? final Configuration config=mContext.getResources().getConfiguration();
? ? ? ? ? ? ? ? mWindow.getDecorView().dispatchConfigurationChanged(config);
? ? ? ? ? ? }
? ? //調(diào)用onStart()
? ? ? ? ? ? onStart();
? ? ? ? ? ? mDecor = mWindow.getDecorView();
? ? ? ? ? ? //設(shè)置參布局參數(shù)略
? ? ? ? ? ? mWindowManager.addView(mDecor, l);//添加到WindowManager
? ? ? ? ? ? mShowing = true;
? ? ? ? ? ? sendShowMessage();
? ? ? ? }
? ? ? ? //分發(fā)onCreate
? ? ? ? void dispatchOnCreate(Bundle savedInstanceState) {
? ? ? ? ? ? if (!mCreated) {
//調(diào)用AlertDialog的onCreate方法,創(chuàng)建AlertDialog視圖
? onCreate(savedInstanceState);
? ? ? ? ? ? ? ? mCreated = true;
? ? ? ? ? ? }
? ? ? ? }
? ? }
//AlertController源碼
public class AlertController {
//其他代碼略
public void installContent() {//安裝內(nèi)容
//選擇合適的布局
int contentView = selectContentView();
//布局添加到Window中
mWindow.setContentView(contentView);
//把dialog.mAlert對象中需要構(gòu)建的元素逐個添加設(shè)置到Window上,即構(gòu)建我們設(shè)置的布局發(fā)生在這一步中
setupView();
? ? ? }
}
簡單流程說明
1.通過AlertDialog.Builder設(shè)置各種屬性后(如:setTitle())尸执,這些屬性信息會保存在P變量中家凯,P變量的類型為AlertController.AlertParams。
2.調(diào)用builder.create()即可返回一個AlertDialog對象如失。
2.1builder.create()方法中首先會創(chuàng)建一個AlertDialog對象绊诲,AlertDialog對象構(gòu)造時會初始化WindowManager和Window。
2.2builder.create()創(chuàng)建完AlertDialog對象后褪贵,會調(diào)用P.apply(dialog.mAlert)掂之;即把P變量中所存儲的用來構(gòu)建AlertDialog對象的元素設(shè)置到了dialog.mAlert中,dialog.mAlert的類型為AlertController脆丁。
3.調(diào)用AlertDialog的show()方法世舰,展示界面。
3.1show()方法中會調(diào)用dispatchOnCreate(null)槽卫,dispatchOnCreate(null)調(diào)起onCreate(),onCreate()會調(diào)起mAlert.installContent()跟压;即安裝AlertDialog的內(nèi)容。
3.2installContent()中會調(diào)用mWindow.setContentView(mAlertDialogLayout);即把mAlertDialogLayout這個布局加到Window中去歼培。
3.3 調(diào)完mWindow.setContentView(mAlertDialogLayout)后會調(diào)用setupView()震蒋,setupView()中會把dialog.mAlert對象中需要構(gòu)建的元素逐個添加設(shè)置到mWindow上。
3.4 最后通過把view添加到mWindowManager上展示出來躲庄。
總結(jié)
1.builder模式隱藏了這種復(fù)雜的構(gòu)建過程查剖,只需幾行簡單的代碼就把AlertDialog給展示出來了。
2.AlertDialog的builder中并沒有抽象建造者(Builder)噪窘、Director(指揮者類)等角色笋庄。AlertDialog.Builder同時扮演了Builder、ConcreteBuilder、Director等角色无切,這是Android中的一種簡化荡短,也值得我們?nèi)W(xué)習(xí)使用。
簡單工廠模式
定義
定義一個用于創(chuàng)建對象的接口哆键,讓子類決定實(shí)例化那個類
介紹
簡單工廠模式屬于創(chuàng)建模式
簡單工廠模式又叫做靜態(tài)工廠模式
UML類圖
角色說明
Product(抽象產(chǎn)品類):要創(chuàng)建的復(fù)雜對象掘托,定義對象的公共接口。
ConcreteProduct(具體產(chǎn)品類):實(shí)現(xiàn)Product接口籍嘹。
Factory(工廠類):返回ConcreteProduct實(shí)例闪盔。
實(shí)現(xiàn)
1.創(chuàng)建抽象產(chǎn)品類,定義公共接口:
//抽象產(chǎn)品類
public abstract class Product {
? ? public abstract void show();
}
2.創(chuàng)建具體產(chǎn)品類辱士,繼承Product類:
//具體產(chǎn)品類A
public class ProductA extends Product {
? ? @Override
? ? public void show() {
? ? ? ? System.out.println("product A");
? ? }
? ? //具體產(chǎn)品類B
? ? public class ProductB extends Product{
? ? ? ? @Override
? ? ? ? public void show() {
? ? ? ? ? ? System.out.println("product B");
? ? ? ? }
? ? }
}
3.創(chuàng)建具體工廠類泪掀,創(chuàng)建具體的產(chǎn)品:
public class Factory {
? ? public static Product create(String productName) {
? ? ? ? Product product = null;
? ? ? ? //通過switch語句控制生產(chǎn)那種商品
? ? ? ? switch (productName) {
? ? ? ? ? ? case "A":
? ? ? ? ? ? ? ? product = new ProductA();
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? case "B":
? ? ? ? ? ? ? ? product = new ProductB();
? ? ? ? ? ? ? ? break;
? ? ? ? }
? ? ? ? return product;
? ? }
}
4.測試方法:
private void test() {
? ? //生產(chǎn)ProductA
? ? Factory.create("A").show();
? ? //生產(chǎn)ProductB
? ? Factory.create("B").show();
? ? try{
? ? ? ? //生產(chǎn)ProductC
? ? ? ? Factory.create("C").show();
? ? }catch (NullPointerException e){
? ? ? ? //沒有ProductC,會報錯
? ? ? ? System.out.println("沒有ProductC");
? ? }
}
應(yīng)用場景
生成復(fù)雜對象時,確定只有一個工廠類颂碘,可以使用簡單工廠模式异赫。否則有多個工廠類的話,使用工廠模式方法头岔。
優(yōu)點(diǎn)
代碼解耦塔拳,創(chuàng)建實(shí)例的工作與使用實(shí)例的工作分開,使用者不必關(guān)心類對象如何創(chuàng)建峡竣。
缺點(diǎn)
1.違背開放封閉原則靠抑,若需添加新產(chǎn)品則必須修改工廠類邏輯,會造成工廠邏輯過于復(fù)雜
2.簡單工廠模式使用靜態(tài)工廠犯法适掰,因此靜態(tài)方法不能被繼承和重寫
3.工廠類包含所有實(shí)例(產(chǎn)品)的創(chuàng)建邏輯颂碧,若工廠類出錯,則會造成整個系統(tǒng)都會受到影響
工廠方法模式與簡單工廠模式比較
1.工廠方法模式有抽象工廠類类浪,簡單工廠模式?jīng)]有抽象工廠類且其工廠類的工廠方法是靜態(tài)的
2.工廠方法模式新增產(chǎn)品時只需要新建一個工廠類即可载城,符合開放封閉原則;而簡單工廠模式需要直接修改工廠類戚宦,違反了開放封閉原則
簡單工廠模式的優(yōu)化
由于簡單工廠模式新增產(chǎn)品是需要直接修改工廠類个曙,違反了開放封閉原則。因此可以使用反射來創(chuàng)建實(shí)例對象受楼,確保能夠遵循開放封閉原則
反射實(shí)現(xiàn)工廠類
public class Factory {
? ? public static <T extends Product> T create(Class<T> clz){
? ? ? ? Product product = null;
? ? ? ? try{
? ? ? ? ? ? //反射出實(shí)例
? ? ? ? ? product = (Product) Class.forName(clz.getName()).newInstance();
? ? ? ? }catch (Exception e){
e.printStackTrace();
? ? ? ? }
? ? ? ? return (T) product;
? ? }
}
測試方法
public void test(){
? ? //生產(chǎn)ProductA
? ? Factory.create(ProductA.class).show();
? ? //生產(chǎn)ProductB
? ? Factory.create(ProductB.class).show();
}
總結(jié)
使用反射來實(shí)現(xiàn)工廠類垦搬,新增產(chǎn)品時無需修改工廠類,但是使用反射來創(chuàng)建實(shí)例對象的話會比正常使用new來創(chuàng)建的要慢艳汽。
觀察者模式
定義
定義對象間的一種一個對多的依賴關(guān)系猴贰,當(dāng)一個對象的狀態(tài)發(fā)送改變時,所以依賴于它的對象都得到通知并被自動更新
介紹
1.觀察者屬于行為型模式
2.觀察者模式又被稱作發(fā)布/訂閱模式
3.觀察者模式主要用來解耦河狐,將被觀察者和觀察者解耦米绕,讓他們之間沒有沒有依賴或者依賴關(guān)系很小
UML類圖
角色說明
Subject(抽象主題):又叫抽象被觀察者瑟捣,把所有觀察者對象的引用保存到一個集合里,每個主題都可以有任何數(shù)量的被觀察者栅干。抽象主題提供一個接口迈套,可以增加和刪除觀察者對象。
ConcreteSubject(具體主題):又叫具體被觀察者桑李,將有關(guān)狀態(tài)存入具體觀察者對象角撞,在具體主題內(nèi)部狀態(tài)改變時,給所有登記過的觀察者發(fā)出通知。
Observer(抽象觀察者):為所有的具體觀察者定義一個接口梆造,在得到主題通知時更新自己烂斋。
ConcreteObserver(具體觀察者):實(shí)現(xiàn)抽象觀察者定義的更新接口歇僧,當(dāng)?shù)玫街黝}更改通知時更新自身的狀態(tài)
實(shí)現(xiàn)
繼續(xù)以送快遞為例柄错,快遞員有時只是把快遞拉到樓下敢伸,然后就通知收件人下樓取快遞钓丰。
創(chuàng)建抽象觀察者
定義一個接到通知的更新方法躯砰,即收件人收到通知后的反應(yīng):
//抽象觀察者
public interface Observer {
? ? //更新方法
? ? public void update(String message);
}
創(chuàng)建具體觀察者
實(shí)現(xiàn)抽象觀察者中的方法,這里創(chuàng)建兩個類携丁,一個男孩類和一個女孩類琢歇,定義他們收到通知后的反應(yīng):
public class Boy implements Observer {
? ? //名字
? ? private String name;
? ? public Boy(String name) {
? ? ? ? this.name = name;
? ? }
? ? //男孩的具體反應(yīng)
? ? @Override
? ? public void update(String message) {
? ? ? System.out.println(name + ",收到了信息:" + message+"屁顛顛的去取快遞.");
? ? }
}
public class Girl implements Observer{
? ? //名字
? ? private String name;
? ? public Girl(String name) {
? ? ? ? this.name = name;
? ? }
? ? //女孩的具體反應(yīng)
? ? @Override
? ? public void update(String message) {
? ? ? System.out.println(name + ",收到了信息:" + message+"讓男朋友去取快遞~");
? ? }
}
創(chuàng)建抽象主題
即抽象被觀察者,定義添加梦鉴,刪除李茫,通知等方法:
//抽象被觀察者
public interface Observable {
? ? //添加觀察者
? ? void add(Observer observer);
? ? //刪除觀察者
? ? void remove(Observer observer);
? ? //通知觀察者
? ? void notify(String message);
}
創(chuàng)建具體主題
即具體被觀察者,也就是快遞員尚揣,派送快遞時根據(jù)快遞信息來通知收件人讓其來取件:
//快遞員
public class Postman implements Observable {
? ? //保存收件人(觀察者)的信息
? ? private List<Observer> personList = new ArrayList<>();
? ? //添加收件人
? ? @Override
? ? public void add(Observer observer) {
? ? ? ? personList.add(observer);
? ? }
? ? //移除收件人
? ? @Override
? ? public void remove(Observer observer) {
? ? ? ? personList.remove(observer);
? ? }
? ? //逐一通知收件人(觀察者)
? ? @Override
? ? public void notify(String message) {
? ? ? ? for (Observer observer : personList){
? ? ? ? ? ? observer.update(message);
? ? ? ? }
? ? }
}
客戶端測試
public void test() {
? ? Postman postman = new Postman();
? ? Observer boy1 = new Boy("路飛");
? ? Observer boy2 = new Boy("路飛");
? ? Observer girl = new Girl("娜美");
? ? postman.add(boy1);
? ? postman.add(boy2);
? ? postman.add(girl);
? ? postman.notify("快遞到了涌矢,請下樓領(lǐng)取");
}
說明
實(shí)際上,JDK內(nèi)部也內(nèi)置了Observable(抽象被觀察者)快骗,Observer(抽象觀察者)這兩個類娜庇,我們也可以直接拿來有塔次,其代碼如下:
//抽象觀察者
public interface Observer {
? ? //更新方法
? ? void update(Observable observable, Object arg);
}
//抽象被觀察者
public class Observable {
? ? //定義改變狀態(tài),默認(rèn)為false
? ? private boolean changed = false;
? ? //定義一個觀察者list
? ? private final ArrayList<Observer> observers;
? ? //構(gòu)造函數(shù)名秀,初始化一個觀察者list來保存觀察者
? ? public Observable() {
? ? ? ? observers = new ArrayList<>();
? ? }
? ? //添加觀察者励负,帶同步字段的,所以是線程安全的
? ? public synchronized void addObserver(Observer o) {
? ? ? ? if (o == null)
? ? ? ? ? ? throw new NullPointerException();
? ? ? ? if (!observers.contains(o)) {
? ? ? ? ? ? observers.add(o);
? ? ? ? }
? ? }
? ? //刪除觀察者
? ? public synchronized void deleteObserver(Observer o) {
? ? ? ? observers.remove(o);
? ? }
? ? //通知所以觀察者匕得,無參數(shù)
? ? public void notifyObservers() {
? ? ? ? notifyObservers(null);
? ? }
? ? //通知所有觀察者继榆,帶參數(shù)
? ? public void notifyObservers(Object arg) {
? ? ? ? Observer[] arrLocal;
? ? ? ? //加synchronized字段,保證多線程下操作沒有問題
? ? ? ? synchronized (this) {
? ? ? ? ? ? //這里做了是否發(fā)生改變的判斷汁掠,是為了防止出現(xiàn)無意義的更新
? ? ? ? ? ? if (!hasChanged())
? ? ? ? ? ? ? ? return;
? //ArrayList轉(zhuǎn)換成一個臨時的數(shù)組略吨,這樣就防止了通知,添加考阱,移除同時發(fā)生可能導(dǎo)致的異常
? ? ? ? ? ? arrLocal = observers.toArray(new Observer[observers.size()]);
? ? ? ? ? ? //清除改變狀態(tài)翠忠,設(shè)置為false
? ? ? ? ? ? clearChanged();
? ? ? ? }
? ? ? ? //遍歷逐一通知
? ? ? ? for (int i = arrLocal.length-1; i>=0; i--)
? ? ? ? ? ? arrLocal[i].update(this,arg);
? ? }
? ? //清楚所有觀察者
? ? public synchronized void deleteObservers() {
? ? ? ? observers.clear();
? ? }
? ? //設(shè)置被觀察者為改變狀態(tài),設(shè)置為true
? ? protected synchronized void setChanged() {
? ? ? ? changed = true;
? ? }
? ? //清除改變狀態(tài)乞榨,設(shè)置為false
? ? protected synchronized void clearChanged() {
? ? ? ? changed = false;
? ? }
? ? //返回當(dāng)前的改變狀態(tài)
? ? public synchronized boolean hasChanged() {
? ? ? ? return changed;
? ? }
? ? //觀察者數(shù)量
? ? public synchronized int countObservers() {
? ? ? ? return observers.size();
? ? }
}
應(yīng)用場景
1.當(dāng)一個對象的改變需要通知其他對象改變時秽之,而且它不知道具體有多少個對象有待改變時。
2.當(dāng)一個對象必須通知其它對象吃既,而它又不能假定其它對象是誰考榨。
3.跨系統(tǒng)的消息交換場景,如消息隊(duì)列鹦倚、事件總線的處理機(jī)制河质。
優(yōu)點(diǎn)
1.解除觀察者與主題之間的耦合。讓耦合雙方都依賴于抽象申鱼,而不是依賴具體愤诱。從而使得自己的變換都不會影響另一邊的變換。
2.易于擴(kuò)展捐友,對同一主題新增觀察者時無需修改原有代碼。
缺點(diǎn)
1.依賴關(guān)系并未完全解除溃槐,抽象主題仍然依賴抽象觀察者匣砖。
2.使用觀察者模式時需要考慮一下開發(fā)效率和運(yùn)行效率的問題,程序中包括一個被觀察者昏滴、多個觀察者猴鲫,開發(fā)、調(diào)試等內(nèi)容會比較復(fù)雜谣殊,而且在Java中消息的通知一般是順序執(zhí)行拂共,那么一個觀察者卡頓,會影響整體的執(zhí)行效率姻几,在這種情況下势告,一般會采用異步實(shí)現(xiàn)抚恒。
3.可能會引起多余的數(shù)據(jù)通知。
Android中的源碼分析
1.控件中的Listener監(jiān)聽方式
Android中我們遇到最常用的觀察者模式就是各種控件的監(jiān)聽,如下:
Button button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
? ? @Override
? ? public void onClick(View v) {
? ? ? ? Log.d(TAG, "onClick: ");
? ? }
});
上面的代碼中逸嘀,button就是具體的主題厘熟,也就是被觀察者屯蹦;new出來的View.OnClickListener對象就是具體的觀察者;OnClickListener實(shí)際上就是個接口绳姨,也就是抽象觀察者登澜;通過setOnClickListener把觀察者注冊到被觀察者中。
一旦button捕獲的點(diǎn)擊事件飘庄,即狀態(tài)發(fā)生變化的時候脑蠕,就會通過回調(diào)注冊的OnClickListener觀察者的onClick方法會來通知觀察者,Button狀態(tài)發(fā)生變化跪削。
1.相關(guān)源碼分析:
//抽象觀察者
public interface OnClickListener {
//只有onClick這個方法
? ? void onClick(View v);
}
public void setOnClickListener(@Nullable OnClickListener l) {
? ? if (!isClickable()) {
? //設(shè)置為可點(diǎn)擊
? ? ? ? setClickable(true);
? ? }
//把傳入的 OnClickListener 對象賦值給了 getListenerInfo().mOnClickListener,即mListenerInfo的mOnClickListener持有OnClickListener對象的引用
? ? getListenerInfo().mOnClickListener = l;
}
//返回ListenerInfo對象,這里是一個單例模式
ListenerInfo getListenerInfo() {
? ? if (mListenerInfo != null) {
? ? ? ? return mListenerInfo;
? ? }
? ? mListenerInfo = new ListenerInfo();
? ? return mListenerInfo;
}
//執(zhí)行點(diǎn)擊事件
public boolean performClick() {
? ? final boolean result;
? ? final ListenerInfo li = mListenerInfo;
? ? if (li != null && li.mOnClickListener != null) {
? ? ? ? playSoundEffect(SoundEffectConstants.CLICK);
? //執(zhí)行onClick方法,li.mOnClickListener即OnClickListener對象
? ? ? ? li.mOnClickListener.onClick(this);
? ? ? ? result = true;
? ? } else {
? ? ? ? result = false;
? ? }
? ? sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
? ? notifyEnterOrExitForAutoFillIfNeeded(true);
? ? return result;
}
2.Adapter的notifyDataSetChanged()方法
當(dāng)我們使用ListView時谴仙,需要更新數(shù)據(jù)時我們就會調(diào)用Adapter的notifyDataSetChanged()方法,那么我們來看看notifyDataSetChanged()的實(shí)現(xiàn)原理碾盐,這個方法是定義在BaseAdapter中具體代碼如下:
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
//數(shù)據(jù)集被觀察者
? ? private final DataSetObservable mDataSetObservable = new DataSetObservable();
? ? public boolean hasStableIds() {
? ? ? ? return false;
? ? }
? ? //注冊觀察者
? ? public void registerDataSetObserver(DataSetObserver observer) {
? ? ? ? mDataSetObservable.registerObserver(observer);
? ? }
//注銷觀察者
? ? public void unregisterDataSetObserver(DataSetObserver observer) {
? ? ? ? mDataSetObservable.unregisterObserver(observer);
? ? }
? ? //數(shù)據(jù)集改變時晃跺,通知所有觀察者
? ? public void notifyDataSetChanged() {
? ? ? ? mDataSetObservable.notifyChanged();
? ? }
}
//其他代碼略
由上面的代碼可以看出BaseAdapter實(shí)際上就是使用了觀察者模式,BaseAdapter就是具體的被觀察者毫玖。接下來mDataSetObservable.notifyChanged()的實(shí)現(xiàn):
public class DataSetObservable extends Observable<DataSetObserver> {
? ? public void notifyChanged() {
? ? ? ? synchronized(mObservers) {
? //遍歷所有觀察者掀虎,并調(diào)用他們的onChanged()方法
? ? ? ? ? ? for (int i = mObservers.size() - 1; i >= 0; i--) {
? ? ? ? ? ? ? ? mObservers.get(i).onChanged();
? ? ? ? ? ? }
? ? ? ? }
? ? }
//其他代碼略
}
現(xiàn)在我們看到了有觀察者額影子,那么這些觀察者時從哪里來的呢付枫?實(shí)際上這些觀察者是在ListView通過setAdapter()設(shè)置Adapter時產(chǎn)生的:
public class ListView extends AbsListView {
//其他代碼略
public void setAdapter(ListAdapter adapter) {
//如果已存在Adapter烹玉,先注銷該Adapter的觀察者
? ? if (mAdapter != null && mDataSetObserver != null) {
? ? ? ? mAdapter.unregisterDataSetObserver(mDataSetObserver);
? ? }
//其他代碼略
super.setAdapter(adapter);
if (mAdapter != null) {
? ? mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
? ? mOldItemCount = mItemCount;
//獲取Adapter中的數(shù)據(jù)的數(shù)量
? mItemCount = mAdapter.getCount();
? checkFocus();
//創(chuàng)建一個數(shù)據(jù)集觀察者
? ? mDataSetObserver = new AdapterDataSetObserver();
//注冊觀察者
? ? mAdapter.registerDataSetObserver(mDataSetObserver);
//其他代碼略
}
}
從上面的代碼可以看到,觀察者有了阐滩,那么這個觀察者主要是干什么的呢二打?
class AdapterDataSetObserver extends DataSetObserver {
? ? private Parcelable mInstanceState = null;
? ? //觀察者的核心實(shí)現(xiàn)
? ? @Override
? ? public void onChanged() {
? ? ? ? mDataChanged = true;
? ? ? ? mOldItemCount = mItemCount;
//獲取Adapter中的數(shù)據(jù)的數(shù)量
? ? ? ? mItemCount = getAdapter().getCount();
? ? ? ? if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
? ? ? ? ? ? ? ? && mOldItemCount == 0 && mItemCount > 0) {
? ? ? ? ? ? AdapterView.this.onRestoreInstanceState(mInstanceState);
? ? ? ? ? ? mInstanceState = null;
? ? ? ? } else {
? ? ? ? ? ? rememberSyncState();
? ? ? ? }
? ? ? ? checkFocus();
? ? ? ? //重新布局
? ? ? ? requestLayout();
? ? }
? ? //其他代碼略
}
最終就是在AdapterDataSetObserver這個類里面的onChanged()方法中實(shí)現(xiàn)了布局的更新。
簡單總結(jié)
當(dāng)ListView的數(shù)據(jù)發(fā)生變化時掂榔,我調(diào)用Adapter的notifyDataSetChanged()方法继效,這個方法又會調(diào)用所以觀察者(AdapterDataSetObserver)的onChanged()方法症杏,onChanged()方法又會調(diào)用requestLayout()方法重新進(jìn)行布局。
BroadcastReceiver
BroadcastReceiver作為Android的四大組件之一莲趣,實(shí)際上也是一個典型的觀察者模式鸳慈,通過sendBroadcast發(fā)送廣播時,只有注冊了相應(yīng)的IntentFilter的BroadcastReceiver對象才會收到這個廣播信息喧伞,其onReceive方法才會被調(diào)起走芋,BroadcastReceiver的代碼比較復(fù)雜
其他
另外,一些著名的第三方事件總線庫潘鲫,比如RxJava翁逞、RxAndroid、EventBus等等溉仑,也是使用了觀察者模式挖函,有興趣的可以去看一下他們的源碼。
工廠模式
定義
定義一個用于創(chuàng)建對象的接口浊竟,讓子類決定實(shí)例化哪個類
介紹
1.工廠方法模式屬于創(chuàng)鍵型模式怨喘。
2.工廠方法模式主要用來創(chuàng)建復(fù)雜的對象,簡單對象能夠使用new來創(chuàng)建就不用工廠方法模式來創(chuàng)建
UML類圖
角色說明
Product(抽象產(chǎn)品類):要創(chuàng)建的復(fù)雜對象振定,定義對象的公共接口必怜。
ConcreteProduct(具體產(chǎn)品類):實(shí)現(xiàn)Product接口。
Factory(抽象工廠類):該方法返回一個Product類型的對象后频。
ConcreteFactory(具體工廠類):返回ConcreteProduct實(shí)例梳庆。
實(shí)現(xiàn)
1.創(chuàng)建抽象產(chǎn)品類,定義公共接口:
//抽象產(chǎn)品類
public abstract class Product {
? ? public abstract void show();
}
2.創(chuàng)建具體產(chǎn)品類卑惜,繼承Product類:
//具體產(chǎn)品類A
public class ProductA extends Product {
? ? @Override
? ? public void show() {
? ? ? ? System.out.println("product A");
? ? }
}
//具體產(chǎn)品類B
public class ProductB extends Product {
? ? @Override
? ? public void show() {
? ? ? ? System.out.println("product B");
? ? }
}
3.創(chuàng)建抽象工廠類膏执,定義公共接口:
//抽象工廠類
public abstract class Factory {
? ? public abstract Product create();
}
4.創(chuàng)建具體工廠類,繼承抽象工廠類露久,實(shí)現(xiàn)創(chuàng)建具體的產(chǎn)品:
//具體工廠類A
public class FactoryA extends Factory {
? ? @Override
? ? public Product create() {
? ? ? ? //創(chuàng)建ProductA
? ? ? ? return new ProductA();
? ? }
}
//具體工廠類B
public class FactoryB extends Factory {
? ? @Override
? ? public Product create() {
? ? ? ? //創(chuàng)建ProductB
? ? ? ? return new ProductB();
? ? }
}
測試方法:
public void test() {
? ? //產(chǎn)品A
? ? FactoryA factoryA = new FactoryA();
? ? Product productA = factoryA.create();
? ? productA.show();
? ? //產(chǎn)品B
? ? FactoryB factoryB = new FactoryB();
? ? Product productB = factoryB.create();
? ? productB.show();
}
應(yīng)用場景
生成復(fù)雜對象時更米,無需知道具體類名,只需知道想應(yīng)的工廠方法即可毫痕。
優(yōu)點(diǎn)
1.符合開放封閉原則壳快。新增產(chǎn)品時,只需增加相應(yīng)的具體產(chǎn)品類和相應(yīng)的工廠子類即可
2.符合單一職責(zé)原則镇草。每個具體工廠類只負(fù)責(zé)創(chuàng)建對應(yīng)的產(chǎn)品。
缺點(diǎn)
1.一個具體工廠只能創(chuàng)建一種具體產(chǎn)品瘤旨。
2.增加新產(chǎn)品時梯啤,還需要增加相應(yīng)的工廠類,系統(tǒng)類的個數(shù)將成對增加存哲,增加了系統(tǒng)的復(fù)雜度和性能開銷
3.引入的抽象類也會導(dǎo)致類結(jié)構(gòu)的復(fù)雜化
Android中的源碼分析
Android中的ThreadFactory就是使用了工廠方法模式來生成線程的因宇,線程就是ThreadFactory的產(chǎn)品
ThreadFactory相關(guān)源碼分析
//抽象產(chǎn)品:Runnable
public interface Runnable {
? ? public abstract void run();
}
//具體產(chǎn)品:Thread
public class Thread implements Runnable {
//構(gòu)造方法
public Thread(Runnable target, String name) {
? init(null, target, name, 0);
}
@Override
//實(shí)現(xiàn)抽象產(chǎn)品的抽象方法
public void run() {
? ? if (target != null) {
? ? ? ? ? ? target.run();
? ? }
}
//其他代碼略
}
//抽象工廠:ThreadFactory
public interface ThreadFactory {
? ? Thread newThread(Runnable r);
}
//具體工廠:AsyncTask中的實(shí)現(xiàn)
private static final ThreadFactory threadFactory = new ThreadFactory() {
? ? private final AtomicInteger mCount = new AtomicInteger(1);
? ? //實(shí)現(xiàn)抽象工廠的抽象方法
? ? @Override
? ? public Thread newThread(Runnable r) {
? ? ? ? //返回Thread這個產(chǎn)品
? ? ? ? return new Thread(r,"AsyncTask #"+mCount.getAndIncrement());
? ? }
};
總結(jié)
1.這里只要是介紹Android系統(tǒng)中工廠模式的應(yīng)用七婴,線程和AsyncTask的原理就不說了。
2.通過ThreadFactory察滑,我們可以創(chuàng)建出不同的Thread來打厘。
3.同樣,我們可以創(chuàng)建另外類似的工廠贺辰,生產(chǎn)某種專門的線程户盯,非常容易擴(kuò)展。
責(zé)任鏈模式
定義
一個請求沿著一條“鏈”傳遞饲化,直到該“鏈”上的某個處理者處理它為止莽鸭。
介紹
1.責(zé)任鏈模式屬于行為型模式。
2.多個對象中吃靠,每個對象都持有下一個對象的引用硫眨,這就構(gòu)成了鏈這種結(jié)構(gòu)。
3.一個請求通過鏈的頭部巢块,一直往下傳遞到鏈上的每一個結(jié)點(diǎn)礁阁,直到有某個結(jié)點(diǎn)對這個請求做出處理為止,這就是責(zé)任鏈模式族奢。
4.責(zé)任鏈模式一般分為處理者與請求者姥闭。具體的處理者分別處理請求者的行為。
UML類圖
角色說明
Handler(抽象處理者):抽象類或者接口歹鱼,定義處理請求的方法以及持有下一個Handler的引用泣栈。
ConcreteHandler1,ConcreteHandler2(具體處理者):實(shí)現(xiàn)抽象處理類弥姻,對請求進(jìn)行處理南片,如果不處理則轉(zhuǎn)發(fā)給下一個處理者。
Client(客戶端):即要使用責(zé)任鏈模式的地方庭敦。
實(shí)現(xiàn)
以送快遞為例疼进,單個快遞員只負(fù)責(zé)某個片區(qū)的快遞,若某個快遞目的地不屬于當(dāng)前的片區(qū)秧廉,則交給下一個快遞員來處理伞广,直到有人處理為止。
創(chuàng)建抽象處理者類
定義處理請求的方法以及持有下一個Handler的引用:
//快遞員抽象類
public abstract class Postman {
? ? //下一個快遞員
? ? protected Postman nextPostman;
? ? //派送快遞
? ? public abstract void handleCourier(String address);
}
創(chuàng)建具體處理者類
實(shí)現(xiàn)抽象處理者類中的方法:
//北京快遞員
public class BeijingPostman extends Postman{
? ? @Override
? ? public void handleCourier(String address) {
? ? ? ? //北京地區(qū)的則派送
? ? ? ? if (address.equals("Beijing")){
? ? ? ? ? ? System.out.println("派送到北京");
? ? ? ? ? ? return;
? ? ? ? }else{
? ? ? ? ? ? //否則交給下一個快遞員去處理
? ? ? ? ? ? nextPostman.handleCourier(address);
? ? ? ? }
? ? }
}
//上禾鄣纾快遞員
public class ShanghaiPostman extends Postman{
? ? @Override
? ? public void handleCourier(String address) {
? ? ? ? if(address.equals("Shanghai")){
? ? ? ? ? ? System.out.println("派送到上海");
? ? ? ? ? ? return;
? ? ? ? }else{
? ? ? ? ? ? nextPostman.handleCourier(address);
? ? ? ? }
? ? }
}
//廣州快遞員
public class GuangzhouPostman extends Postman{
? ? @Override
? ? public void handleCourier(String address) {
? ? ? ? if(address.equals("Guangzhou")){
? ? ? ? ? ? System.out.println("派送到廣州");
? ? ? ? ? ? return;
? ? ? ? }else{
? ? ? ? ? ? nextPostman.handleCourier(address);
? ? ? ? }
? ? }
}
客戶端測試
public void test() {
? ? //創(chuàng)建不同的快遞員對象
? ? BeijingPostman beijingPostman = new BeijingPostman();
? ? ShanghaiPostman shanghaiPostman = new ShanghaiPostman();
? ? GuangzhouPostman guangzhouPostman = new GuangzhouPostman();
? ? //創(chuàng)建下一個結(jié)點(diǎn)
? ? beijingPostman.nextPostman = shanghaiPostman;
? ? shanghaiPostman.nextPostman = guangzhouPostman;
? ? //處理不同地區(qū)的快遞嚼锄,都是從首結(jié)點(diǎn)北京快遞員開始
? ? System.out.println("有一個上海快遞需要派送:");
? ? beijingPostman.handleCourier("Shanghai");
? ? System.out.println("有一個廣州快遞需要派送:");
? ? beijingPostman.handleCourier("Guangzhou");
? ? System.out.println("有一個美國快遞需要派送:");
? ? beijingPostman.handleCourier("America");
}
說明
1.上面的請求只是一個簡單的地址字符串蔽豺,如果是一些復(fù)雜的請求区丑,可以封裝成獨(dú)立的對象。如:普通快遞和生鮮快遞,生鮮快遞還需快遞員做冷鏈處理等等沧侥。
2.請求實(shí)際上可以從責(zé)任鏈中的任意結(jié)點(diǎn)開始可霎,即可以從上海快遞員開始處理也行宴杀。
3.責(zé)任鏈中的結(jié)點(diǎn)順序?qū)嶋H也可以調(diào)整癣朗,即北京->廣州->上海的順序也行。
4.責(zé)任鏈也可以過某些結(jié)點(diǎn)去處理請求旺罢,如北京->廣州,越過了上海旷余。
5.對于請求,只有兩種結(jié)果:一個某個結(jié)點(diǎn)對其進(jìn)行了處理主经,如上面例子上海荣暮、廣州快遞,這種叫純的責(zé)任鏈罩驻;另一個則是所以結(jié)點(diǎn)都不進(jìn)行處理穗酥,如美國的快遞,這種叫不純的責(zé)任鏈模式惠遏。我們所見到的基本都是不純的責(zé)任鏈砾跃。
應(yīng)用場景
1.多個對象處理同一請求時,但是具體由哪個對象去處理需要運(yùn)行時做判斷节吮。
2.具體處理者不明確的情況下抽高,向這組對象提交了一個請求。
優(yōu)點(diǎn)
1.代碼的解耦透绩,請求者與處理者的隔離分開翘骂。
2.易于擴(kuò)展,新增處理者往鏈上加結(jié)點(diǎn)即可帚豪。
缺點(diǎn)
1.責(zé)任鏈過長的話曲横,或者鏈上的結(jié)點(diǎn)判斷處理時間太長的話會影響性能底洗,特別是遞歸循環(huán)的時候腋寨。
2.請求有可能遍歷完鏈都得不到處理