(一)什么是設(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)求有可能遍歷完鏈都得不到處理