23種設(shè)計(jì)模式,畢生心血,咳咳吐血去了

(一)什么是設(shè)計(jì)模式

1. 基本定義:設(shè)計(jì)模式(Design pattern)是一套被反復(fù)使用的代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)。使用設(shè)計(jì)模式的目的是為了可重用代碼讓代碼更容易被他人理解。設(shè)計(jì)模式是是軟件工程的基石脈絡(luò),如大廈的結(jié)構(gòu)一樣。

2. Design pattern的四大要素:模式名(Name),問(wèn)題(Question),解決方案(Solution),效果(Efftive)。

3. OO(面向?qū)ο螅┑牧笤瓌t:?jiǎn)我宦氊?zé)原則,開(kāi)閉原則,里氏替換原則,依賴倒置原則,接口隔離原則,迪米特原則。

單一職責(zé)原則:一個(gè)類中應(yīng)該是一組相關(guān)性很高的函數(shù),數(shù)據(jù)的封裝。兩個(gè)完全不一樣的功能就不應(yīng)該放在一個(gè)類中。

開(kāi)閉原則:對(duì)修改封閉,對(duì)擴(kuò)展放開(kāi)。里氏替換原則:抽象和繼承;所有引用基類的地方必須能透明的使用其子類的對(duì)象。

里氏替換原則:抽象和繼承;所有引用基類的地方必須能透明的使用其子類的對(duì)象。

依賴倒置原則:抽象不應(yīng)該依賴細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴抽象。

接口隔離原則:將大接口改成多個(gè)小接口。

迪米特原則:也稱為最少知識(shí)原則,一個(gè)對(duì)象應(yīng)該對(duì)另一個(gè)對(duì)象有最少的了解。

(二)設(shè)計(jì)模式的分類

(1)創(chuàng)建型模式5種:

單例模式,抽象工廠模式,工廠模式,原型模式,建造者模式。(口訣:?jiǎn)卧ㄔ煺?,東西二廠)

(2)結(jié)構(gòu)型模式7種:

適配器模式,橋接模式,裝飾模式,組合模式,外觀模式,享元模式,代理模式。(口訣:一器一橋一代理;裝飾組合外觀)

(3)行為型模式11種:

觀察者模式,中介者模式,訪問(wèn)者模式,解釋器模式,迭代器模式,備忘錄模式,責(zé)任鏈模式,狀態(tài)模式,策略模式,命令模式,模板模式。(口訣:三者兩器、一錄一鏈一模板,狀態(tài)策略命令)

單利模式

單利模式有一下特點(diǎn):

1. 單例類只能有一個(gè)實(shí)例。

2. 單例類必須自己創(chuàng)建自己的唯一實(shí)例。

3. 單例類必須給所有其他對(duì)象提供這一實(shí)例。

首先說(shuō)一下經(jīng)常用的

1.餓漢式

public class Singleton {

? ? private Singleton() {

? ? ? ? //構(gòu)造方法為private,防止外部代碼直接通過(guò)new來(lái)構(gòu)造多個(gè)對(duì)象

? ? }

? ? //在類初始化時(shí),已經(jīng)自行實(shí)例化,所以是線程安全的。

? ? private static final Singleton single? = new Singleton();

? ? //通過(guò)getInstance()方法獲取實(shí)例對(duì)象

? ? public static Singleton getInstance() {

? ? ? ? return single;

? ? }

}

優(yōu)點(diǎn):寫法簡(jiǎn)單,線程安全

缺點(diǎn):沒(méi)有懶加載效果,如果沒(méi)有使用過(guò)的話會(huì)造成內(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í)例化,實(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í)例化,實(shí)現(xiàn)懶加載,所以叫懶漢式

? ? ? ? ? ? singleton = new Singleton();

? ? ? ? }

? ? ? ? return singleton;

? ? }

? ? private Singleton() {

? ? }

}

優(yōu)點(diǎn):實(shí)現(xiàn)了懶加載,線程也安全了

缺點(diǎn):使用了synchronized會(huì)造成不必要的開(kāi)銷,而且大部分時(shí)候我們是用不到同步的

4.雙重檢查鎖定(DCL)

