平常所说的工厂模式即工厂方法模式,它是最常用的一种工厂模式。注意,本章的抽象类与接口不做具体区分。
简单工厂模式(simple factory pattern)
定义
定义一个工厂类,创建工厂类的静态(static)方法根据传入参数的不同创建不同的实例,被创建的实例通常具有相同的父类。简单工厂方法又称静态工厂方法。
在没有工厂类之前,客户端通过new关键字创建对象,但有了工厂类之后,通过工厂类中提供的静态方法创建实例。
首先将需要创建的不同对象(具体产品)的相关代码封装到一个抽象类(抽象产品)中,作为这些对象的父类。然后提供一个工厂类用于创建不同的对象产品,在工厂类中提供一个创建不同对象的方法,可以根据不同的传入参数创建不同的对象。
简单工厂模式的3个角色(类)
(1)Factory(工厂角色):即工厂类,它是简单工厂模式的核心,它包含了必要的判断逻辑,根据外界给定的信息(即工厂方法的传入参数),决定应该创建哪个具体类的对象。工厂类可以被外界调用,创建所需的产品对象。在工厂类中提供静态工厂方法factoryMethod(),它的返回类型为抽象产品角色Product。
(2)Procuct(抽象产品角色):它是工厂类所创建的所有具体产品类的父类,封装了各种产品类都公有方法,它的引入提供了系统的灵活性,使得在工厂类中只需要定义一个通用的工厂方法,因为所有创建的具体产品对象都是其子类对象。
(3)ConcreteProduct(具体产品角色):它是简单工厂模式的创建目标。所有被创建的对象都是这个角色的某个具体类的实例。每一个具体产品角色都继承了抽象产品角色,所以需要实现抽象产品角色中的抽象方法。
1 | //抽象类 |
1 | //具体实现类ConcreteProductA |
1 | //工厂类 |
在客户端代码中,通过调用工厂类的工厂方法即可得到产品对象,如
1 | class Client{ |
客户端的重构
每更换一次对象都要修改客户端中静态方法的参数,客户端代码将要重新编译,这对于客户端而言,违反了开闭原则。如何在不修改客户端代码的前提下更换具体产品对象?
可以将工厂类中静态方法的参数放在XML或properties格式的配置文件中。如下config.xml所示:
1 |
|
再通过工具类XMLUtil来读取配置文件中的字符串参数:
1 | import javax.xml.parsers.*; |
简单工厂模式的简化
有时候为了简化简单工厂模式,可以将抽象产品类和工厂类进行合并,将静态工厂方法转移到抽象产品中。
工厂方法模式(factory method pattern)
简单工厂模式存在两个严重的问题:
(1)工厂类过于庞大,大量if..else语句会造成维护困难;
(2)当系统中需要引进新产品时,由于静态工厂方法通过传入不同的参数来创建不同的产品,这必定要修改工厂类的源代码,需要在其中加入必要的业务逻辑,这将违背“开闭原则”。
如何实现增加新产品而不影响已有代码?(也可以通过上一节提到的将静态方法的参数放在config.xml和propertise文件中实现。)在工厂方法模式中,不再提供统一的工厂类来创建所有的产品对象,而是针对不同的对象提供不同的工厂。(将简单工厂模式中的工厂类抽象化了。)
定义
定义一个用于创建对象的接口(抽象工厂),让子类决定将哪一个类实例化。工厂方法模式让一个列的实例化延迟到其子类。工厂方法模式又叫工厂模式(factory pattern)、虚拟构造器模式(virtual constructor pattern)或多态工厂模式(polymorphic factory pattern)。
工厂方法模式提供一个抽象工厂接口来声明抽象工厂方法,而由其子类来具体实现工厂方法,创建具体的产品对象。
工厂方法模式的4个角色(类)
(1)Product(抽象产品)
它是定义产品的接口,是产品对象的公共父类。
(2)ConcreteProduct(具体产品)
它实现了抽象产品接口,某种类型的具体产品由专门的工厂创建。
(3)Factory(抽象工厂)
声明了一个工厂方法,用于返回具体产品对象。抽象工厂是工厂方法模式的核心,所有创建对象的工厂类都必须实现该接口。
(4)ConcreteFactory(具体工厂)
它是抽象工厂的子类,实现了抽象工厂中定义的工厂方法,并可由客户端调用,返回一个具体产品类的实例。
1 | //抽象工厂 |
1 | //具体工厂 |
1 | //抽象产品 |
1 | //具体产品 |
1 | //客户端代码 |
反射与配置文件
客户端的重构:不再使用new关键字创建工厂对象,而是将具体工厂类的类名存储在配置文件中。通过读取配置文件读取类名字符串,再使用Java的反射机制,根据类名生成字符串对象.
1 | //通过类名生成实例对象并返回 |
重载的工厂方法
可以有多种方法来初始化具体产品,即在具体工厂类中对构造器进行重载。
抽象工厂模式(abstract factory pattern)
工厂方法模式由于每个工厂类只生产一类产品,可能会导致系统中存在大量的工厂类,增加系统开销。考虑将相关的产品组成“产品族”,由同一个工厂来产生。
定义
提供一个创建一些列相关或相互依赖对象的接口,而无需指定它们的具体类。抽象工厂模式又称Kit模式。与工厂方法模式不同的是,抽象工厂模式中的具体工厂不只是创建一种产品,而是创建一族产品。
抽象工厂中定义了多个抽象工厂方法,每一个具体工厂实现了这些工厂方法用于产生多种不同类型的产品,一个工厂类的产品构成了一个产品族。
抽象工厂模式包含4种角色(类)
(1)Factory(抽象工厂)
声明了一组方法,用于创建一族产品。每个方法对应一种产品。
(2)ConcreteFactory(具体工厂)
实现了在抽象工厂中声明的创建具体产品的方法,一个具体工厂生成一族产品,构成一个产品族。
(3)Product(抽象产品)
为每种产品声明接口,在抽象产品中声明了产品所具有的业务方法。
(4)ConcreteProduct(具体产品)
定义具体工厂生产的具体产品对象,实现抽闲产品中声明的业务方法。
1 | //抽象工厂 |
1 | //具体工厂类1 |
1 | //具体工厂类2 |
1 | //抽象产品 |
1 | //具体产品 |
1 | //客户端类 |
为了让系统局部良好的灵活性和可扩展性,引入XML配置文件和XMLUtil工具类。
“开闭原则”的倾斜性
在抽象工厂模式中,增加新的产品族(即增加一个具体产品类)很方便,但增加新的产品等级结构(即在一个工厂类中增加方法)很麻烦,需要修改所欲的工厂角色。这种性质称为“开闭原则”的倾斜性。
单例模式(singleton pattern)
定义
单例就是保证始终只有一个实例。确保对象的唯一性,如Windows的任务管理器,打印控制器,数据库连接池等。实现单例注意点:
(1)构造器私有,外部无法创建实例;
(2)因此需要提供公有的方法来获取实例getInstance();
(3)静态类私有成员变量,保证只有一个变量引用。
单例模式的两种实现形式
(1)懒汉式:在第一次获取实例时才创建对象。
如果在获取实例前线程A暂停了,此时线程B获取实例并创建了对象并返回了,然后线程A恢复,又创建了一个对象,不能保证单例,因此需要为getInstance()方法加上synchronized关键字保证线程同步。
1 | private static ConObject obj = null; |
(2)饿汉式:在类加载时就已经创建对象了。
饿汉式不存在线程安全问题,但容易造成资源浪费,因为实例在类加载的时候随着静态变量的初始化而创建,但有时候可能并不会使用该实例。
1 | private static ConObject obj = new ConObject (); |