工厂模式

2018-06-05  本文已影响0人  zfylin

工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式。通常我们所说的工厂模式是指工厂方法模式,它也是使用频率最高的工厂模式。工厂模式在Java程序系统可以说是随处可见。因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A() 工厂模式也是用来创建实例对象的,所以以后new时就要多个心眼,是否可以考虑使用工厂模式,虽然这样做,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量。

工厂模式的作用

工厂模式(包括简单工厂模式、工厂方法模式和抽象工厂模式)到底有什么用,很多时候通过反射机制就可以很灵活地创建对象,为什么还要工厂?

与一个对象有关的原则有三类:

  1. 对象本身所具有的职责
  2. 创建对象的职责
  3. 使用对象的职责

对象本身的职责很好理解,就是对象自身所具有的一些数据和行为,可通过一些公开的方法来实现它的职责。在本文中,我们将简单讨论一下对象的创建职责和使用职责。

在Java语言中,我们通常有以下几种创建对象的方式:

  1. 使用new关键字直接创建对象;
  2. 通过反射机制创建对象;
  3. 通过clone()方法创建对象;
  4. 通过工厂类创建对象。

毫无疑问,在客户端代码中直接使用new关键字是最简单的一种创建对象的方式,但是它的灵活性较差,下面通过一个简单的示例来加以说明:

class LoginAction {
    private UserDAO udao;
    
    public LoginAction() {
        udao = new JDBCUserDAO(); //创建对象
    }
    
    public void execute() {
        //其他代码
        udao.findUserById(); //使用对象
        //其他代码
    }
}

在LoginAction类中定义了一个UserDAO类型的对象udao,在LoginAction的构造函数中创建了JDBCUserDAO类型的udao对象,并在execute()方法中调用了udao对象的findUserById()方法,这段代码看上去并没有什么问题。下面我们来分析一下LoginAction和UserDAO之间的关系,LoginAction类负责创建了一个UserDAO子类的对象并使用UserDAO的方法来完成相应的业务处理,也就是说LoginAction即负责udao的创建又负责udao的使用,创建对象和使用对象的职责耦合在一起,这样的设计会导致一个很严重的问题:如果在LoginAction中希望能够使用UserDAO的另一个子类如HibernateUserDAO类型的对象,必须修改LoginAction类的源代码,违反了“开闭原则”。如何解决该问题?

最常用的一种解决方法是将udao对象的创建职责从LoginAction类中移除,在LoginAction类之外创建对象,那么谁来负责创建UserDAO对象呢?答案是:工厂类。通过引入工厂类,客户类(如LoginAction)不涉及对象的创建,对象的创建者也不会涉及对象的使用。引入工厂类UserDAOFactory之后的结构如图1所示:

enter image description here

工厂类的引入将降低因为产品或工厂类改变所造成的维护工作量。如果UserDAO的某个子类的构造函数发生改变或者要需要添加或移除不同的子类,只要维护UserDAOFactory的代码,而不会影响到LoginAction;如果UserDAO的接口发生改变,例如添加、移除方法或改变方法名,只需要修改LoginAction,不会给UserDAOFactory带来任何影响。
在所有的工厂模式中,我们都强调一点:两个类A和B之间的关系应该仅仅是A创建B或是A使用B,而不能两种关系都有。将对象的创建和使用分离,也使得系统更加符合“单一职责原则”,有利于对功能的复用和系统的维护。

此外,将对象的创建和使用分离还有一个好处:防止用来实例化一个类的数据和代码在多个类中到处都是,可以将有关创建的知识搬移到一个工厂类中,这在Joshua Kerievsky的《重构与模式》一书中有专门的一节来进行介绍。因为有时候我们创建一个对象不只是简单调用其构造函数,还需要设置一些参数,可能还需要配置环境,如果将这些代码散落在每一个创建对象的客户类中,势必会出现代码重复、创建蔓延的问题,而这些客户类其实无须承担对象的创建工作,它们只需使用已创建好的对象就可以了。此时,可以引入工厂类来封装对象的创建逻辑和客户代码的实例化/配置选项。