public class Singleton {

? ? //volatile 能夠防止代碼的重排序,保證得到的對(duì)象是初始化過(guò)

? ? private volatile static Singleton singleton = null;

? ? private Singleton() {

? ? }

? ? public static Singleton getInstance() {

? ? ? ? //第一次檢查,避免不必要的同步

? ? ? ? if (singleton == null){

? ? ? ? ? ? //同步

? ? ? ? ? ? synchronized (Singleton.class){

? ? ? ? ? ? ? ? //第二次檢查,為null時(shí)才創(chuàng)建

? ? ? ? ? ? ? ? if (singleton == null){

? ? ? ? ? ? ? ? ? ? singleton = new Singleton();

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? return singleton;

? ? }

}

優(yōu)點(diǎn):懶加載,線程安全,效率高

缺點(diǎn):volatile影響一點(diǎn)性能,高并發(fā)下有一定的缺陷,某些情況下DCL會(huì)失效,雖然概率小

5.靜態(tài)內(nèi)部類

public class Singleton {

? ? private Singleton() {

? ? }

? ? public static Singleton getInstance() {

? ? ? ? //第一次調(diào)用getInstance方法時(shí)才加載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):個(gè)人還沒(méi)發(fā)現(xiàn)

6.枚舉單例

public enum Singleton {

? ? //定義一個(gè)枚舉的元素,它就是Singleton的一個(gè)實(shí)例

? ? INSTANCE;

? ? public void doSomething(){

? ? }

}

優(yōu)點(diǎn):線程安全,寫法簡(jiǎn)單,能防止反序列化重新創(chuàng)建對(duì)象

缺點(diǎn):可讀性不高,枚舉會(huì)比靜態(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.使用反射會(huì)破壞單例模式,所以應(yīng)該慎用反射

Constructor con = Singleton.class.getDeclaredConstructor();

con.setAccessible(true);

Singleton singleton1 = (Singleton) con.newInstance();

Singleton singleton2 = (Singleton) con.newInstance();

//結(jié)果為false,singeton1和singeton2將是兩個(gè)不同的實(shí)例

System.out.println(singleton1 == singleton2);

可以通過(guò)當(dāng)?shù)诙握{(diào)用構(gòu)造函數(shù)是拋出異常來(lái)防止反射破壞單例,以懶漢式為例:

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.反序列化時(shí)也能破壞單例模式,可以重寫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)用場(chǎng)景

1.頻繁訪問(wèn)數(shù)據(jù)庫(kù)或文件對(duì)象。

2.工具類對(duì)象

3.創(chuàng)建對(duì)象時(shí)耗時(shí)過(guò)多或耗費(fèi)資源過(guò)多,但經(jīng)常用到的對(duì)象

優(yōu)點(diǎn)

1.內(nèi)存中只存在一個(gè)對(duì)象,節(jié)省了系統(tǒng)資源

2.避免對(duì)資源的多重占用,例如一個(gè)文件操作,由于只有一個(gè)實(shí)例存在內(nèi)存中,避免對(duì)統(tǒng)一資源文件的同時(shí)操作

缺點(diǎn)

1.獲取對(duì)象時(shí)不能用new

2.單例對(duì)象如果持有Context,那么很容易引發(fā)內(nèi)存泄露。

3.單例模式一般沒(méi)有接口,擴(kuò)展很困難,若要擴(kuò)展,只能修改代碼來(lái)實(shí)現(xiàn)

建造者模式

定義

將一個(gè)復(fù)雜對(duì)象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過(guò)程可以創(chuàng)建不同的表示

介紹

1.建造者模式屬于創(chuàng)建型模式

2.建造者模式主要用來(lái)創(chuàng)建復(fù)雜的對(duì)象,用戶可以不用關(guān)心其建造過(guò)程和細(xì)節(jié)

3.例如:當(dāng)要組裝一臺(tái)電腦時(shí),我們選擇好CPU、內(nèi)存、裝機(jī)師傅就把電腦給組裝起來(lái),我們不需要關(guān)心是怎么拼裝起來(lái)的

UML類圖

角色說(shuō)明

Product(產(chǎn)品類) :要?jiǎng)?chuàng)建的復(fù)雜對(duì)象。在本類圖中,產(chǎn)品類是一個(gè)具體的類,而非抽象類。實(shí)際編程中,產(chǎn)品類可以是由一個(gè)抽象類與它的不同實(shí)現(xiàn)組成,也可以是由多個(gè)抽象類與他們的實(shí)現(xiàn)組成,也是可以由多個(gè)抽象類與他們的實(shí)現(xiàn)組成。

Builder(抽象建造者):創(chuàng)建產(chǎn)品的抽象接口,一般至少有一個(gè)創(chuàng)建產(chǎn)品的抽象方法和一個(gè)返回產(chǎn)品的抽象方法。引入抽象類,是為了更容易擴(kuò)展。

ConcreteBuilder(實(shí)際的建造者):繼承Builder類,實(shí)現(xiàn)抽象類的所以抽象方法。實(shí)現(xiàn)具體的建造過(guò)程和細(xì)節(jié)。

Director(指揮者類):分配不同的建造者來(lái)創(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):組裝電腦的過(guò)程

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.測(cè)試方法

public void CreatComputer() {

? ? Builder builder = new ConcreteBuilder();

? ? Director director = new Director(builder);

? ? director.Construct("i7-6700","三星DDR4","希捷1T");

}

應(yīng)用場(chǎng)景

1.創(chuàng)建一些復(fù)雜的對(duì)象時(shí),對(duì)象內(nèi)部的構(gòu)鍵過(guò)程存在復(fù)雜變化。

2.相同的構(gòu)建過(guò)程,不同的執(zhí)行順序,產(chǎn)生不同結(jié)果時(shí)。

3.不同配置的構(gòu)建對(duì)象,產(chǎn)生不同結(jié)果時(shí)。

優(yōu)點(diǎn)

1.封裝性良好,隱藏內(nèi)部構(gòu)建細(xì)節(jié)。

2.易于解耦,將產(chǎn)品本身于產(chǎn)品創(chuàng)建過(guò)程進(jìn)行解耦,可以使用相同的創(chuàng)建過(guò)程來(lái)得到不同的產(chǎn)品,也就說(shuō)細(xì)節(jié)依賴抽象。

3.易于擴(kuò)展,具體的建造者類之間相互獨(dú)立,增加新的具體建造者無(wú)需修改原有類庫(kù)的代碼。

4.易于精確控制對(duì)象的創(chuàng)建,由于具體的建造者是獨(dú)立的,因此可以對(duì)建造過(guò)程逐步細(xì)化,而不對(duì)其他的模塊產(chǎn)生任何影響。

缺點(diǎn)

1.產(chǎn)生多余的Build對(duì)象以及Dirextor類。

2.建造者模式所創(chuàng)建的產(chǎn)品一般具有較多的共同點(diǎn),其組成部分相似;如果產(chǎn)品之間的差異性很大,則不適合使用建造者模式,因此其使用范圍受到一定的限制。

3.如果產(chǎn)品的內(nèi)部復(fù)雜,可能會(huì)導(dǎo)致需要定義很多具體建造者來(lái)實(shí)現(xiàn)這種變化,導(dǎo)致系統(tǒng)變得很龐大。

Android中的源碼分析

Android中的AlertDialog.Builder就是使用了Builder模式來(lái)構(gòu)建AlertDialog的。

AlertDialog.Builder的簡(jiǎn)單用法

//創(chuàng)建一個(gè)builder對(duì)象

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對(duì)象

AlertDialog alertDialog = builder.create();

//展示AlertDialog

alertDialog.show();

通過(guò)Builder對(duì)象來(lái)構(gòu)建lcon、Title、Message等,講AlertDialog的構(gòu)建過(guò)程和細(xì)節(jié)隱藏了起來(lái)

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對(duì)象

? ? this.mAlert = new AlertController(this.getContext(), this, this.getWindow());

? }

//設(shè)置Title

public void setTitle(CharSequence title) {

? ? super.setTitle(title);

//保存在AlertController對(duì)象中

? ? this.mAlert.setTitle(title);

}

//設(shè)置Message

public void setMessage(CharSequence message) {

//保存在AlertController對(duì)象中

? ? this.mAlert.setMessage(message);

}

//設(shè)置Icon

public void setIcon(int resId) {

//保存在AlertController對(duì)象中

? ? 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對(duì)象所需要的參數(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對(duì)象

? ? 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對(duì)象中需要構(gòu)建的元素逐個(gè)添加設(shè)置到Window上,即構(gòu)建我們?cè)O(shè)置的布局發(fā)生在這一步中

setupView();

? ? ? }

}

簡(jiǎn)單流程說(shuō)明

1.通過(guò)AlertDialog.Builder設(shè)置各種屬性后(如:setTitle()),這些屬性信息會(huì)保存在P變量中,P變量的類型為AlertController.AlertParams。

2.調(diào)用builder.create()即可返回一個(gè)AlertDialog對(duì)象。

2.1builder.create()方法中首先會(huì)創(chuàng)建一個(gè)AlertDialog對(duì)象,AlertDialog對(duì)象構(gòu)造時(shí)會(huì)初始化WindowManager和Window。

2.2builder.create()創(chuàng)建完AlertDialog對(duì)象后,會(huì)調(diào)用P.apply(dialog.mAlert);即把P變量中所存儲(chǔ)的用來(lái)構(gòu)建AlertDialog對(duì)象的元素設(shè)置到了dialog.mAlert中,dialog.mAlert的類型為AlertController。

3.調(diào)用AlertDialog的show()方法,展示界面。

3.1show()方法中會(huì)調(diào)用dispatchOnCreate(null),dispatchOnCreate(null)調(diào)起onCreate(),onCreate()會(huì)調(diào)起mAlert.installContent();即安裝AlertDialog的內(nèi)容。

3.2installContent()中會(huì)調(diào)用mWindow.setContentView(mAlertDialogLayout);即把mAlertDialogLayout這個(gè)布局加到Window中去。

3.3 調(diào)完mWindow.setContentView(mAlertDialogLayout)后會(huì)調(diào)用setupView(),setupView()中會(huì)把dialog.mAlert對(duì)象中需要構(gòu)建的元素逐個(gè)添加設(shè)置到mWindow上。

3.4 最后通過(guò)把view添加到mWindowManager上展示出來(lái)。

總結(jié)

1.builder模式隱藏了這種復(fù)雜的構(gòu)建過(guò)程,只需幾行簡(jiǎn)單的代碼就把AlertDialog給展示出來(lái)了。

2.AlertDialog的builder中并沒(méi)有抽象建造者(Builder)、Director(指揮者類)等角色。AlertDialog.Builder同時(shí)扮演了Builder、ConcreteBuilder、Director等角色,這是Android中的一種簡(jiǎn)化,也值得我們?nèi)W(xué)習(xí)使用。

