面向对象23个设计模式(1)——工厂方法模式
2017-12-12 18:24
281 查看
参考链接:https://en.wikipedia.org/wiki/Factory_method_pattern
http://blog.csdn.net/xmlife/article/details/43491039
工厂方法设计模式(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图解:
迷宫游戏可能有两种模式,一种是带有只有相邻房间才互通的普通房间,还有一种是带有可以允许玩家随机传送的魔法房间,
工厂模式处理对象的实例化而不暴露实例化逻辑。换句话说,工厂实际上是具有公共接口的对象的创建者。
上面的代码创建了一个
你可以看到我们在ConcreteFactory中使用了doSomething,所以你可以通过它调用doSomething()获得IProduct.你也可以获得对象之后在具体的工厂方法写你自定义的逻辑。getObject在工厂接口中是抽象的。
再看一次工厂方法模式的定义:定义一个创建对象的接口,但让子类决定去实例化哪一个类。工厂方法让一个类将它要使用的实例推迟到子类实现。 这个接口不一定是interface,它也可以是abstract类的abstract方法,甚至可以是一个有默认实现的具体方法(当然,不推荐这样做)。
下面我以ListView的BaseAdapter为例:
BaseAdapter的默认实现如下:
每次都要重写这几个方法,嫌麻烦?初步改写:
这样我们在使用时只需继承重写getView即可:
到这里我们发现还是有可以改进的地方:每个Adapter的getView方法都有一段逻辑是重复的:
但是在
这里面
最后还可以使用泛型让子类免去对ViewHolder的强制类型转换,最终的CommonListAdapter如下:
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); } } }
相关文章推荐
- 面向对象的设计模式系列之二:工厂方法模式(FactoryMethod)
- Java 可复用的面向对象的设计模式之 工厂模式Factory详解 :简单工厂模式 工厂方法模式 抽象工厂模式
- 面向对象的设计模式系列之二:工厂方法模式(FactoryMethod)
- 23个经典的面向对象的设计模式与泡妞大法
- 设计模式(2):工厂方法模式
- java 设计模式 学习笔记 (2) - 工厂方法模式
- javascript面向对象和设计模式
- 设计模式---------工厂方法模式
- 设计模式--工厂方法模式(Factory Method)
- 设计模式->创建型模式->工厂方法模式
- 深入浅出设计模式——工厂方法模式(Factory Method)
- 【设计模式】Factory Method Pattern——工厂方法模式
- 23个设计模式列表
- JAVA设计模式之工厂方法模式
- JAVA--简单工厂模式,工厂方法模式--设计模式一
- Android设计模式及面向对象原则
- C#设计模式——工厂方法模式(Factory Method Pattern)
- 设计模式之二 工厂方法模式
- [js高手之路]面向对象+设计模式+继承一步步改造简单的四则运算
- 设计模式系列——三个工厂模式(简单工厂模式,工厂方法模式,抽象工厂模式)