设计模式之简单工厂模式

设计模式之简单工厂模式
动机:
        不暴露实例化逻辑来创建对象。通过公共的接口创建新的对象。
        这是一个简单的实现,客户端需要一个product,但是client不直接使用new对象,而是通过提供需要的对象信息来找factory得到新的product。
        这个factory实例化一个具体的product并返回(转化成抽象的类),client段使用这个抽象的类而不用考虑它具体的实现。

应用举例:        也许工厂模式是使用最多的模式之一。举个例子,一个操作图像的图形应用。在我们实现绘画的模块就是client,而那些shapes图形就是products,所有的图形都继承自一个abstract shape抽象的形状类(或者接口)。 这个abstract shape定义了绘画draw和move方法去让子类实现。让我们假设有个创建圆形的命令,client接收一个string类型的图形类型参数,client从factory得到concrete shape转换成abstract shape。
        这样的好处是很显然的:新增一个图形不用修改client的代码,只要在factory的实现里面增加新的类型即可。
详细问题和解决方法:
        新手使用switch/case参数化factory,生成product的方法可以被重写,所以factory可以生成更多类型的product对象,使用一个条件(输入一个方法参数或者一些全局配置参数)头标识哪种product对象应该被创建。代码如下:
public class ProductFactory{
    public Product createProduct(String ProductID){
        if (id==ID1)
            return new OneProduct();
        if (id==ID2) return
            return new AnotherProduct();
        ... // so on for the other Ids
        return null; //if the id doesn't have any of the expected values
    }
    ...
}
        这种实现方法是简单直接的(让我们称之为新手实现),这里的问题就是,新增一种product我们就要修改factory类,不够弹性,违反了开闭原则。当然我们可以继承factory类,但是别忘了factory类通常是单例的。
注册类-使用反射reflection:
        如果你可以使用反射,就可以在不修改factory的前提下注册新的product类。在不知道product类型的前提下我们在facoty中创建product对象,我们可使用map保存productID和product type,在这种情况下,新增一个新的product,需要在map中注册该product,这个操作不需要修改factory的代码:
class ProductFactory
{
    private HashMap m_RegisteredProducts = new HashMap();
    public void registerProduct (String productID, Class productClass)
    {
        m_RegisteredProducts.put(productID, productClass);
    }
    public Product createProduct(String productID)
    {
        Class productClass = (Class)m_RegisteredProducts.get(productID);
        Constructor productConstructor = cClass.getDeclaredConstructor(new Class[] { String.class });
        return (Product)productConstructor.newInstance(new Object[] { });
    }
}
        我们可以把registion code注册代码放在任何地方,但是使用static静态代码块在product类里面注册是一个方便的方法,看下面的例子:
1、在product类外面注册:
public static void main(String args[]){
        Factory.instance().registerProduct("ID1", OneProduct.class);
    } 
2、在product类里面注册:
class OneProduct extends Product
{
	static {
		Factory.instance().registerProduct("ID1",OneProduct.class);
	}
	...
}        我们必须确保在factroy使用concrete product具体的product之前完成注册,不然就会出现null point空指针,我们在Main类的static静态代码块中使用Class.forName方法。Class.forName方法支持返回一个类的实例,如果一个编译器没有加载这个类,编译器就会在Class.forName被调用的时候加载。
class Main
{
    static
    {
        try
        {
            Class.forName("OneProduct");
            Class.forName("AnotherProduct");
        }
        catch (ClassNotFoundException any)
        {
            any.printStackTrace();
        }
    }
    public static void main(String args[]) throws PhoneCallNotRegisteredException
    {
        ...
    }
}
        这种反射实现有它的坏处,一个主要的坏处就是它的性能表现,使用反射要比不使用反射性能表现降低10%。
注册类-不使用反射:
        在之前的段落中我们看到facotory使用map保存productID和product type,这个注册器在factory外面实现,因为通过使用反射不用再关心所要创建对象的类型。
        我们不想使用反射,但是同时factory又不需要考虑product类型。我们在product abstract class里面新增一个新的abstract方法,每个具体的类都要实现设个方法去创造自己。我们也不得不改变注册器这样我们将注册具体的product对象。
abstract class Product
{
    public abstract Product createProduct();
    ...
}
class OneProduct extends Product
{
    ...
    static
    {
        ProductFactory.instance().registerProduct("ID1", new OneProduct());
    }
    public OneProduct createProduct()
    {
        return new OneProduct();
    }
    ...
}
class ProductFactory
{
    public void registerProduct(String productID, Product p)    {
        m_RegisteredProducts.put(productID, p);
    }
    public Product createProduct(String productID){
        ((Product)m_RegisteredProducts.get(productID)).createProduct();
    }
}
一个更高级的解决方法-使用抽象工厂abstract factory(工厂方法 factory method):
    这种实现代表一个可修改的注册器类实现,假设我们新增一个新的product,使用过程化的switch/case我们需要修改factory类的代码,而使用注册器类,我们要做的只是把注册器类给factory,而不用修改factory,这肯定是一个弹性的解决方式。
    过程化的实现方式是典型的违背开闭原则的坏例子,我们能看到有很多扩展factory的直接解决方法去避免修改factory。
工厂方法模式factory method pattern经典的实现方式有一些坏处通过注册器,而没有很多的好处:
 好处:当product对象被创建的时候,继承的factory method可以被修改去表现添加的操作。
坏处:
    1、factory必须要用singleton实现。
    2、每个factory必须要在使用前先实例化。
    3、实现起来有更多的困难。
    4、如果一个新的product要被创建,一个新的factory要被创建。
不管怎么样,经典的实现方式有它的好处,可以帮助我们理解抽象工厂模式abstract factory pattern。
结论:        当你设计一个应用的时候,考虑你是否需要使用factory,也许使用factory会带来没必要的复杂。如果你有很多类有相同的基本类型,你就需要使用factory,如果你有许多如下的代码,你就要重新考虑:
(if (ConcreteProduct)genericProduct typeof )
    ((ConcreteProduct)genericProduct).doSomeConcreteOperation().
        如果你决定使用factory模式,我会推荐你使用带注册器的实现(使用反射或者不使用反射都行),避免使用factory method,记住switch/case是最简单,违反开闭原则,只是用来解释factory原理的。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章