簡(jiǎn)單工廠模式

定義

定義一個(gè)用于創(chuàng)建對(duì)象的接口,讓子類決定實(shí)例化那個(gè)類

介紹

簡(jiǎn)單工廠模式屬于創(chuàng)建模式

簡(jiǎn)單工廠模式又叫做靜態(tài)工廠模式

UML類圖

角色說(shuō)明

Product(抽象產(chǎn)品類):要?jiǎng)?chuàng)建的復(fù)雜對(duì)象,定義對(duì)象的公共接口。

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;

? ? ? ? //通過(guò)switch語(yǔ)句控制生產(chǎn)那種商品

? ? ? ? switch (productName) {

? ? ? ? ? ? case "A":

? ? ? ? ? ? ? ? product = new ProductA();

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? case "B":

? ? ? ? ? ? ? ? product = new ProductB();

? ? ? ? ? ? ? ? break;

? ? ? ? }

? ? ? ? return product;

? ? }

}

4.測(cè)試方法:

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){

? ? ? ? //沒(méi)有ProductC,會(huì)報(bào)錯(cuò)

? ? ? ? System.out.println("沒(méi)有ProductC");

? ? }

}

應(yīng)用場(chǎng)景

生成復(fù)雜對(duì)象時(shí),確定只有一個(gè)工廠類,可以使用簡(jiǎn)單工廠模式。否則有多個(gè)工廠類的話,使用工廠模式方法。

