Android源碼學(xué)習(xí)之工廠方法模式應(yīng)用及優(yōu)勢(shì)介紹
Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
定義一個(gè)用于創(chuàng)建對(duì)象的接口,讓子類決定實(shí)例化哪一個(gè)類。工廠方法使一個(gè)類的實(shí)例化延遲到其子類。
常用的工廠方法模式結(jié)構(gòu):
如上圖所示(截取自《Head First Design Patterns》一書(shū)),主要包括四個(gè)部分:
抽象產(chǎn)品類Product負(fù)責(zé)定義產(chǎn)品的共性,實(shí)現(xiàn)對(duì)事物抽象的定義;Creator是抽象創(chuàng)建類,也就是抽象工廠,具體如何創(chuàng)建產(chǎn)品類是由具體的實(shí)現(xiàn)工廠ConcreteCreator完成的。其中在《Head First Design Patterns》對(duì)工廠方法模式做了細(xì)節(jié)的說(shuō)明,原文如下:
As in the official definition, you'll often hear developers say that the Factory Method lets subclasses decide which class to instantiate. They say “decides” not because the pattern allows subclasses themselves to decide at runtime, but because the creator class is written without knowledge of the actual products that will be created, which is decided purely by the choice of the subclass that is used.
工廠方法模式有什么優(yōu)勢(shì)呢:
良好的封裝性,代碼結(jié)構(gòu)清楚。一個(gè)對(duì)象創(chuàng)建具有條件約束的,如果一個(gè)調(diào)用者需要一個(gè)具體的產(chǎn)品對(duì)象,只要知道這個(gè)產(chǎn)品的類名就可以了,不用知道創(chuàng)建對(duì)象的過(guò)程,降低模塊間的耦合。
可擴(kuò)展性好。在增加產(chǎn)品類的情況下,只要適當(dāng)?shù)男薷木唧w的工廠類或擴(kuò)展一個(gè)工廠類,就可以完成。
屏蔽產(chǎn)品類。產(chǎn)品類的實(shí)現(xiàn)如何變化,調(diào)用者都不需要關(guān)心,它只需要關(guān)心產(chǎn)品的接口,只要接口保持不變,系統(tǒng)中的上層模塊就不用改變。因?yàn)楫a(chǎn)品類的實(shí)例化工作是由工廠類負(fù)責(zé)的,一個(gè)產(chǎn)品對(duì)象具體由哪一個(gè)產(chǎn)品生成是由工廠類決定的。此外工廠方法模式是典型的松耦合結(jié)構(gòu)。高層模塊只需要知道產(chǎn)品的抽象類,其他的實(shí)現(xiàn)類都不用關(guān)系,符合迪米特法則、依賴倒置原則、里氏替換原則等。
在Android源碼中,ListActivity繼承自Activity,將Activity作為工廠方法,生成具有ListView特點(diǎn)的Activity,對(duì)ListActivity的說(shuō)明如下:
An activity that displays a list of items by binding to a data source such as an array or Cursor, and exposes event handlers when the user selects an item.
ListActivity hosts a ListView object that can be bound to different data sources, typically either an array or a Cursor holding query results. Binding, screen layout, and row layout are discussed in the following sections.
Screen Layout
ListActivity has a default layout that consists of a single, full-screen list in the center of the screen. However, if you desire, you can customize the screen layout by setting your own view layout with setContentView() in onCreate(). To do this, your own view MUST contain a ListView object with the id "@android:id/list" (or listif it's in code)
在Activity類中有這么一個(gè)函數(shù):
/**
* This method is called after {@link #onStart} when the activity is
* being re-initialized from a previously saved state, given here in
* <var>savedInstanceState</var>. Most implementations will simply use {@link #onCreate}
* to restore their state, but it is sometimes convenient to do it here
* after all of the initialization has been done or to allow subclasses to
* decide whether to use your default implementation. The default
* implementation of this method performs a restore of any view state that
* had previously been frozen by {@link #onSaveInstanceState}.
*
* <p>This method is called between {@link #onStart} and
* {@link #onPostCreate}.
*
* @param savedInstanceState the data most recently supplied in {@link #onSaveInstanceState}.
*
* @see #onCreate
* @see #onPostCreate
* @see #onResume
* @see #onSaveInstanceState
*/
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (mWindow != null) {
Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
if (windowState != null) {
mWindow.restoreHierarchyState(windowState);
}
}
}
在注釋中“but it is sometimes convenient to do it here after all of the initialization has been done or to allow subclasses to decide whether to use your default implementation.”,英語(yǔ)不太好,但大致的意思是Activity子類可以重載這個(gè)函數(shù)來(lái)決定是否使用默認(rèn)的實(shí)現(xiàn)。
在看子類ListActivity:
public class ListActivity extends Activity {
/**
* This field should be made private, so it is hidden from the SDK.
* {@hide}
*/
protected ListAdapter mAdapter;
/**
* This field should be made private, so it is hidden from the SDK.
* {@hide}
*/
protected ListView mList;
private Handler mHandler = new Handler();
private boolean mFinishedStart = false;
private Runnable mRequestFocus = new Runnable() {
public void run() {
mList.focusableViewAvailable(mList);
}
};
/**
* This method will be called when an item in the list is selected.
* Subclasses should override. Subclasses can call
* getListView().getItemAtPosition(position) if they need to access the
* data associated with the selected item.
*
* @param l The ListView where the click happened
* @param v The view that was clicked within the ListView
* @param position The position of the view in the list
* @param id The row id of the item that was clicked
*/
protected void onListItemClick(ListView l, View v, int position, long id) {
}
/**
* Ensures the list view has been created before Activity restores all
* of the view states.
*
*@see Activity#onRestoreInstanceState(Bundle)
*/
@Override
protected void onRestoreInstanceState(Bundle state) {
ensureList();
super.onRestoreInstanceState(state);
}
/**
* @see Activity#onDestroy()
*/
@Override
protected void onDestroy() {
mHandler.removeCallbacks(mRequestFocus);
super.onDestroy();
}
/**
* Updates the screen state (current list and other views) when the
* content changes.
*
* @see Activity#onContentChanged()
*/
@Override
public void onContentChanged() {
super.onContentChanged();
View emptyView = findViewById(com.android.internal.R.id.empty);
mList = (ListView)findViewById(com.android.internal.R.id.list);
if (mList == null) {
throw new RuntimeException(
"Your content must have a ListView whose id attribute is " +
"'android.R.id.list'");
}
if (emptyView != null) {
mList.setEmptyView(emptyView);
}
mList.setOnItemClickListener(mOnClickListener);
if (mFinishedStart) {
setListAdapter(mAdapter);
}
mHandler.post(mRequestFocus);
mFinishedStart = true;
}
/**
* Provide the cursor for the list view.
*/
public void setListAdapter(ListAdapter adapter) {
synchronized (this) {
ensureList();
mAdapter = adapter;
mList.setAdapter(adapter);
}
}
/**
* Set the currently selected list item to the specified
* position with the adapter's data
*
* @param position
*/
public void setSelection(int position) {
mList.setSelection(position);
}
/**
* Get the position of the currently selected list item.
*/
public int getSelectedItemPosition() {
return mList.getSelectedItemPosition();
}
/**
* Get the cursor row ID of the currently selected list item.
*/
public long getSelectedItemId() {
return mList.getSelectedItemId();
}
/**
* Get the activity's list view widget.
*/
public ListView getListView() {
ensureList();
return mList;
}
/**
* Get the ListAdapter associated with this activity's ListView.
*/
public ListAdapter getListAdapter() {
return mAdapter;
}
private void ensureList() {
if (mList != null) {
return;
}
setContentView(com.android.internal.R.layout.list_content_simple);
}
private AdapterView.OnItemClickListener mOnClickListener = new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id)
{
onListItemClick((ListView)parent, v, position, id);
}
};
}
其中復(fù)寫(xiě)了函數(shù)onRestoreInstanceState(Bundle state),并在View中設(shè)置了默認(rèn)的setContentView(com.android.internal.R.layout.list_content_simple);
/**
* Ensures the list view has been created before Activity restores all
* of the view states.
*
*@see Activity#onRestoreInstanceState(Bundle)
*/
@Override
protected void onRestoreInstanceState(Bundle state) {
ensureList();
super.onRestoreInstanceState(state);
}
。。。
private void ensureList() {
if (mList != null) {
return;
}
setContentView(com.android.internal.R.layout.list_content_simple);
}
Activity中的setContentView()函數(shù):
/**
* Set the activity content from a layout resource. The resource will be
* inflated, adding all top-level views to the activity.
*
* @param layoutResID Resource ID to be inflated.
*
* @see #setContentView(android.view.View)
* @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
*/
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
initActionBar();
}
/**
* Set the activity content to an explicit view. This view is placed
* directly into the activity's view hierarchy. It can itself be a complex
* view hierarchy. When calling this method, the layout parameters of the
* specified view are ignored. Both the width and the height of the view are
* set by default to {@link ViewGroup.LayoutParams#MATCH_PARENT}. To use
* your own layout parameters, invoke
* {@link #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}
* instead.
*
* @param view The desired content to display.
*
* @see #setContentView(int)
* @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
*/
public void setContentView(View view) {
getWindow().setContentView(view);
initActionBar();
}
/**
* Set the activity content to an explicit view. This view is placed
* directly into the activity's view hierarchy. It can itself be a complex
* view hierarchy.
*
* @param view The desired content to display.
* @param params Layout parameters for the view.
*
* @see #setContentView(android.view.View)
* @see #setContentView(int)
*/
public void setContentView(View view, ViewGroup.LayoutParams params) {
getWindow().setContentView(view, params);
initActionBar();
}

