工厂模式
介绍
作用
实现了创建者和调用者的分离
OPP七大原则
- 开闭原则:一个软件的实体应当对扩展开放,对修改关闭
- 依赖倒转原则:要针对接口编程,不要针对实现编程
- 迪米特法则:只要与你直接的朋友通信,而避免和陌生人通信
核心本质
- 实例化对象不使用
new
,而用工厂方法代替 - 将选择实现类,创建对象统一管理和控制,从而将调用者跟我们的实现类分离
三种模式
- 简单工厂模式:用来生产同一等级结构中的任意产品(对于增加新的产品,需要扩展已有代码)
- 工厂方法模式:用来生产统一等级结构中的固定产品(支持增加任意产品)
- 抽象工厂模式:围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。
应用场景
- JDK中Calendar的
getInstance()
方法 - JDBC中的Connection对象的获取
- Spring中IOC容器创建管理bean对象
- 反射中Class对象的
newInstance()
方法
简单(静态)工厂模式
假设有一个消费者买车场景,定义一个Car接口:
package simple;
public interface Car {
void name();
}
然后分别由五零宏光和特斯拉两款车:
package simple;
public class WuLing implements Car {
@Override
public void name() {
System.out.println("五菱宏光");
}
}
package simple;
public class Tesla implements Car {
@Override
public void name() {
System.out.println("特斯拉");
}
}
普通情况下,消费者买车要用 new 去实例化某款车的类,再调用车的name()
方法:
package simple;
public class Consumer {
public static void main(String[] args) {
// 接口,所有的实现类!
Car car = new WuLing();
Car car2 = new Tesla();
car.name();
car2.name();
}
}
而使用简单工厂模式下,定义一个 CarFactory 类,去代替 Consumer 去执行 new
这个操作:
package simple;
// 静态工厂模式
//增加一个新的产品,如果不修改代码,做不到
// 开闭原则
public class CarFactory {
// 方法一
public static Car getCar(String car) {
if (car.equals("五菱")) {
return new WuLing();
} else if (car.equals("特斯拉")) {
return new Tesla();
} else {
return null;
}
}
// 方法二
public static Car getWuLing(){
return new WuLing();
}
public static Car getTesla(){
return new Tesla();
}
}
此时,Consumer 只需调用 CarFactory 里面的方法即可得到对应 Car 的实例:
package simple;
public class Consumer {
public static void main(String[] args) {
// 接口,所有的实现类!
// Car car = new WuLing();
// Car car2 = new Tesla();
// 使用工厂
// 方法一
Car car = CarFactory.getCar("五菱");
Car car2 = CarFactory.getCar("特斯拉");
// 方法二
Car car = CarFactory.getWuLing();
Car car2 = CarFactory.getTesla();
car.name();
car2.name();
}
}
缺点:当我们想要增加一个Car的实现类的时候,还需要去CarFactory去增加里面的代码。
工厂方法模式
接着上面的Consumer买车的例子,现在想增加一个大众品牌的车,为了避免简单工厂模式需要去修改CarFactory,这里使用工厂方法。
这里把CarFactory也定义为接口:
package method;
public interface CarFactory {
Car getCar();
}
各自品牌的Factory去实现这个CarFactory接口,如TeslaFactory:
package method;
public class TeslaFactory implements CarFactory{
@Override
public Car getCar() {
return new Tesla();
}
}
其他车的Factory也一样,这时候Consumer通过对应品牌车的Factory去获取各品牌车的实例:
package method;
public class Consumer {
public static void main(String[] args) {
Car car = new WuLingFactory().getCar();
Car car2 = new TeslaFactory().getCar();
car.name();
car2.name();
}
}
加入我们想加入大众品牌车,就只需要去新建一个DaZhongFactory类去实现CarFactory这个接口,无需去修改CarFactory里面的代码,从而遵循了开闭原则。
抽象工厂模式
定义:抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定它们的类
适用场景:
- 客户端(应用层)不依赖于产品实例如何被创建、实现等细节
- 强调一系列相关产品对象(属于统一产品族)一起使用创建对象需要大量的重复代码
- 提供一个产品类的库,所有的产品以供养的接口出现,从而使得客户端不依赖于具体的实现
优点:
- 具体产品在应用层的代码隔离,无需关心创建的细节
- 将一个系列的产品统一到一起创建
缺点:
- 规定了所有可能被创建的产品集合,产品族中扩展新的产品困难
- 增加了系统抽象性和理解难度
现在定义手机和路由器的产品接口:
package abstract1;
// 手机产品接口
public interface IPhoneProduct {
void start();
void shutdown();
void callup();
void sendSMS();
}
package abstract1;
// 路由器产品接口
public interface IRouterProduct {
void start();
void shutdown();
void openwifi();
void setting();
}
然后又小米对应的手机和路由器的实现类:(华为同理)
package abstract1;
// 小米手机
public class XiaomiPhone implements IPhoneProduct {
@Override
public void start() {
System.out.println("开启小米手机");
}
@Override
public void shutdown() {
System.out.println("关闭小米手机");
}
@Override
public void callup() {
System.out.println("小米打电话");
}
@Override
public void sendSMS() {
System.out.println("小米发短信");
}
}
package abstract1;
// 小米路由器
public class XiaomiRouter implements IRouterProduct{
@Override
public void start() {
System.out.println("启动小米路由器");
}
@Override
public void shutdown() {
System.out.println("关闭小米路由器");
}
@Override
public void openwifi() {
System.out.println("打开小米wifi");
}
@Override
public void setting() {
System.out.println("小米设置");
}
}
再定义一个产品工厂接口,里面由手机和路由器两款产品:
package abstract1;
// 抽象产品工厂
public interface IProductFactory {
// 生成手机
IPhoneProduct phoneProduct();
IRouterProduct routerProduct();
}
然后就是小米的工厂:(华为同理)
package abstract1;
public class XiaomiFactory implements IProductFactory{
@Override
public IPhoneProduct phoneProduct() {
return new XiaomiPhone();
}
@Override
public IRouterProduct routerProduct() {
return new XiaomiRouter();
}
}
最后客户端像获取某品牌的产品对象,只需要去对应品牌的工厂那里使用相应的函数即可:
package abstract1;
public class Client {
public static void main(String[] args) {
System.out.println("==========小米系列产品==============");
// 小米工厂
XiaomiFactory xiaomiFactory = new XiaomiFactory();
IPhoneProduct xiaomiPhoneProduct = xiaomiFactory.phoneProduct();
xiaomiPhoneProduct.callup();
xiaomiPhoneProduct.sendSMS();
IRouterProduct xiaomiRouterProduct = xiaomiFactory.routerProduct();
xiaomiRouterProduct.openwifi();
xiaomiRouterProduct.setting();
System.out.println("==========华为系列产品==============");
// 华为工厂
HuaweiFactory huaweiFactory = new HuaweiFactory();
IPhoneProduct huaweiPhoneProduct = huaweiFactory.phoneProduct();
huaweiPhoneProduct.callup();
huaweiPhoneProduct.sendSMS();
IRouterProduct huaweiRouterProduct = huaweiFactory.routerProduct();
huaweiRouterProduct.openwifi();
huaweiRouterProduct.setting();
}
}
当需要新增一个产品族的时候需要不同的产品实现类、产品工厂接口和不同品牌工厂实现类都进行修改,十分麻烦,因此抽线工厂模式适用于产品族稳定的情形。
总结
简单工厂模式:虽然某种程度上不符合设计原则,但实际使用最多
工厂方法模式:不修改已有类的前提下,通过增加新的工厂类实现扩展
抽象工厂模式:不可以增加产品,可以增加产品族