優(yōu)點(diǎn)

代碼解耦,創(chuàng)建實(shí)例的工作與使用實(shí)例的工作分開(kāi),使用者不必關(guān)心類對(duì)象如何創(chuàng)建。

缺點(diǎn)

1.違背開(kāi)放封閉原則,若需添加新產(chǎn)品則必須修改工廠類邏輯,會(huì)造成工廠邏輯過(guò)于復(fù)雜

2.簡(jiǎn)單工廠模式使用靜態(tài)工廠犯法,因此靜態(tài)方法不能被繼承和重寫

3.工廠類包含所有實(shí)例(產(chǎn)品)的創(chuàng)建邏輯,若工廠類出錯(cuò),則會(huì)造成整個(gè)系統(tǒng)都會(huì)受到影響

工廠方法模式與簡(jiǎn)單工廠模式比較

1.工廠方法模式有抽象工廠類,簡(jiǎn)單工廠模式?jīng)]有抽象工廠類且其工廠類的工廠方法是靜態(tài)的

2.工廠方法模式新增產(chǎn)品時(shí)只需要新建一個(gè)工廠類即可,符合開(kāi)放封閉原則;而簡(jiǎn)單工廠模式需要直接修改工廠類,違反了開(kāi)放封閉原則

簡(jiǎn)單工廠模式的優(yōu)化

由于簡(jiǎn)單工廠模式新增產(chǎn)品是需要直接修改工廠類,違反了開(kāi)放封閉原則。因此可以使用反射來(lái)創(chuàng)建實(shí)例對(duì)象,確保能夠遵循開(kāi)放封閉原則

反射實(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;

? ? }

}

測(cè)試方法

public void test(){

? ? //生產(chǎn)ProductA

? ? Factory.create(ProductA.class).show();

? ? //生產(chǎn)ProductB

? ? Factory.create(ProductB.class).show();

}

總結(jié)