總結(jié):Activity作為“工廠方法”,具體View中顯示什么由默認(rèn)設(shè)置或者由子類來(lái)實(shí)現(xiàn);ListActivity作為具體實(shí)現(xiàn),它決定在View中顯示的是ListView;這里的View是Activity中的默認(rèn)顯示,即為“Product”,而ListView是“ConcreteProduct”,由ListActivity來(lái)決定顯示。
除了ListActivity以外,還有ExpandableListActivity也是以Activity為工廠類,創(chuàng)建自己的顯示效果。
本人能力和時(shí)間有限(缺少“模式使用”內(nèi)容,以后會(huì)添加),寫(xiě)的很粗糙,恭候大家的批評(píng)指正,謝謝~~~
- Android編程設(shè)計(jì)模式之工廠方法模式實(shí)例詳解
- Android設(shè)計(jì)模式系列之工廠方法模式
- Android編程設(shè)計(jì)模式之原型模式實(shí)例詳解
- Android編程設(shè)計(jì)模式之Builder模式實(shí)例詳解
- Android編程設(shè)計(jì)模式之單例模式實(shí)例詳解
- Android編程設(shè)計(jì)模式之觀察者模式實(shí)例詳解
- 基于Android設(shè)計(jì)模式之--SDK源碼之策略模式的詳解
- Android設(shè)計(jì)模式之適配器(Adapter)模式
- Android 單例模式 Singleton 簡(jiǎn)單實(shí)例設(shè)計(jì)模式解析
- Android設(shè)計(jì)模式系列之組合模式
- Android編程設(shè)計(jì)模式之抽象工廠模式詳解
相關(guān)文章
Android超詳細(xì)講解彈出多選框的實(shí)現(xiàn)
這篇文章主要介紹了在Android開(kāi)發(fā)中如何實(shí)現(xiàn)彈出多選框的功能,多選框是很常見(jiàn)的操作控件,感興趣的朋友都來(lái)一起看看吧2022-03-03
Android sqlite設(shè)置主鍵自增長(zhǎng)的方法教程
這篇文章主要給大家介紹了關(guān)于Android sqlite設(shè)置主鍵自增長(zhǎng)的方法教程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編一起來(lái)學(xué)習(xí)學(xué)習(xí)吧。2017-06-06
Android實(shí)現(xiàn)系統(tǒng)重新啟動(dòng)的功能
有些Android版本沒(méi)有系統(tǒng)重啟的功能,非常不方便。需要我們自己開(kāi)發(fā)一個(gè)能夠重新啟動(dòng)的應(yīng)用2013-11-11
Android studio配置lambda表達(dá)式教程
Java 8的一個(gè)大亮點(diǎn)是引入Lambda表達(dá)式,使用它設(shè)計(jì)的代碼會(huì)更加簡(jiǎn)潔。接下來(lái)通過(guò)本文給大家介紹Android studio配置lambda表達(dá)式教程,需要的朋友參考下吧2017-05-05
Android控件gridview實(shí)現(xiàn)單行多列橫向滾動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了Android控件gridview實(shí)現(xiàn)單行多列橫向滾動(dòng)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12
利用HorizontalScrollView實(shí)現(xiàn)滑動(dòng)頁(yè)面時(shí)的縮放效果
這篇文章主要為大家詳細(xì)介紹了利用HorizontalScrollView實(shí)現(xiàn)滑動(dòng)頁(yè)面時(shí)的縮放效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-11-11
RecyclerView焦點(diǎn)跳轉(zhuǎn)BUG優(yōu)化的方法
這篇文章主要介紹了RecyclerView焦點(diǎn)跳轉(zhuǎn)BUG優(yōu)化的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-04-04
關(guān)于android studio升級(jí)4.1 某些插件使用不了的問(wèn)題(Mac)
這篇文章主要介紹了關(guān)于android studio升級(jí)4.1 某些插件使用不了的問(wèn)題(Mac),本文給大家分享解決方法供大家參考,感興趣的朋友跟隨小編一起看看吧2020-10-10

