首页 > 代码库 > 设计模式(四)The Factory Pattern 工厂模式
设计模式(四)The Factory Pattern 工厂模式
一、简单工厂
定义:定义一个创建对象的接口,但是由其子类决定要实例化的对象是哪一个,工厂方法让类的实例化推迟到子类。
通俗的来讲就是由工厂方法确定一个框架,具体的实现由其子类来完成。与简单工厂相比,简单工厂可是完成了整个对象的创建。
严格的来说简单工厂并不是一种设计模式,他更像是一种编程习惯。
代码说明一切!
1、这是一个简单工厂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | package my.oschina.net.design.factory; public class SimplePizzaFactory { /** * 根据传入的type参数,返回相应的pizza * @param type * @return */ public Pizza createPizza(String type) { Pizza pizza = null ; if (type.equals( "cheese" )) { pizza = new CheesePizza(); } else if (type.equals( "pepperoni" )) { pizza = new PepperoniPizza(); } else if (type.equals( "clam" )) { pizza = new ClamPizza(); } else if (type.equals( "veggie" )) { pizza = new VeggiePizza(); } return pizza; } } |
2、这是一个pizza的store
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | package my.oschina.net.design.factory; public class PizzaStore { //通过组合的使用,加上一个简单工厂SimplePizzaFactory的引用,用于创建pizza SimplePizzaFactory factory; public PizzaStore(SimplePizzaFactory factory) { this .factory = factory; } public Pizza orderPizza(String type) { Pizza pizza; //调用简单工厂SimplePizzaFactory的createPizza(type)方法创建pizza pizza = factory.createPizza(type); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } } |
我们可以看到,当我们需要一个pizza时并不需要知道该如何创建具体的pizza,我们只要调用一个简单工厂的方法,工厂就会给我们返回一个pizza,很炫酷!(更深一步,我们可以加入第一个模式——策略模式,也就是说可以动态的设置不同的简单工厂,从而让简单工厂返回不同的pizza)
其实,大家在写代码的时候都或多或少的用到过这个code习惯。比如说在写一些项目的时候,我们会额外加一个叫作utils的包,里面基本上都是一些类的静态方法或者叫做工具函数(像一些提取特定字符串啊等)。这其实就是静态工厂(我们不需要创建对象就可以通过其类名调用想用的工具函数),而与普通的简单工厂相比,他不能通过继承改变创建对象的行为。
二、工厂方法模式
看完简单工厂,你可能会说,哟,不错哦!那我们为什么还要使用工厂方法模式呢?
仔细想想,假如我们又出新产品了,不仅有cheese, peperoni, clam, vegie的pizza还有什么白菜,萝卜,西瓜pizza,意思就是说我们的产品可是变化着的,那问题来了,我们会不可避免的修改代码,什么?修改类的代码!那就不好了!head first 曾提及过一个原则叫做开放关闭原则,大概的意思是说,类应该对扩展开放(你可以通过继承来扩展这个类),而对修改关闭(你要是想要修改我这个类,我就不愿意了)。
很明显啊!简单工厂要想实现产品的更新工作,就必须要违反这个原则!那肿么办呢?好吧,来看看工厂方法模式。
来看看我们新的PizzaStore(这是一个abstract类,不能创建这个类的对象)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | package my.oschina.net.design.factory; /** * 这是一个抽象类,通过继承实现createPizza方法, * 我们可以创建不同的对象 * @author Eswin * */ public abstract class PizzaStore { //这里定义一个工厂方法 abstract Pizza createPizza(String item); public Pizza orderPizza(String type) { Pizza pizza = createPizza(type); System.out.println( "--- Making a " + pizza.getName() + " ---" ); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } } |
再来看两个他的子类(通过继承我们实现了超类代码和子类创建对象的代码的解耦)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | package my.oschina.net.design.factory; public class ChicagoPizzaStore extends PizzaStore { Pizza createPizza(String item) { if (item.equals( "cheese" )) { return new ChicagoStyleCheesePizza(); } else if (item.equals( "veggie" )) { return new ChicagoStyleVeggiePizza(); } else if (item.equals( "clam" )) { return new ChicagoStyleClamPizza(); } else if (item.equals( "pepperoni" )) { return new ChicagoStylePepperoniPizza(); } else return null ; } } package my.oschina.net.design.factory; public class NYPizzaStore extends PizzaStore { Pizza createPizza(String item) { if (item.equals( "cheese" )) { return new NYStyleCheesePizza(); } else if (item.equals( "veggie" )) { return new NYStyleVeggiePizza(); } else if (item.equals( "clam" )) { return new NYStyleClamPizza(); } else if (item.equals( "pepperoni" )) { return new NYStylePepperoniPizza(); } else return null ; } } |
检验代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | package my.oschina.net.design.factory; public class PizzaTestDrive { public static void main(String[] args) { PizzaStore nyStore = new NYPizzaStore(); PizzaStore chicagoStore = new ChicagoPizzaStore(); Pizza pizza = nyStore.orderPizza( "cheese" ); System.out.println( "Ethan ordered a " + pizza.getName() + "\n" ); pizza = chicagoStore.orderPizza( "cheese" ); System.out.println( "Joel ordered a " + pizza.getName() + "\n" ); pizza = nyStore.orderPizza( "clam" ); System.out.println( "Ethan ordered a " + pizza.getName() + "\n" ); pizza = chicagoStore.orderPizza( "clam" ); System.out.println( "Joel ordered a " + pizza.getName() + "\n" ); } } |
这样,我们通过不同的store(不同的子类)相同的orderPizza方法就可以创造出不同的pizza!
三、抽象工厂
定义:提供一个接口,用于创建一组相关的或者相依赖的的家族,而不需明确指明具体类。
在我理解就是纯粹的工厂方法的叠加,他提供一个创建一系列相关联的一组对象的接口,由实现这个接口的类来具体实现各个对象的创建工作
先来定义一个接口PizzaIngredientFactory,他定义了一系列的创建Pizza材料的方法
1 2 3 4 5 6 7 8 9 10 11 12 | package my.oschina.net.design.abstract_actory; public interface PizzaIngredientFactory { public Dough createDough(); public Sauce createSauce(); public Cheese createCheese(); public Veggies[] createVeggies(); public Pepperoni createPepperoni(); public Clams createClam(); } |
看他的两个子类(准确的说是实现了这个接口的类)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | package my.oschina.net.design.abstract_actory; public class NYPizzaIngredientFactory implements PizzaIngredientFactory { public Dough createDough() { return new ThinCrustDough(); } public Sauce createSauce() { return new MarinaraSauce(); } public Cheese createCheese() { return new ReggianoCheese(); } public Veggies[] createVeggies() { Veggies veggies[] = { new Garlic(), new Onion(), new Mushroom(), new RedPepper() }; return veggies; } public Pepperoni createPepperoni() { return new SlicedPepperoni(); } public Clams createClam() { return new FreshClams(); } } package my.oschina.net.design.abstract_actory; public class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory { public Dough createDough() { return new ThickCrustDough(); } public Sauce createSauce() { return new PlumTomatoSauce(); } public Cheese createCheese() { return new MozzarellaCheese(); } public Veggies[] createVeggies() { Veggies veggies[] = { new BlackOlives(), new Spinach(), new Eggplant() }; return veggies; } public Pepperoni createPepperoni() { return new SlicedPepperoni(); } public Clams createClam() { return new FrozenClams(); } } |
看看我们的CheesePizza类和ClamPizza类的创建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | package my.oschina.net.design.abstract_actory; public class CheesePizza extends Pizza { //这里组合了一个PizzaIngredientFactory对象的引用,用于提供不同的原料 PizzaIngredientFactory ingredientFactory; /** * 通过传入一个PizzaIngredientFactory原料工厂,我们可以在制作Pizza的时候动态的产生所需要的原料 * @param ingredientFactory */ public CheesePizza(PizzaIngredientFactory ingredientFactory) { this .ingredientFactory = ingredientFactory; } void prepare() { System.out.println( "Preparing " + name); dough = ingredientFactory.createDough(); sauce = ingredientFactory.createSauce(); cheese = ingredientFactory.createCheese(); } } package my.oschina.net.design.abstract_actory; public class ClamPizza extends Pizza { PizzaIngredientFactory ingredientFactory; public ClamPizza(PizzaIngredientFactory ingredientFactory) { this .ingredientFactory = ingredientFactory; } void prepare() { System.out.println( "Preparing " + name); dough = ingredientFactory.createDough(); sauce = ingredientFactory.createSauce(); cheese = ingredientFactory.createCheese(); clam = ingredientFactory.createClam(); } } |
再来看看改进后的NYPPizzaStore
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | package my.oschina.net.design.abstract_actory; public class NYPizzaStore extends PizzaStore { protected Pizza createPizza(String item) { Pizza pizza = null ; PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory(); if (item.equals( "cheese" )) { pizza = new CheesePizza(ingredientFactory); pizza.setName( "New York Style Cheese Pizza" ); } else if (item.equals( "veggie" )) { pizza = new VeggiePizza(ingredientFactory); pizza.setName( "New York Style Veggie Pizza" ); } else if (item.equals( "clam" )) { pizza = new ClamPizza(ingredientFactory); pizza.setName( "New York Style Clam Pizza" ); } else if (item.equals( "pepperoni" )) { pizza = new PepperoniPizza(ingredientFactory); pizza.setName( "New York Style Pepperoni Pizza" ); } return pizza; } } |
对于此时的NYPPizzaStore,创建具体的Pizza的时候我们就要传入一个PizzaIngredientFactory对象了!
四、三者的比较
对于简单工厂,所有的对象的创建由简单工厂全权负责,只要最后可以给我们返回一个我们需要的对象就可以了,他的运用是通过组合的方式实现的;
对于工厂方法,抽象的父类提供一个对象创建的接口,具体对象的创建推迟到子类中去实现,从而实现扩展;
对于抽象工厂,可以看成是工厂方法的叠加,他提供了创建一系列相关的对象的接口,由各个子类去实现,他的运用也是通过组合的方式实现的。
主要内容:
- 工厂方法模式定义
- 工厂方法模式优势
- 工厂方法模式在Android源码中的应用
一、工厂方法模式定义
工厂方法模式定义: |
Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses. |
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。 |
常用的工厂方法模式结构:
如上图所示(截取自《Head First Design Patterns》一书),主要包括四个部分:
抽象产品类Product负责定义产品的共性,实现对事物抽象的定义;Creator是抽象创建类,也就是抽象工厂,具体如何创建产品类是由具体的实现工厂ConcreteCreator完成的。其中在《Head First Design Patterns》对工厂方法模式做了细节的说明,原文如下:
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.
二、工厂方法模式优势
良好的封装性,代码结构清楚。一个对象创建具有条件约束的,如果一个调用者需要一个具体的产品对象,只要知道这个产品的类名就可以了,不用知道创建对象的过程,降低模块间的耦合。 |
可扩展性好。在增加产品类的情况下,只要适当的修改具体的工厂类或扩展一个工厂类,就可以完成。 |
屏蔽产品类。产品类的实现如何变化,调用者都不需要关心,它只需要关心产品的接口,只要接口保持不变,系统中的上层模块就不用改变。因为产品类的实例化工作是由工厂类负责的,一个产品对象具体由哪一个产品生成是由工厂类决定的。此外工厂方法模式是典型的松耦合结构。高层模块只需要知道产品的抽象类,其他的实现类都不用关系,符合迪米特法则、依赖倒置原则、里氏替换原则等。 |
三、工厂方法模式在Android源码中的应用
在Android源码中,ListActivity继承自Activity,将Activity作为工厂方法,生成具有ListView特点的Activity,对ListActivity的说明如下:
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 |
在Activity类中有这么一个函数:
/** * 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.”,英语不太好,但大致的意思是Activity子类可以重载这个函数来决定是否使用默认的实现。
在看子类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); } }; }
其中复写了函数onRestoreInstanceState(Bundle state),并在View中设置了默认的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()函数:
/** * 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(); }
总结:Activity作为“工厂方法”,具体View中显示什么由默认设置或者由子类来实现;ListActivity作为具体实现,它决定在View中显示的是ListView;这里的View是Activity中的默认显示,即为“Product”,而ListView是“ConcreteProduct”,由ListActivity来决定显示。
除了ListActivity以外,还有ExpandableListActivity也是以Activity为工厂类,创建自己的显示效果。
设计模式(四)The Factory Pattern 工厂模式