使用反射來(lái)實(shí)現(xiàn)工廠類,新增產(chǎn)品時(shí)無(wú)需修改工廠類,但是使用反射來(lái)創(chuàng)建實(shí)例對(duì)象的話會(huì)比正常使用new來(lái)創(chuàng)建的要慢。

觀察者模式

定義

定義對(duì)象間的一種一個(gè)對(duì)多的依賴關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)送改變時(shí),所以依賴于它的對(duì)象都得到通知并被自動(dòng)更新

介紹

1.觀察者屬于行為型模式

2.觀察者模式又被稱作發(fā)布/訂閱模式

3.觀察者模式主要用來(lái)解耦,將被觀察者和觀察者解耦,讓他們之間沒(méi)有沒(méi)有依賴或者依賴關(guān)系很小

UML類圖

角色說(shuō)明

Subject(抽象主題):又叫抽象被觀察者,把所有觀察者對(duì)象的引用保存到一個(gè)集合里,每個(gè)主題都可以有任何數(shù)量的被觀察者。抽象主題提供一個(gè)接口,可以增加和刪除觀察者對(duì)象。

ConcreteSubject(具體主題):又叫具體被觀察者,將有關(guān)狀態(tài)存入具體觀察者對(duì)象,在具體主題內(nèi)部狀態(tài)改變時(shí),給所有登記過(guò)的觀察者發(fā)出通知。

Observer(抽象觀察者):為所有的具體觀察者定義一個(gè)接口,在得到主題通知時(shí)更新自己。

ConcreteObserver(具體觀察者):實(shí)現(xiàn)抽象觀察者定義的更新接口,當(dāng)?shù)玫街黝}更改通知時(shí)更新自身的狀態(tài)

實(shí)現(xiàn)

繼續(xù)以送快遞為例,快遞員有時(shí)只是把快遞拉到樓下,然后就通知收件人下樓取快遞。

創(chuàng)建抽象觀察者

定義一個(gè)接到通知的更新方法,即收件人收到通知后的反應(yīng):

//抽象觀察者

public interface Observer {

? ? //更新方法

? ? public void update(String message);


}

創(chuàng)建具體觀察者

實(shí)現(xiàn)抽象觀察者中的方法,這里創(chuàng)建兩個(gè)類,一個(gè)男孩類和一個(gè)女孩類,定義他們收到通知后的反應(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)建具體主題

即具體被觀察者,也就是快遞員,派送快遞時(shí)根據(jù)快遞信息來(lái)通知收件人讓其來(lái)取件:

//快遞員

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);

? ? ? ? }

? ? }

}

客戶端測(cè)試

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("快遞到了,請(qǐng)下樓領(lǐng)取");

}

說(shuō)明

實(shí)際上,JDK內(nèi)部也內(nèi)置了Observable(抽象被觀察者),Observer(抽象觀察者)這兩個(gè)類,我們也可以直接拿來(lái)有,其代碼如下:

//抽象觀察者

public interface Observer {

? ? //更新方法

? ? void update(Observable observable, Object arg);

}

//抽象被觀察者

public class Observable {

? ? //定義改變狀態(tài),默認(rèn)為false

? ? private boolean changed = false;

? ? //定義一個(gè)觀察者list

? ? private final ArrayList<Observer> observers;

? ? //構(gòu)造函數(shù),初始化一個(gè)觀察者list來(lái)保存觀察者

? ? 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);

? ? }

? ? //通知所以觀察者,無(wú)參數(shù)

? ? public void notifyObservers() {

? ? ? ? notifyObservers(null);

? ? }

? ? //通知所有觀察者,帶參數(shù)

