工厂模式(复习笔记 2021.6.11)
前言:
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
**意图:**定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
**主要解决:**主要解决接口选择的问题。
**何时使用:**我们明确地计划不同条件下创建不同实例时。
**如何解决:**让其子类实现工厂接口,返回的也是一个抽象的产品。
**关键代码:**创建过程在其子类执行。
应用实例: 1、您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。 2、Hibernate 换数据库只需换方言和驱动就可以。
优点: 1、一个调用者想创建一个对象,只要知道其名称就可以了。 2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 3、屏蔽产品的具体实现,调用者只关心产品的接口。
**缺点:**每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
使用场景: 1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。 2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。 3、设计一个连接服务器的框架,需要三个协议,“POP3”、“IMAP”、“HTTP”,可以把这三个作为产品类,共同实现一个接口。
**注意事项:**作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
工厂模式分为3种:
1: 简单工厂模式 (根据方法参数, 生产出不同的实例)
2: 方法工厂模式 (简单工厂的升级版, 为每一种实例构建不同的工厂, 对应工厂只负责生产对应实例)
3: 抽象工厂模式 (一个工厂, 不单单只能生产一种实例)
前提条件:
我们开了一家油漆工厂, 店内在售卖一种品牌油漆。
油漆(涂料) 是一个接口, 里面指定了涂料的保质期、品牌、颜色
public interface Coating {
LocalDateTime shelfLife();
String brand();
String color();
}
假设根据用户需要的清单, 获取不同颜色的油漆, 普通的面向实现方式:
@Test
public void applicationTest() throws Exception {
Coating coating = null;
String color = "green";
if ("red".equals(color)){
coating = new RedCoating();
}else if ("green".equals(color)){
coating = new GreenCoating();
}
System.out.println("油漆品牌:"+coating.brand());
System.out.println("油漆保质期:"+coating.shelfLife());
System.out.println("油漆颜色:"+coating.color());
}
//-----------
油漆品牌:嘉宝莉
油漆保质期:2021-06-12T00:00
油漆颜色:绿色
简单工厂:
下面利用简单工厂进行解耦, 不在由我们进行手动创建(new), 改为被动的从工厂获取实例对象
1.1 定义一个简单工厂
/**
* @Author: ZhiHao
* @Date: 2021/6/8 16:47
* @Description: 简单工厂
* @Versions 1.0
**/
public class SimpleFactory {
private String color;
public SimpleFactory(String color) {
this.color = color;
}
/**
* 判断逻辑从业务方, 移到了工厂
*
* @return com.zhihao.interfaces.Coating
* @author: ZhiHao
* @date: 2021/6/8
*/
public Coating createCoating() {
Coating coating = null;
if ("red".equals(color)) {
coating = new RedCoating("嘉宝莉");
} else if ("green".equals(color)) {
coating = new GreenCoating("嘉宝莉");
}
return coating;
}
}
1.2 业务方
改为被动的获取实例对象
@Test
public void applicationTest() throws Exception {
String color = "red";
SimpleFactory simpleFactory = new SimpleFactory(color);
Coating coating = simpleFactory.createEntity();
System.out.println("油漆品牌:"+coating.brand());
System.out.println("油漆保质期:"+coating.shelfLife());
System.out.println("油漆颜色:"+coating.color());
}
//
油漆品牌:嘉宝莉
油漆保质期:2021-06-08T18:48:11.226
油漆颜色:红色
这就是最简单的工厂模式, 根据参数生产实例对象。
后面我们的工厂想扩展做大做强, 开分厂, 不单单只想生产一种嘉宝莉油漆了, 想扩展生产多2种不同品牌的油漆(三棵树, 嘉实多)。
如果这个时候用户想买5桶嘉宝莉与5桶三棵树, 还使用到简单工厂的话, 则需要增加一个创建涂料的参数了, 需要生产什么品牌与什么颜色的油漆了, 下面使用工厂方法进行职责划分更为清晰的模式进行解决!
工厂方法
工厂方法, 是简单工厂的一种升级, 使职责划分更为清晰, 什么工厂生产什么实例对象。 耦合更低
这种情况就需要抽象一个大家都遵守的工厂规则(类似规章制度, 只能生产油漆), 然后允许分厂做决定生产那个牌子。
1.1 定义一个抽象基类工厂
/**
* @Author: ZhiHao
* @Date: 2021/6/9 17:02
* @Description: 需要大家都遵守规则的基础(总厂)
* @Versions 1.0
**/
public abstract class BaseFactory {
/**
* 生产油漆
*
* @return com.zhihao.interfaces.Coating
* @author: ZhiHao
* @date: 2021/6/9
*/
public abstract Coating createCoating();
}
1.2 定义一个专门生产嘉宝莉工厂
/**
* @Author: ZhiHao
* @Date: 2021/6/9 17:09
* @Description: 嘉宝莉工厂
* @Versions 1.0
**/
public class CarpolyFactory extends BaseFactory {
private String color;
public CarpolyFactory() {
}
public CarpolyFactory(String color) {
this.color = color;
}
@Override
public Coating createCoating() {
Coating coating = null;
if ("red".equals(color)) {
coating = new RedCoating("嘉宝莉");
} else if ("green".equals(color)) {
coating = new GreenCoating("嘉宝莉");
}
return coating;
}
}
1.3 定义一个专门生产三棵树工厂
/**
* @Author: ZhiHao
* @Date: 2021/6/9 17:09
* @Description: 三棵树工厂
* @Versions 1.0
**/
public class ThreeTreesFactory extends BaseFactory {
private String color;
public ThreeTreesFactory() {
}
public ThreeTreesFactory(String color) {
this.color = color;
}
@Override
public Coating createCoating() {
Coating coating = null;
if ("red".equals(color)) {
coating = new RedCoating("三棵树");
} else if ("green".equals(color)) {
coating = new GreenCoating("三棵树");
}
return coating;
}
}
1.4 业务方
用户想买5桶嘉宝莉与5桶三棵树的绿色油漆
@Test
public void applicationTest() throws Exception {
String color = "red";
// 先创建出2个工厂
BaseFactory carpolyFactory = new CarpolyFactory(color);
BaseFactory treesFactory = new ThreeTreesFactory(color);
// 生产不同品牌
Coating carpolyCoating = carpolyFactory.createCoating();
Coating treesCoating = treesFactory.createCoating();
System.out.println("油漆品牌:"+carpolyCoating.brand());
System.out.println("油漆保质期:"+carpolyCoating.shelfLife());
System.out.println("油漆颜色:"+carpolyCoating.color());
System.out.println("油漆品牌:"+treesCoating.brand());
System.out.println("油漆保质期:"+treesCoating.shelfLife());
System.out.println("油漆颜色:"+treesCoating.color());
}
//------------
油漆品牌:嘉宝莉
油漆保质期:2021-06-09T17:37:20.998
油漆颜色:红色
油漆品牌:三棵树
油漆保质期:2021-06-09T17:37:20.998
油漆颜色:红色
正式定义:
工厂方法模式定义了一个创建对象的接口, 但由子类决定要实例化的类是哪一个, 工厂方法让类把实例化推迟到子类。
如同在正式定义中所说的, 常常听到其他开发人员说: 工厂方法让子类决定要实例化的类是哪一个。希望不要理解错误, 所谓的“决定”, 并不是指模式允许子类本身在运行时做决定, 而是指在编写创建者类时, 不需要知道实际创建的产品是哪一个。选择了使用哪个子类,自然就决定了实际创建的产品是什么。
后面我们的工厂想进行扩展业务, 不仅仅出售生产油漆了, 想进行根据不同油漆捆绑销售油漆刷子、腻子、手套、等等相关产品。
抽象工厂
不仅仅可以生产一种产品, 可以生产一组产品, 允许客户创建对象的家族, 而无需指定他们的具体类
1.1 定义一个抽象基工厂接口
/**
* @Author: ZhiHao
* @Date: 2021/6/11 18:47
* @Description: 抽象工厂接口
* @Versions 1.0
**/
public interface class AbstractBaseFactory {
/**
* 获取刷子附属产品
*
* @param name
* @return com.zhihao.entity.AffiliatedProduct
* @author: ZhiHao
* @date: 2021/6/11
*/
AffiliatedProduct getBrush(String name);
/**
* 获取手套附属产品
*
* @param name
* @return com.zhihao.entity.AffiliatedProduct
* @author: ZhiHao
* @date: 2021/6/11
*/
AffiliatedProduct getGlove(String name);
}
//----------AffiliatedProduct接口, Brush、Glove都实现了该接口-------
public interface AffiliatedProduct {String affiliatedProductName();}
1.2 定义一个专门生产嘉宝莉附属产品工厂
/**
* @Author: ZhiHao
* @Date: 2021/6/11 19:05
* @Description: 嘉宝莉附属产品工厂
* @Versions 1.0
**/
public class CarpolyAffiliatedFactory implements AbstractBaseFactory {
@Override
public Brush getBrush(String name) {
return new Brush("嘉宝莉"+name);
}
@Override
public Glove getGlove(String name) {
return new Glove("嘉宝莉"+name);
}
}
1.3 定义一个专门生产三棵树附属产品工厂
/**
* @Author: ZhiHao
* @Date: 2021/6/11 19:05
* @Description: 三棵树附属产品工厂
* @Versions 1.0
**/
public class ThreeTreesAffiliatedFactory implements AbstractBaseFactory {
@Override
public Brush getBrush(String name) {
return new Brush("三棵树"+name);
}
@Override
public Glove getGlove(String name) {
return new Glove("三棵树"+name);
}
}
1.4 改造Coating
油漆接口与其实现实体
public interface Coating {
LocalDateTime shelfLife();
String brand();
String color();
// 增加设置与返回附属产品
AffiliatedProduct[] getAffiliatedProducts();
AffiliatedProduct[] setAffiliatedProducts(AffiliatedProduct[] affiliatedProducts);
}
// 修改其2个油漆实体, 增加成员参数, 以下都是增加的参数, 其他参数省略不写
public class RedCoating implements Coating{
private AffiliatedProduct[] affiliatedProducts;
@Override
public AffiliatedProduct[] getAffiliatedProducts() {
return affiliatedProducts;
}
@Override
public AffiliatedProduct[] setAffiliatedProducts(AffiliatedProduct[] affiliatedProducts) {
return this.affiliatedProducts = affiliatedProducts;
}
}
// GreenCoating 实体也和RedCoating 一模一样
1.5 改造工厂方法中createCoating
方法
public class ThreeTreesFactory extends BaseFactory {
private String color;
private AbstractBaseFactory baseFactory;
public ThreeTreesFactory(String color) {
this.color = color;
this.baseFactory = new ThreeTreesAffiliatedFactory();
}
@Override
public Coating createCoating() {
Coating coating = null;
if ("red".equals(color)) {
coating = new RedCoating("三棵树");
coating.setAffiliatedProducts(new AffiliatedProduct[]{baseFactory.getBrush("红色油漆捆绑销售刷子")
,baseFactory.getGlove("红色油漆捆绑销售手套")});
} else if ("green".equals(color)) {
coating = new GreenCoating("三棵树");
coating.setAffiliatedProducts(new AffiliatedProduct[]{baseFactory.getBrush("绿色油漆捆绑销售刷子")
,baseFactory.getGlove("绿色油漆捆绑销售手套")});
}
return coating;
}
}
// 嘉宝莉工厂代码也是几乎一模一样
1.6 业务方
@Test
public void applicationTest() throws Exception {
String color = "red";
// 先创建出2个工厂
BaseFactory carpolyFactory = new CarpolyFactory(color);
BaseFactory treesFactory = new ThreeTreesFactory(color);
// 生产不同品牌
Coating carpolyCoating = carpolyFactory.createCoating();
Coating treesCoating = treesFactory.createCoating();
System.out.println("油漆品牌:"+carpolyCoating.brand());
System.out.println("油漆保质期:"+carpolyCoating.shelfLife());
System.out.println("油漆颜色:"+carpolyCoating.color());
Arrays.stream(carpolyCoating.getAffiliatedProducts()).forEach((affiliatedProduct)->
System.out.println(affiliatedProduct.affiliatedProductName()));
System.out.println("-----------------------------");
System.out.println("油漆品牌:"+treesCoating.brand());
System.out.println("油漆保质期:"+treesCoating.shelfLife());
System.out.println("油漆颜色:"+treesCoating.color());
Arrays.stream(treesCoating.getAffiliatedProducts()).forEach((affiliatedProduct)->
System.out.println(affiliatedProduct.affiliatedProductName()));
}
// ----
油漆品牌:嘉宝莉
油漆保质期:2021-06-11T19:47:23.895
油漆颜色:红色
嘉宝莉红色油漆捆绑销售刷子
嘉宝莉红色油漆捆绑销售手套
-----------------------------
油漆品牌:三棵树
油漆保质期:2021-06-11T19:47:23.897
油漆颜色:红色
三棵树红色油漆捆绑销售刷子
三棵树红色油漆捆绑销售手套
从上面可以看出, 我们的工厂油漆并不关心也并不知道附属产品是那个品牌, 达到了解耦,
后续在有附属产品, 则只需要构建一个抽象工厂生产一组附属产品
定义:
抽象工厂模式提供一个接口, 用于创建相关或依赖对象的家族(一组), 而不需要明确指定具体类。
抽象工厂允许客户使用抽象接口来创建一组的相关产品, 而不需要知道(或关心) 实际产出的具体产品是什么, 这样一来客户就从具体的产品中被解耦
1