使用工厂类还有一个“不是特别明显的”优点,一个类可能拥有多个构造函数,而在Java、C#等语言中构造函数名字都与类名相同,客户端只能通过传入不同的参数来调用不同的构造函数创建对象,从构造函数和参数列表中也许大家根本不了解不同构造函数所构造的产品的差异。但如果将对象的创建过程封装在工厂类中,我们可以提供一系列名字完全不同的工厂方法,每一个工厂方法对应一个构造函数,客户端可以以一种更加可读、易懂的方式来创建对象,而且,从一组工厂方法中选择一个意义明确的工厂方法,比从一组名称相同参数不同的构造函数中选择一个构造函数要方便很多。

那么,有人可能会问,是否需要为设计中的每一个类都配备一个工厂类?答案是:具体情况具体分析。如果产品类很简单,而且不存在太多变数,其构造过程也很简单,此时无须为其提供工厂类,直接在使用之前实例化即可,例如Java语言中的String类,我们就无须为它专门提供一个StringFactory,这样做反而有点像杀鸡用牛刀,大材小用,而且会导致工厂泛滥,增加系统的复杂度。

工厂模式是为了将对象的创建和使用分离,也使得系统更加符合“单一职责原则”,有利于对功能的复用和系统的维护,防止用来实例化一个类的数据和代码在多个类中到处都是,可以将有关创建的知识搬移到一个工厂类中

转载:创建对象与使用对象——谈谈工厂的作用

简单工厂模式

简单工厂模式(Simple Factory Pattern):定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。因为在简单工厂模式中用于创建实例的方法是静态(static)方法,因此简单工厂模式又被称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。

简单工厂模式的要点在于:当你需要什么,只需要传入一个正确的参数,就可以获取你所需要的对象,而无须知道其创建细节。简单工厂模式结构比较简单,其核心是工厂类的设计, 其结构如下图所示。

image.png

在简单工厂模式结构图中包含如下几个角色:

在简单工厂模式中,客户端通过工厂类来创建一个产品类的实例,而无须直接使用new关键字来创建对象,它是工厂模式家族中最简单的一员。
在使用简单工厂模式时,首先需要对产品类进行重构,不能设计一个包罗万象的产品类,而需根据实际情况设计一个产品层次结构,将所有产品类公共的代码移至抽象产品类,并在抽象产品类中声明一些抽象方法,以供不同的具体产品类来实现,典型的抽象产品类代码如下所示:

abstract class Product {
    //所有产品类的公共业务方法
    public void methodSame() {
        //公共方法的实现
    }

    //声明抽象业务方法
    public abstract void methodDiff();
}

在具体产品类中实现了抽象产品类中声明的抽象业务方法,不同的具体产品类可以提供不同的实现,典型的具体产品类代码如下所示:

class ConcreteProduct extends Product {
    //实现业务方法
    public void methodDiff() {
        //业务方法的实现
    }
}

简单工厂模式的核心是工厂类,在没有工厂类之前,客户端一般会使用new关键字来直接创建产品对象,而在引入工厂类之后,客户端可以通过工厂类来创建产品,在简单工厂模式中,工厂类提供了一个静态工厂方法供客户端使用,根据所传入的参数不同可以创建不同的产品对象,典型的工厂类代码如下所示:

class Factory {
    //静态工厂方法
    public static Product getProduct(String arg) {
        Product product = null;
        if (arg.equalsIgnoreCase("A")) {
            product = new ConcreteProductA();
            //初始化设置product
        }
        else if (arg.equalsIgnoreCase("B")) {
            product = new ConcreteProductB();
            //初始化设置product
        }
        return product;
    }
}

在客户端代码中,我们通过调用工厂类的工厂方法即可得到产品对象,典型代码如下所示:

class Client {
    public static void main(String args[]) {
        Product product; 
        product = Factory.getProduct("A"); //通过工厂类创建产品对象
        product.methodSame();
        product.methodDiff();
    }
}

简单工厂模式总结:

简单工厂模式提供了专门的工厂类用于创建对象,将对象的创建和对象的使用分离开,它作为一种最简单的工厂模式在软件开发中得到了较为广泛的应用。