? ? public void notifyObservers(Object arg) {

? ? ? ? Observer[] arrLocal;

? ? ? ? //加synchronized字段,保證多線程下操作沒(méi)有問(wèn)題

? ? ? ? synchronized (this) {

? ? ? ? ? ? //這里做了是否發(fā)生改變的判斷,是為了防止出現(xiàn)無(wú)意義的更新

? ? ? ? ? ? if (!hasChanged())

? ? ? ? ? ? ? ? return;

? //ArrayList轉(zhuǎn)換成一個(gè)臨時(shí)的數(shù)組,這樣就防止了通知,添加,移除同時(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)用場(chǎng)景

1.當(dāng)一個(gè)對(duì)象的改變需要通知其他對(duì)象改變時(shí),而且它不知道具體有多少個(gè)對(duì)象有待改變時(shí)。

2.當(dāng)一個(gè)對(duì)象必須通知其它對(duì)象,而它又不能假定其它對(duì)象是誰(shuí)。

3.跨系統(tǒng)的消息交換場(chǎng)景,如消息隊(duì)列、事件總線的處理機(jī)制。

優(yōu)點(diǎn)

1.解除觀察者與主題之間的耦合。讓耦合雙方都依賴于抽象,而不是依賴具體。從而使得自己的變換都不會(huì)影響另一邊的變換。

2.易于擴(kuò)展,對(duì)同一主題新增觀察者時(shí)無(wú)需修改原有代碼。

缺點(diǎn)

1.依賴關(guān)系并未完全解除,抽象主題仍然依賴抽象觀察者。

2.使用觀察者模式時(shí)需要考慮一下開(kāi)發(fā)效率和運(yùn)行效率的問(wèn)題,程序中包括一個(gè)被觀察者、多個(gè)觀察者,開(kāi)發(fā)、調(diào)試等內(nèi)容會(huì)比較復(fù)雜,而且在Java中消息的通知一般是順序執(zhí)行,那么一個(gè)觀察者卡頓,會(huì)影響整體的執(zhí)行效率,在這種情況下,一般會(huì)采用異步實(shí)現(xiàn)。

3.可能會(huì)引起多余的數(shù)據(jù)通知。

Android中的源碼分析

1.控件中的Listener監(jiān)聽(tīng)方式

Android中我們遇到最常用的觀察者模式就是各種控件的監(jiān)聽(tīng),如下:

Button button = findViewById(R.id.button);

button.setOnClickListener(new View.OnClickListener() {

? ? @Override

? ? public void onClick(View v) {

? ? ? ? Log.d(TAG, "onClick: ");

? ? }

});

上面的代碼中,button就是具體的主題,也就是被觀察者;new出來(lái)的View.OnClickListener對(duì)象就是具體的觀察者;OnClickListener實(shí)際上就是個(gè)接口,也就是抽象觀察者;通過(guò)setOnClickListener把觀察者注冊(cè)到被觀察者中。

一旦button捕獲的點(diǎn)擊事件,即狀態(tài)發(fā)生變化的時(shí)候,就會(huì)通過(guò)回調(diào)注冊(cè)的OnClickListener觀察者的onClick方法會(huì)來(lái)通知觀察者,Button狀態(tài)發(fā)生變化。

1.相關(guān)源碼分析:

//抽象觀察者

public interface OnClickListener {

//只有onClick這個(gè)方法

? ? void onClick(View v);

}

public void setOnClickListener(@Nullable OnClickListener l) {

? ? if (!isClickable()) {

? //設(shè)置為可點(diǎn)擊

? ? ? ? setClickable(true);

? ? }

//把傳入的 OnClickListener 對(duì)象賦值給了 getListenerInfo().mOnClickListener,即mListenerInfo的mOnClickListener持有OnClickListener對(duì)象的引用

? ? getListenerInfo().mOnClickListener = l;

}

//返回ListenerInfo對(duì)象,這里是一個(gè)單例模式

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對(duì)象

? ? ? ? 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í),需要更新數(shù)據(jù)時(shí)我們就會(huì)調(diào)用Adapter的notifyDataSetChanged()方法,那么我們來(lái)看看notifyDataSetChanged()的實(shí)現(xiàn)原理,這個(gè)方法是定義在BaseAdapter中具體代碼如下:

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {

//數(shù)據(jù)集被觀察者

? ? private final DataSetObservable mDataSetObservable = new DataSetObservable();

? ? public boolean hasStableIds() {

? ? ? ? return false;

? ? }

? ? //注冊(cè)觀察者

? ? public void registerDataSetObserver(DataSetObserver observer) {

? ? ? ? mDataSetObservable.registerObserver(observer);

? ? }

//注銷觀察者

? ? public void unregisterDataSetObserver(DataSetObserver observer) {

? ? ? ? mDataSetObservable.unregisterObserver(observer);

? ? }

? ? //數(shù)據(jù)集改變時(shí),通知所有觀察者

? ? public void notifyDataSetChanged() {

? ? ? ? mDataSetObservable.notifyChanged();

? ? }

}

//其他代碼略

由上面的代碼可以看出BaseAdapter實(shí)際上就是使用了觀察者模式,BaseAdapter就是具體的被觀察者。接下來(lái)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í)從哪里來(lái)的呢?實(shí)際上這些觀察者是在ListView通過(guò)setAdapter()設(shè)置Adapter時(shí)產(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)建一個(gè)數(shù)據(jù)集觀察者

? ? mDataSetObserver = new AdapterDataSetObserver();

//注冊(cè)觀察者

? ? mAdapter.registerDataSetObserver(mDataSetObserver);

//其他代碼略

}

}

