參考鏈接:https://en.wikipedia.org/wiki/Factory_method_pattern
http://blog.csdn.net/xmlife/article/details/43491039
概念
在基於類的編程(class-based programming)中,工廠方法模式(the factory method pattern)是創建型模式(creational pattern)之一,它是使用工廠方法來處理在不指定對象的具體類的情況下創建對象的問題。使用工廠方法模式,並不直接調用構造方法,而是通過調用工廠方法創建對象,或者定義一個接口由子類實現,又或者在一個基類中實現並在派生類中選擇性地重寫。
工廠方法設計模式(The Factory Method design pattern)是23個著名的GoF設計模式之一。GoF設計模式描述瞭如何解決
反覆出現的設計問題,從而設計出靈活的可複用的面向對象軟件,即易實現、易更改、易測試和易複用的對象。
工廠方法設計模式解決如下問題:
1. 怎樣創建對象以便讓子類重定義要實例化哪個類。(How can an object be created so that subclasses can redefine which class to instantiate?)
2. 怎樣將類的實例化推遲到子類實現?(How can a class defer instantiation to subclasses?)
在需要(使用)對象的類中直接創建對象是不靈活的,因爲這將類交給了特定的對象,無法獨立於類來修改這個實例。
工廠方法設計模式描述瞭如何解決這類問題:
1. 定義一個創建對象的單獨操作(工廠方法);
2. 調用工廠方法創建對象。
這使得在子類編寫中更改對象的創建方式成爲可能(重新定義要實例化哪個類)(This enables writing of subclasses to change the way an object is created(to redefine which class to instantiate)).
作用
在談到工廠方法模式的用意之前,我們先了解一個概念:低耦合
兩圖中的每根線代表系統的模塊,圓圈代表接口。
緊耦合:如左圖,各個模塊之間相互依賴,當我們面對需求改變時,一個模塊改變時,其他模塊隨之改變,導致整個系統處於不穩定的狀態。
鬆耦合:如右圖,主線爲系統的主模塊,分支爲次模塊。次模塊與主模塊通過接口建立聯繫。各次模塊之間相互獨立,當此模塊被修改和替換時其他次模塊不受影響。
當我們設計軟件系統時,首先得分清系統模塊的主次,並建立主模塊和多個次模塊,主模塊與次模塊之間通過接口連接,各次模塊通過主模塊聯繫。也即:高層模塊(主模塊)不直接依賴於低層次模塊(次模塊),低層次模塊(次模塊)的變動不會影響到主模塊和其他次模塊的變動。
在軟件系統中,經常面臨着”某個對象”的創建工作,但由於需求的變化導致這個對象的創建方式頻繁變化,但是它卻擁有相對穩定的接口。如何應對這種變化呢?需要提供一種”封裝機制”來隔離這個”易變對象”的變化,而使”依賴於該易變對象的其他對象”不隨着需求改變而改變。
工廠方法模式定義一個創建對象的接口,但讓子類決定去實例化哪一個類。工廠方法讓一個類將它要使用的實例推遲到子類實現(The Factory method lets a class defer instantiation it uses to subclasses)
創建一個對象往往需要複雜的過程,這不適合包含在一個組合對象中。對象的創建可能產生大量重複的代碼,可能需要組合對象無法訪問的信息,可能無法提供足夠的抽象級別,或者可能不是組合對象關注的部分。工廠方法設計模式通過定義一個獨立的創建對象的方法,然後讓子類重寫指定要創建的產品的派生類型,解決這些問題。
工廠方法模式依賴繼承,因爲對象的創建是委託給實現工廠方法以創建對象的子類(The factory method pattern relies on inheritance, as object is delegated to subclasses that implement the factory method to create objects.)
參見下面的UML圖解:
結構
在如上的UML類圖解中,需要(使用)Product
對象的Creator
類不會直接實例化Product1
.相反的,Creator
引用一個獨立的factoryMethod()
創建產品對象,這使得Creator
獨立於實例化的具體類。Creator
的子類可以重新定義要實例化的類(Subclasses of Creator can redefine which class to instantiate)。例如,子類Creator1
通過實例化Product1
類實現抽象的factoryMethod()
。
示例
例1 迷宮遊戲
Room
是最終產品(final product: MagicRoom
或者OrdinaryRoom
)的基類。MazeGame
聲明瞭生產這種基本產品( base product )的抽象工廠方法。MagicRoom
或者OrdinaryRoom
是基本產品的子類,實現了最終產品。MagicMazeGame
和OrdinaryMazeGame
是MazeGame的子類,實現了工廠方法可生產最終產品。因此工廠方法將調用者(MazeGame
)從具體類的實現中分離出來。這使得“new”操作冗餘,允許遵守開閉原則,最終產品在發生變化時更加靈活。
迷宮遊戲可能有兩種模式,一種是帶有隻有相鄰房間才互通的普通房間,還有一種是帶有可以允許玩家隨機傳送的魔法房間,MazeGame
使用房間,但是它把創建房間的職責交給創建具體類的子類。常規遊戲可以使用這種模板方法:
public abstract class Room {
public abstract void connect(Room room);
}
/*MazeGame的構造函數是一個模板方法,做些通用邏輯。
它引用了封裝Rooms創建的工廠方法——makeRoom(),這樣子類可以創建其它房間,實現其它模式的遊戲。*/
public abstract class MazeGame {
private final List<Room> rooms = new ArrayList<>();
public MazeGame() {
Room room1 = makeRoom();
Room room2 = makeRoom();
room1.connect(room2);
rooms.add(room1);
rooms.add(room2);
}
protected abstract Room makeRoom();
}
public class MagicRoom extends Room{
@Override
public void connect(Room room) {
}
}
public class OrdinaryRoom extends Room{
@Override
public void connect(Room room) {
}
}
public class MagicMazeGame extends MazeGame{
@Override
protected Room makeRoom() {
return new MagicRoom();
}
}
public class OrdinaryMazeGame extends MazeGame{
@Override
protected Room makeRoom() {
return new OrdinaryRoom();
}
}
例2 造車廠
這一次使用接口而不是子類化(subclassing,但是也可以通過子類化實現同樣的效果)。值得注意的是工廠方法也可以定義爲public,由客戶端代碼直接調用(與上例相反)。
public interface Car {
String getType();
}
public interface CarFactory {
Car makeCar();
}
public class Sedan implements Car{
@Override
public String getType() {
return "Sedan";
}
}
public class SedanFactory implements CarFactory{
@Override
public Car makeCar() {
return new Sedan();
}
}
public class Test {
public static void main(String[] args) {
CarFactory factory = new SedanFactory();
Car car = factory.makeCar();
System.out.println(car.getType());
}
}
工廠模式處理對象的實例化而不暴露實例化邏輯。換句話說,工廠實際上是具有公共接口的對象的創建者。
例3 城裏人和鄉下人
public interface IPerson {
String getName();
}
public class CityPerson implements IPerson{
@Override
public String getName() {
return "City Person";
}
}
public class Villager implements IPerson{
@Override
public String getName() {
return "Village Person";
}
}
public enum PersonType {
RURAL,URBAN
}
public class Factory {
public static IPerson getPerson(PersonType personType) throws NoSuchObjectException {
switch (personType) {
case RURAL:
return new Villager();
case URBAN:
return new CityPerson();
default:
throw new NoSuchObjectException("哪有這種人啊大哥?");
}
}
}
public class Test {
public static void main(String[] args) throws NoSuchObjectException {
System.out.println(Factory.getPerson(PersonType.Urban).getName());
}
}
上面的代碼創建了一個IPerson
接口和Villager
和CityPerson
兩個實現。基於傳入到Factory對象(我這裏用的是靜態方法)的類型(指PersonType
),我們將原來那個具體的對象作爲IPerson
接口返回。工廠方法只是工廠類的補充。它通過接口創建類的對象,但在另一方面,它也還是讓子類(subclass)決定實例化哪個類。
例4 手機
public interface IProduct {
String getName();
String setPrice(double price);
}
public class Phone implements IProduct{
private double price;
@Override
public String getName() {
return "Apple TouchPad";
}
@Override
public String setPrice(double price) {
this.price = price;
return "success";
}
}
/*Almost same as Factory, just a additional exposure to do something with the created method*/
public abstract class ProductAbstractFactory {
protected abstract IProduct doSomething();
public IProduct getObject(){// 工廠方法的實現
return this.doSomething();
}
}
public class PhoneConcreteFactory extends ProductAbstractFactory{
@Override
protected IProduct doSomething() {
IProduct product = new Phone();
product.setPrice(20.30);
return product;
}
}
public class Test {
public static void main(String[] args) {
ProductAbstractFactory factory = new PhoneConcreteFactory();
IProduct product = factory.getObject();
System.out.println(product.getName());
}
}
你可以看到我們在ConcreteFactory中使用了doSomething,所以你可以通過它調用doSomething()獲得IProduct.你也可以獲得對象之後在具體的工廠方法寫你自定義的邏輯。getObject在工廠接口中是抽象的。
在Android中的應用
我們在學習工廠方法模式的時候很容易被“工廠”迷惑,總覺得工廠應該“生產”東西,然後建立一個**Factory類。這是對工廠方法模式的曲解。
再看一次工廠方法模式的定義:定義一個創建對象的接口,但讓子類決定去實例化哪一個類。工廠方法讓一個類將它要使用的實例推遲到子類實現。 這個接口不一定是interface,它也可以是abstract類的abstract方法,甚至可以是一個有默認實現的具體方法(當然,不推薦這樣做)。
下面我以ListView的BaseAdapter爲例:
BaseAdapter的默認實現如下:
public class CommonListAdapter extends BaseAdapter{
@Override
public int getCount() {
return 0;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return null;
}
}
每次都要重寫這幾個方法,嫌麻煩?初步改寫:
public abstract class CommonListAdapter<T> extends BaseAdapter {
private Context mContext;
private LayoutInflater mInflater;
private List<T> mData;
public CommonListAdapter(Context context, List<T> data){
mContext = context;
mInflater = LayoutInflater.from(mContext);
mData = data;
}
protected Context getContext() {
return mContext;
}
protected LayoutInflater getInflater(){
return mInflater;
}
@Override
public int getCount() {
return mData != null ? mData.size() : 0;
}
@Override
public Object getItem(int position) {
return mData != null ? mData.get(position) : null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public abstract View getView(int position, View convertView, ViewGroup parent);
}
這樣我們在使用時只需繼承重寫getView即可:
public class TestAdapter extends CommonListAdapter<String> {
public TestAdapter(Context context, List<String> data) {
super(context, data);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView==null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_test,parent,false);
viewHolder = new ViewHolder(convertView);
convertView.setTag(viewHolder);
}else {
viewHolder = ((ViewHolder) convertView.getTag());
}
// todo:some more code
return convertView;
}
class ViewHolder {
View mItemView;
public ViewHolder(View itemView) {
mItemView = itemView;
}
}
}
到這裏我們發現還是有可以改進的地方:每個Adapter的getView方法都有一段邏輯是重複的:
ViewHolder viewHolder;
if(convertView == null){
convertView = ...
viewHolder = ...
convertView.setTag(viewHolder);
}else{
viewHolder = ((ViewHolder) convertView.getTag());
}
但是在Adapter
的具體類中,每個Adapter
的itemView
和ViewHolder
都是不一樣的,在Adapter
的基類中沒能直接得到itemView
和ViewHolder
的實例,怎麼辦?對,這裏可以用到工廠方法,將獲取ItemView
和ViewHolder
對象的操作定義爲一個抽象方法,這樣子類繼承的時候不就可以根據實際情況進行實例化了?於是CommonListAdapter
的getView()
部分進一步修改如下:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
convertView = onCreateItemView(mInflater, position, parent);
viewHolder = onCreateViewHolder(convertView);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
onBindViewHolder(position, viewHolder);
return convertView;
}
protected abstract View onCreateItemView(LayoutInflater inflater, int position, ViewGroup parent);
protected abstract ViewHolder onCreateViewHolder(View convertView);
protected abstract void onBindViewHolder(int position, ViewHolder viewHolder);
這裏面onCreateItemView
和onCreateViewHolder
都屬於工廠方法,CommonListAdapter
通過這兩個方法獲得需要的對象,而這兩個對象的具體實例化,則是交給子類實現的:
public class TestAdapter extends CommonListAdapter<String> {
public TestAdapter(Context context, List<String> data) {
super(context, data);
}
@Override
protected View onCreateItemView(LayoutInflater inflater, int position, ViewGroup parent) {
return inflater.inflate(aResId,parent,false);
}
@Override
protected ViewHolder onCreateViewHolder(View convertView) {
return new TestViewHolder(convertView);
}
@Override
protected void onBindViewHolder(int position, ViewHolder viewHolder) {
TestViewHolder vh = (TestViewHolder)viewHolder;
// TODO: 操作TestViewHolder
}
class TestViewHolder extends ViewHolder{
// TODO: View成員變量
public TestViewHolder(View itemView) {
super(itemView);
// TODO: findViewById
}
}
}
最後還可以使用泛型讓子類免去對ViewHolder的強制類型轉換,最終的CommonListAdapter如下:
public abstract class CommonListAdapter<T,VH extends CommonListAdapter.ViewHolder> extends BaseAdapter {
private LayoutInflater mInflater;
private Context mContext;
private List<T> mData;
public CommonListAdapter(Context context, List<T> data) {
mContext = context;
mData = data;
mInflater = LayoutInflater.from(mContext);
}
protected Context getContext() {
return mContext;
}
@Override
public int getCount() {
return mData != null ? mData.size() : 0;
}
@Override
public Object getItem(int position) {
return mData != null ? mData.get(position) : null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
VH viewHolder;
if (convertView == null) {
convertView = onCreateItemView(mInflater, position, parent);
viewHolder = onCreateViewHolder(convertView);
} else {
viewHolder = (VH) convertView.getTag();
}
onBindViewHolder(position, viewHolder);
return convertView;
}
protected abstract View onCreateItemView(LayoutInflater inflater, int position, ViewGroup parent);
protected abstract VH onCreateViewHolder(View convertView);
protected abstract void onBindViewHolder(int position, VH viewHolder);
public static class ViewHolder {
private View mItemView;
public ViewHolder(View itemView) {
mItemView = itemView;
}
public View getItemView() {
return mItemView;
}
}
}
//使用時:
public class TestAdapter extends CommonListAdapter<String, TestAdapter.TestViewHolder> {
public TestAdapter(Context context, List<String> data) {
super(context, data);
}
@Override
protected View onCreateItemView(LayoutInflater inflater, int position, ViewGroup parent) {
return inflater.inflate(aResId, parent, false);
}
@Override
protected TestViewHolder onCreateViewHolder(View convertView) {
return new TestViewHolder(convertView);
}
@Override
protected void onBindViewHolder(int position, TestViewHolder viewHolder) {
// TODO: 操作TestViewHolder
}
public class TestViewHolder extends CommonListAdapter.ViewHolder {
public TestViewHolder(View itemView) {
super(itemView);
}
}
}