1. 工厂模式
在面向对象编程中,继承是一个基本概念,它与多态共同构成了类的父子关系。Phone对象可以被当做Product对象处理,Computer对象也可以被当做Product对象处理。一方面,这种抽象方式使得同一段代码能为Phone对象和Computer对象提供同样的处理操作,使代码更加简洁。另一方面,如果要扩展新的Product对象类型,比如Watch,不再需要修改代码,只需添加新的类即可。
- 在大多数情况下,最棘手的问题往往是对象的创建。在面向对象编程中,每个对象都使用特定的构造器进行实例化操作,如下:
Product product = new Phone();
- 这段代码说明了Product和Phone两个类之间的依赖关系。这样的依赖关系使代码紧密耦合,在不更改的情况下很难扩展。假设要用Computer替换Phone,就需要修改代码,如下:
Product product = new Computer();
- 这里存在两个问题:其一,类应该保持对扩展的开放对修改的关闭(开闭原则)。其二,每个类应该只有一个发生变化的原因(单一职责原则)。每增加新的类造成主要代码修改时会打破开闭原则,而主类除了其固有功能之外还负责实例化Product对象,这种行为将会打破单一职责原则。
在这种情况下就需要一种更好的设计方案,增加一个新类来负责实例化,称之为工厂模式。
2. 静态工厂模式
工厂模式用于实现逻辑的封装,并通过公共的接口提供对象的实例化服务,在添加新的类时只需要做少量的修改。
SimpleFactory类中包含实例化Phone和Computer的逻辑。当客户需要对象时,调用SimpleFactory的createProduct()方法,并提供参数指明所需要的对象类型。SimpleFactory实例化相应的具体产品并返回,返回的产品对象转换为基类型。因此,无论是Phone还是Computer,客户都能以相同的方式处理。
2.1 产品接口
- 新建接口,名称为Product,定义产品的唯一行为,获取产品名称getProductName(),代码如下:
public interface Product
{
/**
* 产品行为:获取产品名称
* @return 产品名称
*/
String getProductName();
}
2.2 具体产品
- 新建具体产品类,名称为Phone,并实现产品接口Product,代码如下:
public class Phone implements Product
{
/**
* 产品名称:手机
*/
public static final String PRODUCT_NAME = "Phone";
@Override
public String getProductName()
{
return PRODUCT_NAME;
}
}
- 同理新建第二个产品类,名称为Computer,内容如下:
public class Computer implements Product
{
/**
* 产品名称:电脑
*/
public static final String PRODUCT_NAME = "Computer";
@Override
public String getProductName()
{
return PRODUCT_NAME;
}
}
2.3 产品工厂
- 新建工厂类,名称为SimpleFactory,唯一职责就是根据需求生产对应的产品,内容如下:
public class SimpleFactory
{
public enum ProductType
{
PHONE, COMPUTER;
}
/**
* 职责:生产产品
* @param type 产品类型
* @return 具体产品
*/
public static Product createProduct(ProductType type)
{
switch (type)
{
// 生产手机产品
case PHONE:
return new Phone();
// 生产电脑产品
case COMPUTER:
return new Computer();
default:
return null;
}
}
}
2.4 客户端
- 新建客户端类,类名为Client,通过工厂生产产品,打印产品名称,内容如下:
public class Client
{
public static void main(String[] args)
{
// 生产手机产品并使用
Product phone = SimpleFactory.createProduct(SimpleFactory.ProductType.PHONE);
System.out.println(phone.getProductName());
// 生产电脑产品并使用
Product computer = SimpleFactory.createProduct(SimpleFactory.ProductType.COMPUTER);
System.out.println(computer.getProductName());
}
}
- 启动main()方法运行结果如下:
工厂类的逻辑非常简单,只负责Product类的实例化,符合单一职责原则。用户只调用Product接口,这样做可以减少耦合,符合依赖倒置原则。如果新增一种产品,那么就必须修改工厂类的生产逻辑,这样就打破了开闭原则,原因在于产品类型和具体产品存在对应关系,这种对应关系在工厂中进行了反映。为了更彻底的解耦,一般会通过配置文件来描述产品类型和具体产品类之间的这种对应关系,工厂读取配置映射内容,通过反射动态生产产品来达到解耦的目的。
但在某些情况下,反射并不适用。比如反射机制会降低程序的运行效率,在性能要求很高的场景下应该避免使用这种机制,设计不能过度。
3. 工厂方法模式
类比我们的现实生活,工厂其实也是千姿百态,不同的工厂生产不同的具体产品,汽车工厂生产各式各样的汽车,手机工厂生产各式各样的手机,工厂也需要扩展。工厂方法模式是在静态工厂模式上的改进,工厂类被抽象化,用于实例化特定产品类的代码被转移到实现具体抽象工厂方法的子类中,这样就可以扩展各式各样的工厂。
- 比如生活中的畜牧场,畜牧场有养马场和养牛场,养马场专门用来养马,养牛场专门用来养牛,结构如下:
- 抽象工厂(AnimalFarm):提供了创建产品的接口,调用者通过它访问具体的工厂方法来创建产品。
- 具体工厂(HorseFarm和CattleFarm):主要是实现抽象工厂,完成具体产品的创建。
- 抽象产品(Animal):定义了产品的规范,描述了产品的特征和行为。
- 具体产品(Horse和Cattle):实现抽象产品接口,由具体工厂来创建。
4. 抽象工厂模式
抽象工厂模式是静态工厂模式的扩展版本,它不再是创建单一类型的对象,而是创建一系列相关联的对象。如果说静态工厂模式中只包含一个抽象产品类,那么抽象工厂模式则包含多个抽象产品类。
华为和小米都有自己的手机和电脑两种产品的销售业务,但是他们的产品加工工艺都交给一个知名的第三方公司A来进行处理,A公司和华为、小米有了合作,建立了华为加工工厂和小米加工工厂,华为加工工厂既要加工华为手机也得加工华为电脑,华为加工工厂开始负责两类抽象产品的加工。
在抽象工厂中,每个抽象产品都有一个实例化方法,抽象工厂的体系更加庞大复杂。
三种工厂模式之间确实存在许多重叠的地方,它们并不存在明确的定义,某些专家在如何实施这些模式上也存在分歧。我们需要知道的就是,工厂模式的核心就是由工厂类来负责对象的创建,这就是工厂的单一职责。