從上面的代碼可以看到,觀察者有了,那么這個(gè)觀察者主要是干什么的呢?

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這個(gè)類里面的onChanged()方法中實(shí)現(xiàn)了布局的更新。

簡(jiǎn)單總結(jié)

當(dāng)ListView的數(shù)據(jù)發(fā)生變化時(shí),我調(diào)用Adapter的notifyDataSetChanged()方法,這個(gè)方法又會(huì)調(diào)用所以觀察者(AdapterDataSetObserver)的onChanged()方法,onChanged()方法又會(huì)調(diào)用requestLayout()方法重新進(jìn)行布局。

BroadcastReceiver

BroadcastReceiver作為Android的四大組件之一,實(shí)際上也是一個(gè)典型的觀察者模式,通過(guò)sendBroadcast發(fā)送廣播時(shí),只有注冊(cè)了相應(yīng)的IntentFilter的BroadcastReceiver對(duì)象才會(huì)收到這個(gè)廣播信息,其onReceive方法才會(huì)被調(diào)起,BroadcastReceiver的代碼比較復(fù)雜

其他

另外,一些著名的第三方事件總線庫(kù),比如RxJava、RxAndroid、EventBus等等,也是使用了觀察者模式,有興趣的可以去看一下他們的源碼。

工廠模式

定義

定義一個(gè)用于創(chuàng)建對(duì)象的接口,讓子類決定實(shí)例化哪個(gè)類

介紹

1.工廠方法模式屬于創(chuàng)鍵型模式。

2.工廠方法模式主要用來(lái)創(chuàng)建復(fù)雜的對(duì)象,簡(jiǎn)單對(duì)象能夠使用new來(lái)創(chuàng)建就不用工廠方法模式來(lái)創(chuàng)建

UML類圖

角色說(shuō)明

Product(抽象產(chǎn)品類):要?jiǎng)?chuàng)建的復(fù)雜對(duì)象,定義對(duì)象的公共接口。

ConcreteProduct(具體產(chǎn)品類):實(shí)現(xiàn)Product接口。

Factory(抽象工廠類):該方法返回一個(gè)Product類型的對(duì)象。

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();

? ? }

}

測(cè)試方法:

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)用場(chǎng)景

生成復(fù)雜對(duì)象時(shí),無(wú)需知道具體類名,只需知道想應(yīng)的工廠方法即可。

優(yōu)點(diǎn)

1.符合開(kāi)放封閉原則。新增產(chǎn)品時(shí),只需增加相應(yīng)的具體產(chǎn)品類和相應(yīng)的工廠子類即可

2.符合單一職責(zé)原則。每個(gè)具體工廠類只負(fù)責(zé)創(chuàng)建對(duì)應(yīng)的產(chǎn)品。

缺點(diǎn)

1.一個(gè)具體工廠只能創(chuàng)建一種具體產(chǎn)品。

2.增加新產(chǎn)品時(shí),還需要增加相應(yīng)的工廠類,系統(tǒng)類的個(gè)數(shù)將成對(duì)增加,增加了系統(tǒng)的復(fù)雜度和性能開(kāi)銷

3.引入的抽象類也會(huì)導(dǎo)致類結(jié)構(gòu)的復(fù)雜化

Android中的源碼分析

Android中的ThreadFactory就是使用了工廠方法模式來(lái)生成線程的,線程就是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這個(gè)產(chǎn)品

? ? ? ? return new Thread(r,"AsyncTask #"+mCount.getAndIncrement());

? ? }

};

總結(jié)

1.這里只要是介紹Android系統(tǒng)中工廠模式的應(yīng)用,線程和AsyncTask的原理就不說(shuō)了。

2.通過(guò)ThreadFactory,我們可以創(chuàng)建出不同的Thread來(lái)。

3.同樣,我們可以創(chuàng)建另外類似的工廠,生產(chǎn)某種專門的線程,非常容易擴(kuò)展。

責(zé)任鏈模式

定義

一個(gè)請(qǐng)求沿著一條“鏈”傳遞,直到該“鏈”上的某個(gè)處理者處理它為止。

介紹

1.責(zé)任鏈模式屬于行為型模式。

2.多個(gè)對(duì)象中,每個(gè)對(duì)象都持有下一個(gè)對(duì)象的引用,這就構(gòu)成了鏈這種結(jié)構(gòu)。

3.一個(gè)請(qǐng)求通過(guò)鏈的頭部,一直往下傳遞到鏈上的每一個(gè)結(jié)點(diǎn),直到有某個(gè)結(jié)點(diǎn)對(duì)這個(gè)請(qǐng)求做出處理為止,這就是責(zé)任鏈模式。

