设计模式(一)工厂模式
- September 2, 2021
你能get到的知识点
- 工厂模式的介绍
- 工厂模式通过代码的实现
作者:lomtom
个人网站:https://lomtom.cn 🔗
个人公众号:博思奥园 🔗
你的支持就是我最大的动力。
简介
创建者模式模式提供创建对象的机制, 能够提升已有代码的灵活性和可复⽤性。
创建者模式包括:工厂方法、抽象工厂、生成器、原型、单例,这5类。
这一节主要讲述工厂模式:
-
简单工厂模式
- 定义:定义一个工厂类,他可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类
- 核心就是用一个类来负责控制创建实例的过程。
- 适用场景:
- 工厂类负责创建对的对象比较少,因为不会造成工厂方法中的业务逻辑过于复杂
- 客户端只知道传入工厂类的参数,对如何创建对象不关心
-
工厂模式
- 定义:在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。
- 适用场景:
- 当你在编写代码的过程中, 如果无法预知对象确切类别及其依赖关系时, 可使用工厂方法。
- 如果你希望用户能扩展你软件库或框架的内部组件, 可使用工厂方法。
- 如果你希望复用现有对象来节省系统资源, 而不是每次都重新创建对象, 可使用工厂方法。
场景引入
初始场景
场景实例: 小葛参加Jd平台的抽奖活动,而此时只有一种奖品奖品是1000元购物卡一张。 为了更好的模拟,假设没有不抽中的情况
那么我们来模拟小葛参加抽奖的这个过程,代码如下
目录结构:
└─com
└─lomtom
└─demo_0_0
│ Test.java
│
└─service
AwardService.java
类 | 功能 |
---|---|
AwardService | 模拟发放1000元购物卡奖品 |
Test | 测试 |
实现发放奖品类
public class AwardService {
private String award = "1000元 购物卡";
public void getAward(String username){
System.out.println(username + "获得了" + award);
}
}
测试验证
public class Test {
public static void main(String[] args) {
int index = 0;
while(index++ < 10) {
String employee = "小葛";
System.out.print(employee + "抽奖兑换------ ");
AwardService service = new AwardService();
service.getAward(employee);
}
}
}
模拟十次抽奖,结果:
小葛抽奖兑换------ 小葛获得了1000元 购物卡
小葛抽奖兑换------ 小葛获得了1000元 购物卡
小葛抽奖兑换------ 小葛获得了1000元 购物卡
小葛抽奖兑换------ 小葛获得了1000元 购物卡
小葛抽奖兑换------ 小葛获得了1000元 购物卡
小葛抽奖兑换------ 小葛获得了1000元 购物卡
小葛抽奖兑换------ 小葛获得了1000元 购物卡
小葛抽奖兑换------ 小葛获得了1000元 购物卡
小葛抽奖兑换------ 小葛获得了1000元 购物卡
小葛抽奖兑换------ 小葛获得了1000元 购物卡
增加需求
场景实例: 小葛参加Jd平台的抽奖活动,平台为了增加奖品的多样性,在原来只有一种奖品的情况下新增两种奖品,分别为Iphone12 和3000元现金
如果代码其余部分与现有类已经存在耦合关系, 那么向程序中添加新类其实并没有那么容易。
下面我们不使用设计模式的情况下,我们一般还用if...else...
来实现需求,通过一个标志awardNumber
来表示小葛获得的奖品类型。
工程结构
└─com
└─lomtom
└─demo_0_1
│ Test.java
│
└─service
AwardService.java
类 | 功能 |
---|---|
AwardService | 模拟发放三种奖品 |
Test | 测试 |
实现发放奖品类
通过一个变量awardNumber
来表明小葛抽中哪一个奖品,并且发放相应的奖品。
public class AwardService {
private String cashAward = "3000元 现金";
private String mallCardAward = "Iphone 12";
private String iphoneAward = "1000元 购物卡";
public void getAward(String username,Integer awardNumber){
if (awardNumber == 1){
System.out.println(username + "获得了" + mallCardAward);
}else if (awardNumber == 2){
System.out.println(username + "获得了" + iphoneAward);
}else{
System.out.println(username + "获得了" + cashAward);
}
}
}
测试验证
public class Test {
public static void main(String[] args) {
int index = 0;
while(index++ < 10) {
String employee = "小葛";
//小葛随机获得奖品
Random random = new Random();
Integer awardNumber = random.nextInt(3);
System.out.print(employee + "抽奖兑换------ ");
AwardService service = new AwardService();
service.getAward(employee,awardNumber);
}
}
}
这里使用0 - 2的随机数来代表小葛抽中了具体哪种商品,测试结果
小葛抽奖兑换------ 小葛获得了1000元 购物卡
小葛抽奖兑换------ 小葛获得了3000元 现金
小葛抽奖兑换------ 小葛获得了Iphone 12
小葛抽奖兑换------ 小葛获得了Iphone 12
小葛抽奖兑换------ 小葛获得了3000元 现金
小葛抽奖兑换------ 小葛获得了3000元 现金
小葛抽奖兑换------ 小葛获得了3000元 现金
小葛抽奖兑换------ 小葛获得了1000元 购物卡
小葛抽奖兑换------ 小葛获得了3000元 现金
小葛抽奖兑换------ 小葛获得了1000元 购物卡
小结:
使用if...else...
非常直接的实现了业务需求,如果只从产品需求来说,使用if...else...
确实满足了需求,并且也能缩短上线的时间,但是这样简单粗暴的方式,在一定程度上给我们带来了便捷,但是也种下了一定的隐患。
- 当产品后续迭代时,需要引入更多的需求时,代码将变得臃肿;
- 并且也在一定程度上提高的重构代码的难度与成本;
- 一旦出现问题,测试所花的时间也会相应的增加。
那么,问题来了,我们怎么使用工厂模式来进行优化呢?
运用到简单工厂模式
使用简单工厂模式步骤:
-
创建抽象产品接口
-
创建具体产品类
-
创建工厂类
-
客户端调用工厂类
场景实例: 小葛参加Jd平台的抽奖活动,平台为了增加奖品的多样性,在原来只有一种奖品的情况下新增两种奖品,分别为Iphone12 和3000元现金
对上面使用if...else...
的代码进行抽取,将其每个奖品发放的功能相应的抽取出来,然后使用一个工厂来决定发放哪一个奖品。
工程结构
└─com
└─lomtom
└─demo_0_2
│ Test.java
│
├─factory
│ AwardFactory.java
│
└─service
│ AwardService.java
│
└─impl
CashAwardService.java
IphoneAwardService.java
MallCardAwardService.java
我们创建一个发放奖品的接口(AwardService
)和发放奖品接口的实现类,并且使用AwardFactory
来获取AwardService
对象,从而实现奖品的发放。
类 | 功能 |
---|---|
AwardService | 奖品方法接口 |
CashAwardService | 模拟发放现金 |
IphoneAwardService | 模拟发放手机 |
MallCardAwardService | 模拟发放购物卡 |
AwardFactory | 发放奖品工厂 |
Test | 测试 |
实现发放奖品接口与其实现类 首先创建发放奖品的接口。 其次创建具体的发放奖品的实现类。
1. 发放奖品接口
public interface AwardService {
void getAward(String username);
}
2. 发放手机实现类
public class IphoneAwardService implements AwardService {
private String award = "Iphone 12";
@Override
public void getAward(String username){
System.out.println(username + "获得了" + award);
}
}
3. 发放现金实现类
public class CashAwardService implements AwardService {
private String award = "3000元 现金";
@Override
public void getAward(String username){
System.out.println(username + "获得了" + award);
}
}
4. 发放购物卡实现类
public class MallCardAwardService implements AwardService {
private String award = "1000元 购物卡";
@Override
public void getAwardService(String username){
System.out.println(username + "获得了" + award);
}
}
工厂类
使用一个变量awardNumber
来代表小葛是抽中了哪一个奖品,然后来决定具体实现哪一个实现类。
public class AwardFactory {
public AwardService getAward(Integer awardNumber) {
if (awardNumber == 1){
return new MallCardAwardService();
}else if (awardNumber == 2){
return new IphoneAwardService();
}else{
return new CashAwardService();
}
}
}
测试验证 用test来模拟客户端发起请求。
public class Test {
public static void main(String[] args) {
int index = 0;
while(index++ < 10) {
String employee = "小葛";
//小葛随机获得奖品
Random random = new Random();
Integer awardNumber = random.nextInt(3);
System.out.print(employee + "抽奖兑换------ ");
AwardFactory factory = new AwardFactory();
AwardService service = factory.getAwardService(awardNumber);
service.getAward(employee);
}
}
}
结果:
小葛抽奖兑换------ 小葛获得了Iphone 12
小葛抽奖兑换------ 小葛获得了Iphone 12
小葛抽奖兑换------ 小葛获得了1000元 购物卡
小葛抽奖兑换------ 小葛获得了3000元 现金
小葛抽奖兑换------ 小葛获得了1000元 购物卡
小葛抽奖兑换------ 小葛获得了1000元 购物卡
小葛抽奖兑换------ 小葛获得了3000元 现金
小葛抽奖兑换------ 小葛获得了3000元 现金
小葛抽奖兑换------ 小葛获得了3000元 现金
小葛抽奖兑换------ 小葛获得了Iphone 12
优点:简单工厂模式分离产品的创建者和消费者,有利于软件系统结构的优化,消费者的逻辑较为复杂时,这样的代码风格能够让我们对其功能一目了然;
缺点:但是由于一切逻辑都集中在一个工厂类中,导致了没有很高的内聚性,同时也违背了“开放封闭原则
”。也就是说我需要增加一个其他的奖品,则需要修改工厂类,重新进行编译。
运用到工厂模式
使用简单工厂模式步骤:
- 创建抽象产品接口
- 创建具体产品类实现产品接口
- 创建工厂(具体创建者)接口
- 创建具体产品工厂类实现该工厂接口
- 创建工厂类(抽象创建者,可以说是一个调度中心)
- 客户端调用工厂类
小疑问?具体创建者与抽象创建者是什么东西?
在本实例中,因为这里有两种类型的工厂,AwardFactory
来实现具体需要创建的产品(来发放需要发放的产品),所以这个工厂叫做具体的创建者,而FactoryFactory
并没有参与到产品的创建,而是将产品的创建延迟到AwardFactory
来进行创建,所以FactoryFactory
被称为抽象的创建者。
场景实例: 小葛参加Jd平台的抽奖活动,平台为了增加奖品的多样性,在原来只有一种奖品的情况下新增两种奖品,分别为Iphone12 和3000元现金
工程结构
└─com
└─lomtom
└─demo_0_3
│ Test.java
│
├─factory
│ │ AwardFactory.java
│ │ FactoryFactory.java
│ │
│ └─impl
│ CashAwardFactory.java
│ IphoneAwardFactory.java
│ MallCardAwardFactory.java
│
└─service
│ AwardService.java
│
└─impl
CashAwardService.java
IphoneAwardService.java
MallCardAwardService.java
首先第一步: 让所有产品都遵循同一接口。
1.声明一个发放奖品的接口
public interface AwardService {
void getAward(String username);
}
2.编写具体发放奖品的实现类并且实现发放奖品接口
public class CashAwardService implements AwardService {
private String award = "3000元 现金";
@Override
public void getAward(String username){
System.out.println(username + "获得了" + award);
}
}
public class IphoneAwardService implements AwardService {
private String award = "Iphone 12";
@Override
public void getAward(String username){
System.out.println(username + "获得了" + award);
}
}
public class MallCardAwardService implements AwardService {
private String award = "1000元 购物卡";
@Override
public void getAward(String username){
System.out.println(username + "获得了" + award);
}
}
其次,创建工厂接口(具体创建者),然后创建具体产品的工厂类实现工厂接口(一个工厂只负责生产一个产品,做到专职)
1.创建奖品发放工厂接口
public interface AwardFactory {
AwardService getAwardService(Integer awardNumber);
}
2.创建不同的奖品发放工厂,并且实现奖品发放工厂
public class CashAwardFactory implements AwardFactory {
@Override
public AwardService getAwardService(Integer awardNumber) {
return new CashAwardService();
}
}
public class IphoneAwardFactory implements AwardFactory {
@Override
public AwardService getAwardService(Integer awardNumber) {
return new IphoneAwardService();
}
}
public class MallCardAwardFactory implements AwardFactory {
@Override
public AwardService getAwardService(Integer awardNumber) {
return new MallCardAwardService();
}
}
最后,创建抽象创建者工厂,工厂方法中添加临时参数来控制返回的需要发放奖品的类型。
public class FactoryFactory {
public static AwardFactory createFactory(Integer awardNumber) {
AwardFactory factory;
if (awardNumber == 1){
factory = new MallCardAwardFactory();
}else if (awardNumber == 2){
factory = new IphoneAwardFactory();
}else{
factory = new CashAwardFactory();
}
return factory;
}
}
测试验证
public class Test {
public static void main(String[] args) {
int index = 0;
while(index++ < 10) {
String employee = "小葛";
//小葛随机获得奖品
Random random = new Random();
Integer awardNumber = random.nextInt(3);
System.out.print(employee + "抽奖兑换------ ");
AwardFactory factory = FactoryFactory.createFactory(awardNumber);
AwardService service = factory.getAwardService(awardNumber);
service.getAward(employee);
}
}
}
结果
小葛抽奖兑换------ 小葛获得了1000元 购物卡
小葛抽奖兑换------ 小葛获得了1000元 购物卡
小葛抽奖兑换------ 小葛获得了1000元 购物卡
小葛抽奖兑换------ 小葛获得了1000元 购物卡
小葛抽奖兑换------ 小葛获得了Iphone 12
小葛抽奖兑换------ 小葛获得了1000元 购物卡
小葛抽奖兑换------ 小葛获得了Iphone 12
小葛抽奖兑换------ 小葛获得了3000元 现金
小葛抽奖兑换------ 小葛获得了1000元 购物卡
小葛抽奖兑换------ 小葛获得了1000元 购物卡
从结果中我们可以看出,我们使用工厂模式同样到达了预期效果,相对于最开始使用if...else...
来实现,
- 其更满足设计模式中的
单一职责原则
与开闭原则
,我们每一种奖品的发放都由相应的类来进行控制,以及在后续进行扩展的时候,我们能够更加方便的进行扩展。 - 其代码可以在一定程度上避免创建者与产品的解耦
当然,面对非常复杂的业务,仅仅使用工厂模式远远是不够的,还需要配合其他的设计模式来进行使用。而对于特别简单的业务,使用工厂模式往往会增加系统的复杂性,具体使用场景还是根据当前的业务来。
工厂模式在源码的应用:
- jdk 的Integer的valueOf方法(静态工厂方法)
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
JDK在1.5中添加的一项新特性,把-128~127的数字缓存起来了。所以这个范围内的自动装箱的数字都会从缓存中获取,返回同一个数字。这样的好处就是提升性能和节省内存。
- Mybatis数据源 🔗(工厂 + 代理)
- Dubbo - Registry提供服务的注册 🔗 (工厂 + 模板)