工厂模式
工厂模式我们分为两个方面讨论,一个是工厂模式,另外一个是spring中的工厂模式应用。
工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式,这里着重理解下替代new操作,一般来说对象我们需要new Object()去实例化,如果一个对象需要在构造函数中实例化,在实例化之前需要处理一大堆操作和流程,那么在创建这个对象的时候需要写一大堆代码,这样我们的构造函数看起来就非常的臃肿了,这时候我们想到了工厂模式,我们将实例化的操作交给工厂对象去实现,这样我们即避免了对客户端暴露创建逻辑,又避免了将很多鸡蛋放在一个篮子里,尽量将长代码分成各个段,并将每一段再次封装起来,这样更加符合面相对象的开闭原则。
工厂模式分为 简单工厂 和 抽象工厂。
简单工厂
简单工厂中有三个角色(以下面代码为例):
工厂角色(Fatory): 这就是工厂模式的核心,也就是工厂类,负责创建所有产品对象的内部逻辑,他可以用static来修饰,然后外部主程序可以直接调用它内部的方法produce来创建所需要的产品对象,我们看到它的返回参数类型是Car。
抽象产品角色(Product): 它是工厂模式中所有创建的具体对象的父类,可以定义成接口,也可以用抽象类定义,看自己的需求而定。
具体产品角色(ConcreteProduct): 它是简单工厂模式创建的目标对象,可以创建N个继承于抽象角色对象的具体角色对象,每一个都需要实现抽象对象中的抽象方法,也可以有自己的内部特色实现。
直接看接口和实现:
第一步,先定义一个基础的抽象接口,用Product这个做基础的抽象接口,接口中有一个productName方法,可以让继承这个接口的派生类实际对象去实现。
package factory;
public interface Product {
void productName();
}
第二步,添加多个实现类
新建一个HaierFridge继承Product,并实现productName方法。
package factory;
public class HaierFridge implements Product {
@Override
public void productName() {
System.out.println("This is a Haier fridge");
}
}
新建一个MideaFridge继承Product,并实现productName方法。
package factory;
public class MideaFridge implements Product {
@Override
public void productName() {
System.out.println("This is a Midea fridge");
}
}
第三步,建一个工厂类SimpleFactory,用于实例化所需的各个对象,我们根据厂商名去实例化对应的产品。
package factory;
public class SimpleFactory {
public static Product produce(String manufacturer) {
if (manufacturer == null) {
return null;
}
System.out.println("Produce a fridge");
switch (manufacturer) {
case "Haier":
return new HaierFridge();
case "Midea":
return new MideaFridge();
default:
return null;
}
}
}
第四步,主程序测试。
package factory;
public class SimpleFactoryTest {
public static void main(String[] args) {
Product mideaFridge = SimpleFactory.produce("Midea");
mideaFridge.productName();
Product haierFridge = SimpleFactory.produce("Haier");
haierFridge.productName();
}
}
第五步,输出结果
Produce a fridge
This is a Midea fridge
Produce a fridge
This is a Haier fridge
总结,主程序SimpleFactoryTest只需要告诉工厂对象SimpleFactory它需要什么样类型的对象,SimpleFactory便生产什么样的对象,主程序并不关心这个对象的生产过程是什么样的,就像我们工作中的领导分配任务,只看最终结果,你们怎么去实现他Care,简单工厂看起来就是这样简明清晰,各司其职,分工明确,但是 缺点 是如果主程序想再加一个GreeFridge,那么这时就需要再新建一个类继承Product实现productName()方法,并在SimpleFactory的produce()方法中实例化GreeFridge这个对象,才能返回给主程序使用,这样的模式违背了开放-封闭原则,具体的产品和工厂之间的耦合度非常高,严重影响了系统的灵活性和扩展性,所以我们需要对简单工厂模式进行进一步抽象,下一步我们来看下抽象工厂
抽象工厂
OK,我们在简单工厂模式中看到抽象产品Product有不同的实现HaierFridge,MideaFridge,GreeFridge,那么对于工厂对象SimpleFactory我们可不可以也做一次抽象,让SimpleFactory继承自一个抽象对象IFactory,IFactory是所有这一类型Product生成商的父类,我们都知道冰箱有单门,双门,三门,四门冰箱,我们可以根据IFactory这个对象派生出SingleDoorFactory,TwoDoorFactory,ThreeDoorFactory等这几种ConcreteFactory对象,这样我们就可以根据不同的产品类型提供不同的工厂,将工厂类的实例化延伸到了子类,这就是所谓的工厂方法模式,由称为多态工厂模式。
####工厂方法模式
除了简单工厂模式中的有四种角色类型,产品角色类型和简单工厂模式中相同,剩下两个角色是抽象工厂和真实工厂。DP中是这么定义工厂方法模式的:
工厂方法模式定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
抽象工厂角色(IFactory): 抽象工厂是所有真实工厂类的父类,可以是接口,也可以是抽象类,所有真实角色都需要实现这个类中的抽象方法。
具体工厂角色(ConcreteFactory): 由抽象工厂派生出的子类,可以由客户端主程序调用返回一个具体的产品。
代码中,我在这里新增加几个真实产品角色,HaierSingleDoorFridge和MideaSingleDoorFridge用于让SingleDoorFactory生产这两个产品,而原有的HaierTwoDoorFridge和MideaTwoDoorFridge用TwoDoorFactory这个工厂生产。
第一步,增加产品角色HaierSingleDoorFridge和MideaSingleDoorFridge;HaierTwoDoorFridge和MideaTwoDoorFridge。
package factory;
public class HaierSingleDoorFridge implements Product {
@Override
public void productName() {
System.out.println("This is a Haier single door fridge");
}
}
package factory;
public class HaierTwoDoorFridge implements Product {
@Override
public void productName() {
System.out.println("This is a Haier single door fridge");
}
}
package factory;
public class MideaSingleDoorFridge implements Product {
@Override
public void productName() {
System.out.println("This is a Midea single door fridge");
}
}
package factory;
public class MideaTwoDoorFridge implements Product {
@Override
public void productName() {
System.out.println("This is a Midea single door fridge");
}
}
第二步,增加抽象工厂IFactory
package factory;
public interface IFactory {
Product produce(String manufacturer);
}
第三步,新建真实工厂SingleDoorFactory和TwoDoorFactory实现IFactory,并重写父类IFactory中的抽象方法。
package factory;
public class SingleDoorFactory implements IFactory {
@Override
public Product produce(String manufacturer) {
if (manufacturer == null) {
return null;
}
System.out.println("Produce a single door fridge");
switch (manufacturer) {
case "Haier":
return new HaierSingleDoorFridge();
case "Midea":
return new MideaSingleDoorFridge();
default:
return null;
}
}
}
package factory;
public class TwoDoorFactory implements IFactory {
@Override
public Product produce(String manufacturer) {
if (manufacturer == null) {
return null;
}
System.out.println("Produce a two doors fridge");
switch (manufacturer) {
case "Haier":
return new HaierTwoDoorFridge();
case "Midea":
return new MideaTwoDoorFridge();
default:
return null;
}
}
}
在实际使用中,真实工厂还需要处理一些产品的初始化,数据库连接等操作。
第四步,编写测试代码FactoryMethodTest。
package factory;
public class FactoryMethodTest {
public static void main(String[] args) {
SingleDoorFactory singleDoorFactory = new SingleDoorFactory();
Product mideaSingleDoorFridge = singleDoorFactory.produce("Midea");
mideaSingleDoorFridge.productName();
Product haierSingleDoorFridge = singleDoorFactory.produce("Haier");
haierSingleDoorFridge.productName();
TwoDoorFactory twoDoorFactory = new TwoDoorFactory();
Product mideaTwoDoorFridge = twoDoorFactory.produce("Midea");
mideaTwoDoorFridge.productName();
Product haierTwoDoorFridge = twoDoorFactory.produce("Haier");
haierTwoDoorFridge.productName();
}
}
看下输出:
Produce a single door fridge
This is a Midea single door fridge
Produce a single door fridge
This is a Haier single door fridge
-----------------------------
Produce a two doors fridge
This is a Midea single door fridge
Produce a two doors fridge
This is a Haier single door fridge
可以看到各个工厂各司其职,生产了各自的属性的产品,我们如果需要生产三门冰箱,那么我们只需要新增一个各种类型厂商的冰箱继承Product,譬如HaierThreeDoorFridge,MideaThreeDoorFridge,再添加一个工厂ThreeDoorFactory继承IFactory,ThreeDoorFactory实现IFactory的produce方法的初始化各个厂商的真实产品实例即可,这样的改动不会影响到另外两个工厂,极大的改善了简单工厂模式违背开闭原则的缺点。
工厂方法模式是对简单工厂模式进一步的解耦,由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点,因为在工厂方法模式中是一个子类对应一个工厂类,而这些工厂类都实现于一个抽象接口。这相当于是把原本会因为业务代码而庞大的简单工厂类,拆分成了一个个的工厂类,这样代码就不会都耦合在同一个类里了,就像把一个大蛋糕切成了多个小蛋糕。但工厂方法模式的缺点是每增加一个产品类,就需要增加一个对应的工厂类,增加了额外的开发量。
在工厂方法模式中我们可以看到具体的工厂生产具体的产品,每个工厂生产一种产品,工厂方法具有唯一性。但是有的时候我们对于一个工厂我们需要生产多种产品,譬如目前我们每一个国家的汽车工厂想要增加流水线生产电动汽车,这样的情况我们可以使用工厂方法模式解决这个问题,为每一种类型的产品增加一个基类抽象产品,再为每一类产品每一种产品分别添加一个工厂实现,天呐!这是一种非常冗余的写法,写到后面会怀疑人生的,所以在这里我们引入了抽象工厂模式。
###抽象工厂模式
抽象工厂模式就是为了解决这种每个工厂需要生产多种产品的情况产生的解决方案,在这里我们引入了产品族和产品等级结构的概念。
(1)产品等级结构
产品等级结构就是产品的继承结构,如抽象类是Fridge,其子类就是真实对象就有海尔冰箱HaierFridge,美的冰箱MideaFridge,格力冰箱GreeFridge等,抽象冰箱类和具体产品的子类之间构成了一个产品等级结构。
(2)产品族
产品族就是由同一个工厂生产的,所属于不同产品等级结构中的中的一组产品,譬如海尔工厂生产的海尔的冰箱,海尔的空调,海尔的洗衣机等,就是一个产品族
如下图,这个表格结构我们可以看的跟家清晰。
每一个工厂可以生产所属一个产品族中的所有产品,所生产的产品又所属于不同的产品等级结构。如图
DP中对抽象工厂模式的定义如下:
为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。
与工厂方法模式相比,抽象工厂模式中的工厂不只是创建一种商品,而是创建一组商品,可以理解为工厂集团化的表现,以前的工厂只生产冰箱,后面工厂发展好了,开始生产空调,洗衣机,热水器等等。
在抽象工厂模式中包含一下角色:
抽象产品角色(AbstractProduct):为每种产品声明接口,声明了产品所需要的业务方法。
具体产品角色(ConcreteProduct):定义了具体工厂生产的具体角色,实现抽象产品接口中的业务方法。
抽象工厂角色(AbstractFactory):声明了一组创建一类产品的方法,每一个方法对应一类产品。
具体工厂角色(ConcreteFactory):实现了抽象工厂角色中创建产品的方法,生成一组产品,这些产品构成一个产品族,每一个产品都位于一个产品等级结构中。
下面我们看下实例:
第一步,先创建抽象产品角色,定义两类产品接口(这里也可以是抽象对象)IFridge冰箱和IWasher洗衣机。
package factory.abstractFactory;
public interface IFridge {
void fridgeName();
void fidgeType();
}
package factory.abstractFactory;
public interface IWasher {
void washerName();
void washerType();
}
第二步,创建真实产品角色,用IFridge冰箱和IWasher洗衣机派生出HaierFridge和HaierWasher,MideaFridge和MideaWasher。
package factory.abstractFactory;
public class HaierFridge implements IFridge {
@Override
public void fridgeName() {
System.out.println("This is an Haier fridge");
}
@Override
public void fidgeType() {
System.out.println("This is a single-Door fridge");
}
}
package factory.abstractFactory;
public class HaierWasher implements IWasher {
@Override
public void washerName() {
System.out.println("This is an Haier washer");
}
@Override
public void washerType() {
System.out.println("This is an Platen washer");
}
}
package factory.abstractFactory;
public class MideaFridge implements IFridge {
@Override
public void fridgeName() {
System.out.println("This is a Midea fridge");
}
@Override
public void fidgeType() {
System.out.println("This is a single-Door fridge");
}
}
package factory.abstractFactory;
public class MideaWasher implements IWasher {
@Override
public void washerName() {
System.out.println("This is an Midea washer");
}
@Override
public void washerType() {
System.out.println("This is an Platen washer");
}
}
第三步,创建抽象工厂角色IFactory,定义两个方法produceFridge和produceWasher,分别用于生产IFridge类商品和IWasher类商品
package factory.abstractFactory;
public interface IFactory {
IFridge produceFridge();
IWasher produceWasher();
}
第四步,创建具体工厂角色HaierFactory和MideaFactory,实现抽象工厂角色IFactory的两个方法。
package factory.abstractFactory;
public class HaierFactory implements IFactory {
@Override
public IFridge produceFridge() {
System.out.println("Produce an Haier fridge");
return new HaierFridge();
}
@Override
public IWasher produceWasher() {
System.out.println("Produce an Haier washer");
return new HaierWasher();
}
}
package factory.abstractFactory;
public class MideaFactory implements IFactory {
@Override
public IFridge produceFridge() {
System.out.println("Produce an Midea fridge");
return new MideaFridge();
}
@Override
public IWasher produceWasher() {
System.out.println("Produce an Midea washer");
return new MideaWasher();
}
}
第五步,测试。
package factory.abstractFactory;
public class AbstractFactoryTest {
public static void main(String[] args) {
HaierFactory haierFactory = new HaierFactory();
IFridge haierFridge = haierFactory.produceFridge();
haierFridge.fridgeName();
IWasher haierWasher = haierFactory.produceWasher();
haierWasher.washerName();
MideaFactory mideaFactory = new MideaFactory();
IFridge mideaFridge = mideaFactory.produceFridge();
mideaFridge.fridgeName();
IWasher mideaWasher = mideaFactory.produceWasher();
mideaWasher.washerName();
}
}
第六步,看下输出。
Produce an Haier fridge
This is an Haier fridge
Produce an Haier washer
This is an Haier washer
Produce an Midea fridge
This is a Midea fridge
Produce an Midea washer
This is an Midea washer
总结,抽象工厂模式是工厂方法模式的一种延伸,它并不是完全符合开闭原则,因为,如果单纯的只增加新的产品族,只需要,增加新的产品和新的工厂实现就可以了,但是如果是需要增加新的产品等级结构,不仅需要修改抽象工厂类,还需要在所有的工厂角色实现抽象工厂中新加的方法,这就违背了开闭原。DP上解释这是一种倾斜式的实现。
工厂模式在spring中的应用
工厂模式的思想正好契合SpringIOC的设计思想: *** 某一接口的具体实现类的选择控制权从调用类中移除,转而交给第三方决定,*** 即借由Spring的Bean配置来实现控制,这同样也是工厂模式的思想。
在spring中有两个基本的工厂BeanFactory和ApplicationContext,都是通过抽象工厂模式做的实现,ApplicationContext继承了很多接口,其中有ListableBeanFactory和HierarchicalBeanFactory也是由BeanFactory派生而来。BeanFactory是Spring框架的基础设施,面向的是Spring本身,也就是用于创建Spring扩展的其他内容,在Spring代码中,BeanFactory只是个接口,并不是IOC容器的具体实现,但是Spring容器给出了很多种实现,如 DefaultListableBeanFactory、XmlBeanFactory等。ApplicationContext以一种更向面向框架的方式工作以及对上下文进行分层和实现继承,ApplicationContext包还提供了以下的功能:
• MessageSource, 提供国际化的消息访问
• 资源访问,如URL和文件
• 事件传播
• 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层;
FactoryBean也使用的是工厂模式,但是作用和BeanFactory完全不同,看下FactoryBean的代码:
package org.Springframework.beans.factory;
public interface FactoryBean<T> {
T getObject() throws Exception;
Class<?> getObjectType();
boolean isSingleton();
}
在该接口定义了以下3个方法。
T getObject():返回由FactoryBean创建的bean实例,如果isSingleton()返回true,则该实例会放到Spring容器中单实例缓存池中。
boolean isSingleton():返回由FactoryBean创建的bean实例的作用域是singleton还是prototype。
Class getObjectType():返回FactoryBean创建的bean类型。