4.責(zé)任鏈模式一般分為處理者與請(qǐng)求者。具體的處理者分別處理請(qǐng)求者的行為。

UML類圖

角色說(shuō)明

Handler(抽象處理者):抽象類或者接口,定義處理請(qǐng)求的方法以及持有下一個(gè)Handler的引用。

ConcreteHandler1,ConcreteHandler2(具體處理者):實(shí)現(xiàn)抽象處理類,對(duì)請(qǐng)求進(jìn)行處理,如果不處理則轉(zhuǎn)發(fā)給下一個(gè)處理者。

Client(客戶端):即要使用責(zé)任鏈模式的地方。

實(shí)現(xiàn)

以送快遞為例,單個(gè)快遞員只負(fù)責(zé)某個(gè)片區(qū)的快遞,若某個(gè)快遞目的地不屬于當(dāng)前的片區(qū),則交給下一個(gè)快遞員來(lái)處理,直到有人處理為止。

創(chuàng)建抽象處理者類

定義處理請(qǐng)求的方法以及持有下一個(gè)Handler的引用:

//快遞員抽象類

public abstract class Postman {

? ? //下一個(gè)快遞員

? ? 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{

? ? ? ? ? ? //否則交給下一個(gè)快遞員去處理

? ? ? ? ? ? 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);

? ? ? ? }

? ? }

}

客戶端測(cè)試

public void test() {

? ? //創(chuàng)建不同的快遞員對(duì)象

? ? BeijingPostman beijingPostman = new BeijingPostman();

? ? ShanghaiPostman shanghaiPostman = new ShanghaiPostman();

? ? GuangzhouPostman guangzhouPostman = new GuangzhouPostman();

? ? //創(chuàng)建下一個(gè)結(jié)點(diǎn)

? ? beijingPostman.nextPostman = shanghaiPostman;

? ? shanghaiPostman.nextPostman = guangzhouPostman;

? ? //處理不同地區(qū)的快遞,都是從首結(jié)點(diǎn)北京快遞員開(kāi)始

? ? System.out.println("有一個(gè)上??爝f需要派送:");

? ? beijingPostman.handleCourier("Shanghai");

? ? System.out.println("有一個(gè)廣州快遞需要派送:");

? ? beijingPostman.handleCourier("Guangzhou");

? ? System.out.println("有一個(gè)美國(guó)快遞需要派送:");

? ? beijingPostman.handleCourier("America");

}

說(shuō)明

1.上面的請(qǐng)求只是一個(gè)簡(jiǎn)單的地址字符串,如果是一些復(fù)雜的請(qǐng)求,可以封裝成獨(dú)立的對(duì)象。如:普通快遞和生鮮快遞,生鮮快遞還需快遞員做冷鏈處理等等。

2.請(qǐng)求實(shí)際上可以從責(zé)任鏈中的任意結(jié)點(diǎn)開(kāi)始,即可以從上??爝f員開(kāi)始處理也行。

3.責(zé)任鏈中的結(jié)點(diǎn)順序?qū)嶋H也可以調(diào)整,即北京->廣州->上海的順序也行。

4.責(zé)任鏈也可以過(guò)某些結(jié)點(diǎn)去處理請(qǐng)求,如北京->廣州,越過(guò)了上海。

5.對(duì)于請(qǐng)求,只有兩種結(jié)果:一個(gè)某個(gè)結(jié)點(diǎn)對(duì)其進(jìn)行了處理,如上面例子上海、廣州快遞,這種叫純的責(zé)任鏈;另一個(gè)則是所以結(jié)點(diǎn)都不進(jìn)行處理,如美國(guó)的快遞,這種叫不純的責(zé)任鏈模式。我們所見(jiàn)到的基本都是不純的責(zé)任鏈。

應(yīng)用場(chǎng)景

1.多個(gè)對(duì)象處理同一請(qǐng)求時(shí),但是具體由哪個(gè)對(duì)象去處理需要運(yùn)行時(shí)做判斷。

2.具體處理者不明確的情況下,向這組對(duì)象提交了一個(gè)請(qǐng)求。

優(yōu)點(diǎn)

1.代碼的解耦,請(qǐng)求者與處理者的隔離分開(kāi)。

2.易于擴(kuò)展,新增處理者往鏈上加結(jié)點(diǎn)即可。

缺點(diǎn)

1.責(zé)任鏈過(guò)長(zhǎng)的話,或者鏈上的結(jié)點(diǎn)判斷處理時(shí)間太長(zhǎng)的話會(huì)影響性能,特別是遞歸循環(huán)的時(shí)候。

2.請(qǐng)求有可能遍歷完鏈都得不到處理

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容