Collection 接口就相当于 VideoFactory
相当于各种具体的工厂,如 JavaVideoFactory
Itr 就是具体产品 JavaVideo
返回值是一个抽象类,必有一子类实现其,看一下
这其中URLStreamHandler就相当于各种抽象产品,而其实现类即各种具体的产品 URLStreamHandlerFactory就相当于 VideoFactory 而如下 Factory 就相当于如 JavaVideoFactory/PythonVideoFactory
对造人过程进行分析,该过程涉及三个对象:女娲、八卦炉、三种不同肤色的人
Client
表示所有人种定义完毕,下一步就是定义一个八卦炉,然后烧制人类
最可能给八卦炉下达什么样的生产命令呢?
应该是给我生产出一个黄色人种(YellowHuman类)
而不会是给我生产一个会走、会跑、会说话、皮肤是黄色的人种
因为这样的命令增加了交流的成本,作为一个生产的管理者,只要知道生产什么就可以了,而不需要事物的具体信息
在这里采用了泛型,通过定义泛型对createHuman的输入参数产生两层限制
● 必须是Class类型
● 必须是Human的实现类
其中的T
表示,只要实现了Human接口的类都可以作为参数
只有一个八卦炉,其实现生产人类的方法
人种有了,八卦炉也有了,剩下的工作就是女娲采集黄土,然后命令八卦炉开始生产
人种有了,八卦炉有了,负责生产的女娲也有了 运行一下,结果如下所示
Pet
层次生成对象的问题
每当添加一种新Pet
类型,必须记住将其添加到 LiteralPetCreator.java
的条目中。在一个定期添加更多类的系统中,这可能会成为问题。你可能会考虑向每个子类添加静态初始值设定项,因此初始值设定项会将其类添加到某个列表中。但静态初始值设定项仅在首次加载类时调用:生成器的列表中没有类,因此它无法创建该类的对象,因此类不会被加载并放入列表中。
必须自己手工创建列表。所以最好就是把列表集中放在一个明显的地方:层次结构的基类
使用工厂方法设计模式将对象的创建推迟到类本身。
工厂方法以多态方式调用,创建适当类型的对象。
java.util.function.Supplier
用 T get()
描述了原型工厂方法。协变返回类型允许 get()
为 Supplier
的每个子类实现返回不同的类型。
在本例中,基类 Part
包含一个工厂对象的静态列表,列表成员类型为 Supplier
。对于应该由 get()
方法生成的类型的工厂,通过将它们添加到 prototypes
列表向基类“注册”。奇怪的是,这些工厂本身就是对象的实例。此列表中的每个对象都是用于创建其他对象的原型:
// typeinfo/RegisteredFactories.java
// 注册工厂到基础类
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
class Part implements Supplier<Part> {
@Override
public String toString() {
return getClass().getSimpleName();
}
static List<Supplier<? extends Part>> prototypes =
Arrays.asList(
new FuelFilter(),
new AirFilter(),
new CabinAirFilter(),
new OilFilter(),
new FanBelt(),
new PowerSteeringBelt(),
new GeneratorBelt()
);
private static Random rand = new Random(47);
public Part get() {
int n = rand.nextInt(prototypes.size());
return prototypes.get(n).get();
}
}
class Filter extends Part {}
class FuelFilter extends Filter {
@Override
public FuelFilter get() {
return new FuelFilter();
}
}
class AirFilter extends Filter {
@Override
public AirFilter get() {
return new AirFilter();
}
}
class CabinAirFilter extends Filter {
@Override
public CabinAirFilter get() {
return new CabinAirFilter();
}
}
class OilFilter extends Filter {
@Override
public OilFilter get() {
return new OilFilter();
}
}
class Belt extends Part {}
class FanBelt extends Belt {
@Override
public FanBelt get() {
return new FanBelt();
}
}
class GeneratorBelt extends Belt {
@Override
public GeneratorBelt get() {
return new GeneratorBelt();
}
}
class PowerSteeringBelt extends Belt {
@Override
public PowerSteeringBelt get() {
return new PowerSteeringBelt();
}
}
public class RegisteredFactories {
public static void main(String[] args) {
Stream.generate(new Part())
.limit(10)
.forEach(System.out::println);
}
}
并非层次结构中的所有类都应实例化;这里的 Filter
和 Belt
只是分类器,这样你就不会创建任何一个类的实例,而是只创建它们的子类(请注意,如果尝试这样做,你将获得 Part
基类的行为)。
因为 Part implements Supplier
,Part
通过其 get()
方法供应其他 Part
。如果为基类 Part
调用 get()
(或者如果 generate()
调用 get()
),它将创建随机特定的 Part
子类型,每个子类型最终都从 Part
继承,并重写相应的 get()
以生成它们中的一个。
在工厂方法模式中,抽象产品类Product负责定义产品的共性,实现对事物最抽象的定义;Creator为抽象创建类,也就是抽象工厂,具体如何创建产品类是由具体的实现工厂ConcreteCreator完成的。工厂方法模式的变种较多,我们来看一个比较实用的通用源码。
具体的产品类可以有多个,都继承于抽象产品类
该通用代码是一个比较实用、易扩展的框架,读者可以根据实际项目需要进行扩展
一个产品对象具体由哪一个产品生成是由工厂类决定的
在数据库开发中,大家应该能够深刻体会到工厂方法模式的好处:如果使用JDBC连接数据库,数据库从MySQL切换到Oracle,需要改动的地方就是切换一下驱动名称(前提条件是SQL语句是标准语句),其他的都不需要修改,这是工厂方法模式灵活性的一个直接案例。在所有需要生成对象的地方都可以使用,但是需要慎重地考虑是否要增加一个工厂类进行管理,增加代码的复杂度
万物皆对象,那万物也就皆产品类
例如需要设计一个连接邮件服务器的框架,有三种网络协议可供选择:POP3、IMAP、HTTP
我们就可以把这三种连接方法作为产品类,定义一个接口如IConnectMail
然后定义对邮件的操作方法
用不同的方法实现三个具体的产品类(也就是连接方式)
再定义一个工厂方法,按照不同的传入条件,选择不同的连接方式
如此设计,可以做到完美的扩展,如某些邮件服务器提供了WebService接口,很好,我们只要增加一个产品类就可以了
例如通过WebService与一个非Java的项目交互,虽然WebService号称是可以做到异构系统的同构化,但是在实际的开发中,还是会碰到很多问题,如类型问题、WSDL文件的支持问题,等等。从WSDL中产生的对象都认为是一个产品,然后由一个具体的工厂类进行管理,减少与外围系统的耦合。
例如,测试一个类A,就需要把与类A有关联关系的类B也同时产生出来,我们可以使用工厂方法模式把类B虚拟出来,避免类A与类B的耦合。目前由于JMock和EasyMock的诞生,该使用场景已经弱化了,读者可以在遇到此种情况时直接考虑使用JMock或EasyMock
工厂方法模式有很多扩展,而且与其他模式结合使用威力更大,下面将介绍4种扩展。
我们这样考虑一个问题:一个模块仅需要一个工厂类,没有必要把它产生出来,使用静态的方法就可以了,根据这一要求,我们把上例中的AbstarctHumanFactory
修改一下
我们在类图中去掉了AbstractHumanFactory
抽象类,同时把createHuman
方法设置为静态类型,简化了类的创建过程,变更的源码仅仅是HumanFactory和NvWa类
HumanFactory类仅有两个地方发生变化
createHuman
前增加static关键字工厂类发生变化,也同时引起了调用者NvWa的变化
运行结果没有发生变化,但是我们的类图变简单了,而且调用者也比较简单,该模式是工厂方法模式的弱化,因为简单,所以称为简单工厂模式
(Simple Factory Pattern),也叫做静态工厂模式
在实际项目中,采用该方法的案例还是比较多的
当我们在做一个比较复杂的项目时,经常会遇到初始化一个对象很耗费精力的情况,所有的产品类都放到一个工厂方法中进行初始化会使代码结构不清晰 例如,一个产品类有5个具体实现,每个实现类的初始化(不仅仅是new,初始化包括new一个对象,并对对象设置一定的初始值)方法都不相同,如果写在一个工厂方法中,势必会导致该方法巨大无比,那该怎么办?
考虑到需要结构清晰,我们就为每个产品定义一个创造者,然后由调用者自己去选择与哪个工厂方法关联 我们还是以女娲造人为例,每个人种都有一个固定的八卦炉,分别造出黑色人种、白色人种、黄色人种
抽象方法中已经不再需要传递相关参数了,因为每一个具体的工厂都已经非常明确自己的职责:创建自己负责的产品类对象。
三个具体的创建工厂都非常简单,但是,如果一个系统比较复杂时工厂类也会相应地变复杂。
运行结果还是相同 每一个产品类都对应了一个创建类,好处就是创建类的职责清晰,而且结构简单,但是给可扩展性和可维护性带来了一定的影响。为什么这么说呢?如果要扩展一个产品类,就需要建立一个相应的工厂类,这样就增加了扩展的难度。因为工厂类和产品类的数量相同,维护时需要考虑两个对象之间的关系。
当然,在复杂的应用中一般采用多工厂的方法,然后再增加一个协调类,避免调用者与各个子工厂交流,协调类的作用是封装子工厂类,对高层模块提供统一的访问接口。
单例模式的核心要求就是在内存中只有一个对象
,通过工厂方法模式也可以只在内存中生产一个对象
Singleton定义了一个private的无参构造函数,目的是不允许通过new的方式创建一个对象
Singleton保证不能通过正常的渠道建立一个对象
通过获得类构造器,然后设置private访问权限,生成一个对象,然后提供外部访问,保证内存中的对象唯一
以上通过工厂方法模式创建了一个单例对象,该框架可以继续扩展,在一个项目中可以产生一个单例构造器,所有需要产生单例的类都遵循一定的规则(构造方法是private),然后通过扩展该框架,只要输入一个类型就可以获得唯一的一个实例。
一个对象被消费完毕后,并不立刻释放,工厂类保持其初始状态,等待再次被使用 延迟初始化是工厂方法模式的一个扩展应用
ProductFactory负责产品类对象的创建工作,并且通过prMap变量产生一个缓存,对需要再次被重用的对象保留
通过定义一个Map容器,容纳所有产生的对象,如果在Map容器中已经有的对象,则直接取出返回;如果没有,则根据需要的类型产生一个对象并放入到Map容器中,以方便下次调用。
延迟加载框架是可以扩展的,例如限制某一个产品类的最大实例化数量,可以通过判断Map中已有的对象数量来实现,这样的处理是非常有意义的,例如JDBC连接数据库,都会要求设置一个MaxConnections最大连接数量,该数量就是内存中最大实例化的数量。
延迟加载还可以用在对象初始化比较复杂的情况下,例如硬件访问,涉及多方面的交互,则可以通过延迟加载降低对象的产生和销毁带来的复杂性。
工厂方法模式在项目中使用得非常频繁,以至于很多代码中都包含工厂方法模式 该模式几乎尽人皆知,但不是每个人都能用得好。熟能生巧,熟练掌握该模式,多思考工厂方法如何应用,而且工厂方法模式还可以与其他模式混合使用(例如模板方法模式、单例模式、原型模式等),变化出无穷的优秀设计,这也正是软件设计和开发的乐趣所在。 。