转载:工厂三兄弟之简单工厂模式(二)

工厂方法模式

简单工厂模式虽然简单,但存在一个很严重的问题。当系统中需要引入新产品时,由于静态工厂方法通过所传入参数的不同来创建不同的产品,这必定要修改工厂类的源代码,将违背“开闭原则”,如何实现增加新产品而不影响已有代码?工厂方法模式应运而生.

在工厂方法模式中,我们不再提供一个统一的工厂类来创建所有的产品对象,而是针对不同的产品提供不同的工厂,系统提供一个与产品等级结构对应的工厂等级结构。

工厂方法模式(Factory Method Pattern):定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。工厂方法模式又简称为工厂模式(Factory Pattern),又可称作虚拟构造器模式(Virtual Constructor Pattern)或多态工厂模式(Polymorphic Factory Pattern)。工厂方法模式是一种类创建型模式。

工厂方法模式提供一个抽象工厂接口来声明抽象工厂方法,而由其子类来具体实现工厂方法,创建具体的产品对象。工厂方法模式结构如图所示:

image.png

在工厂方法模式结构图中包含如下几个角色:

与简单工厂模式相比,工厂方法模式最重要的区别是引入了抽象工厂角色,抽象工厂可以是接口,也可以是抽象类或者具体类,其典型代码如下所示:


interface Factory {
    public Product factoryMethod();
}

在实际使用时,具体工厂类在实现工厂方法时除了创建具体产品对象之外,还可以负责产品对象的初始化工作以及一些资源和环境配置工作,例如连接数据库、创建文件等。
在客户端代码中,只需关心工厂类即可,不同的具体工厂可以创建不同的产品,典型的客户端类代码片段如下所示:

Factory factory;
factory = new ConcreteFactory(); //可以结合配置文件和反射机制来实现
Product product;
product = factory.factoryMethod();

可以通过配置文件来存储具体工厂类ConcreteFactory的类名,更换新的具体工厂时无须修改源代码,系统扩展更为方便。

工厂方法模式总结

参考:工厂三兄弟之工厂方法模式(三)

抽象工厂模式

抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,它是一种对象创建型模式。

在抽象工厂模式中,每一个具体工厂都提供了多个工厂方法用于产生多种不同类型的产品,这些产品构成了一个产品族,抽象工厂模式结构如图所示:

image.png

在抽象工厂模式结构图中包含如下几个角色:

在抽象工厂中声明了多个工厂方法,用于创建不同类型的产品,抽象工厂可以是接口,也可以是抽象类或者具体类,其典型代码如下所示:

class ConcreteFactory1 extends AbstractFactory {
        //工厂方法一
        public AbstractProductA createProductA() {
            return new ConcreteProductA1();
        }

        //工厂方法二
        public AbstractProductB createProductB() {
            return new ConcreteProductB1();
        }

        ……
}

与工厂方法模式一样,抽象工厂模式也可为每一种产品提供一组重载的工厂方法,以不同的方式对产品对象进行创建。

具体工厂实现了抽象工厂,每一个具体的工厂方法可以返回一个特定的产品对象,而同一个具体工厂所创建的产品对象构成了一个产品族。

开闭原则”的倾斜性

在抽象工厂模式中,增加新的产品族很方便,但是增加新的产品等级结构很麻烦,抽象工厂模式的这种性质称为“开闭原则”的倾斜性。“开闭原则”要求系统对扩展开放,对修改封闭,通过扩展达到增强其功能的目的,对于涉及到多个产品族与多个产品等级结构的系统,其功能增强包括两方面:

正因为抽象工厂模式存在“开闭原则”的倾斜性,它以一种倾斜的方式来满足“开闭原则”,为增加新产品族提供方便,但不能为增加新产品结构提供这样的方便,因此要求设计人员在设计之初就能够全面考虑,不会在设计完成之后向系统中增加新的产品等级结构,也不会删除已有的产品等级结构,否则将会导致系统出现较大的修改,为后续维护工作带来诸多麻烦。

抽象工厂模式总结

上一篇 下一篇

猜你喜欢

热点阅读