在现实生活中社会分工越来越细,越来越专业化。
各种产品有专门的工厂生产,彻底告别了自给自足的小农经济时代,这大大缩短了产品的生产周期,提高了生产效率。同样,在软件开发中能否做到软件对象的生产和使用相分离呢?
能否在满足“开闭原则”的前提下,客户随意增删或改变对软件相关对象的使用呢?
工厂方法模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂方法模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
定义: 定义一个创建对象的接口,但让实现这个接口的类来决定要实例化那个类,工厂方法让类的实例化延迟到子类进行。
类型: 创建型
使用场景
创建对象需要大量重复的代码
客户端(应用层)不依赖与产品类实例如何被创建、实现细节
一个类通过其子类来指定创建那个对象
优点
用户只需要关心所需产品的对应工厂,无序关心创建细节
加入新的产品符合开闭原则,提高扩展性
缺点
类的个数容易过多,增加复杂度
增加了系统的抽象性和理解难度
分析
工厂方法中一个工厂只生产一种产品,这样可以保证了工厂的唯一性,这点是和抽象工厂最大的区别。例如:冰箱工厂只能生产冰箱不能生产其他产品。
我们可以通过提供一个或者一组重载方法来完成产品的定制。比如格力冰箱实现/继承冰箱方法抽象/接口类完成自己工厂的创建,美的冰箱同样也可以实现/继承冰箱抽象/接口类来完成自己工厂的创建。
由于他们都是实现了统一的冰箱接口和冰箱方法类,那么客户端(应用层)只需要使用冰箱接口接口来根据自己需求获取自己需要的产品,并不需要知道具体的创建过程和细节。这也是编程中面向接口编程的思想。
案例
进入夏天正是冰箱售卖的好时节,供应商小李想采购一批冰箱售卖。那么他就需要知道冰箱的品牌和价格,而且他有可能采购不同厂商的冰箱。冰箱工厂会定义好的商品来生成。
1. 定义产品即可和工厂接口**
/**
* 冰箱接口
*/
public interface IRefrigerator {
//获取品牌名
String getBrandName();
//获取价格
double getPrice();
}
/**
* 冰箱工厂接口
*/
public interface IRefrigeratorFactory {
IRefrigerator getIRefrigerator();
}
2. 产品实现类**
/**
* 海尔冰箱
*/
public class HaiErRefrigerator implements IRefrigerator {
@Override
public String getBrandName() {
return "海尔冰箱";
}
@Override
public double getPrice() {
return 5999;
}
}
/**
* 美的冰箱
*/
public class MeiDiRefrigerator implements IRefrigerator {
@Override
public String getBrandName() {
return "美的冰箱";
}
@Override
public double getPrice() {
return 2999;
}
}
/**
* 格力冰箱
*/
public class GeLiRefrigerator implements IRefrigerator {
@Override
public String getBrandName() {
return "格力冰箱";
}
@Override
public double getPrice() {
return 3999;
}
}
3. 工厂实现类
/**
* 海尔冰箱工厂
*/
public class HaiErRefrigeratorFactory implements IRefrigeratorFactory {
@Override
public IRefrigerator getIRefrigerator() {
return new HaiErRefrigerator();
}
}
/**
* 美的冰箱工厂
*/
public class MeiDiRefrigeratorFactory implements IRefrigeratorFactory {
@Override
public IRefrigerator getIRefrigerator() {
return new MeiDiRefrigerator();
}
}
/**
* 格力冰箱工厂
*/
public class GeLiRefrigeratorFactory implements IRefrigeratorFactory {
@Override
public IRefrigerator getIRefrigerator() {
return new GeLiRefrigerator();
}
}
4. 测试类
/**
* 测试类
*/
public class Test {
public static void main(String[] args) {
IRefrigeratorFactory refrigeratorFactory = new HaiErRefrigeratorFactory();
IRefrigeratorFactory refrigeratorFactory2 = new GeLiRefrigeratorFactory();
IRefrigeratorFactory refrigeratorFactory3 = new MeiDiRefrigeratorFactory();
IRefrigerator refrigerator = refrigeratorFactory.getIRefrigerator();
System.out.println("您购买了:" + refrigerator.getBrandName() + ",您需要支付:" + refrigerator.getPrice());
}
}
5. 日志信息
您购买了:海尔冰箱,您需要支付:5999.0
回到开头讲到的,来复盘一次
优缺点和适用环境
工厂方法模式的优点
1)用户只需要关心产品对应的工厂,甚至无需关心创建细节或具体产品类的类名
2)基于工厂角色和产品的多态性设计是工厂模式的关键。它能自主决定如何创建哪种产品对象,而创建细节都封装在具体工厂内部
3)在系统要添加新的产品时,无需修改抽象工厂和抽象产品提供的接口,无需修改客户端,也无需修改其他的具体工厂和具体产品,只要添加一个具体工厂和具体产品即可,从而提高系统的可扩展性(符合开闭原则)
工厂方法模式的缺点
1)在添加新产品时,要编写新的具体产品类,并要提供与之对应的具体工厂类。系统软件个数也成对增加,从而增加了系统的复杂度,带来更多开销
2)由于系统的可扩展性,在客户端中要引入抽象层进行定义,从而增加了系统的抽象性和理解难度
工厂方法模式的适用环境
1)客户端不需要知道它需要的对象的类。只需知道对应的工厂即可
2)抽象工厂类通过子类来指定创建那哪个对象。即抽象工厂类只需要提供一个创建产品的接口,而由其子类确定具体要创建的对象,利用多态性和里氏代换原则,子类对象将覆盖父类对象,从而使系统更容易扩展