目录
1 工厂方法模式介绍
- 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
- 简单工厂模式(不属于GOF的23种经典设计模式)
- 工厂方法模式
- 抽象工厂模式
1.1 需求: 模拟发放奖品业务
新用户注册立即参与抽奖活动 ,奖品的种类有: 打折券, 免费优酷会员,小礼品.
1.2 原始开发方式
1) 实体类
public class AwardInfo {
private String uid; //用户唯一ID
private Integer awardType; //奖品类型: 1 打折券 ,2 优酷会员,3 小礼品
private String awardNumber; //奖品编号
Map<String, String> extMap; //额外信息
}
public class DiscountInfo {
//属性信息省略......
}
public class YouKuMember {
//属性信息省略......
}
public class SmallGiftInfo {
private String userName; // 用户姓名
private String userPhone; // 用户手机
private String orderId; // 订单ID
private String relAddress; // 收货地址
}
public class DiscountResult {
private String status; // 状态码
private String message; // 信息
}
2) 服务层
public class DiscountService {
public DiscountResult sendDiscount(String uid, String number){
System.out.println("向用户发放打折券一张: " + uid + " , " + number);
return new DiscountResult("200","发放打折券成功");
}
}
public class YouKuMemberService {
public void openMember(String bindMobile , String number){
System.out.println("发放优酷会员: " + bindMobile + " , " + number);
}
}
public class SmallGiftService {
public Boolean giveSmallGift(SmallGiftInfo smallGiftInfo){
System.out.println("小礼品已发货,获奖用户注意查收! " + JSON.toJSON(smallGiftInfo));
return true;
}
}
3) 控制层
public class DeliverController {
/**
* 按照类型的不同发放商品
* 奖品类型: 1 打折券 ,2 优酷会员,3 小礼品
*/
public void awardToUser(AwardInfo awardInfo){
if(awardInfo.getAwardType() == 1){ //打折券
DiscountService discountService = new DiscountService();
DiscountResult result = discountService.sendDiscount(awardInfo.getUid(), awardInfo.getAwardNumber());
System.out.println("打折券发放成功!"+ JSON.toJSON(result));
}else if(awardInfo.getAwardType() == 2){ //优酷会员
//获取用户手机号
String bindMobile = awardInfo.getExtMap().get("phone");
//调用service
YouKuMemberService youKuMemberService = new YouKuMemberService();
youKuMemberService.openMember(bindMobile,awardInfo.getAwardNumber());
System.out.println("优酷会员发放成功!");
}else if(awardInfo.getAwardType() == 3){ /*
小礼品
封装收货用户信息
*/
SmallGiftInfo smallGiftInfo = new SmallGiftInfo();
smallGiftInfo.setUserName(awardInfo.getExtMap().get("username"));
smallGiftInfo.setOrderId(UUID.randomUUID().toString());
smallGiftInfo.setRelAddress(awardInfo.getExtMap().get("adderss"));
SmallGiftService smallGiftService = new SmallGiftService();
Boolean isSuccess = smallGiftService.giveSmallGift(smallGiftInfo);
System.out.println("小礼品发放成功!" + isSuccess);
}
}
}
4) 测试
public class TestApi01 {
//测试发放奖品接口
@Test
public void test01(){
DeliverController deliverController = new DeliverController();
//1. 发放打折券优惠
AwardInfo info1 = new AwardInfo();
info1.setUid("1001");
info1.setAwardType(1);
info1.setAwardNumber("DEL12345");
deliverController.awardToUser(info1);
//2. 发放优酷会员
AwardInfo info2 = new AwardInfo();
info2.setUid("1002");
info2.setAwardType(2);
info2.setAwardNumber("DW12345");
Map<String,String> map = new HashMap<>();
map.put("phone","13512341234");
info2.setExtMap(map);
deliverController.awardToUser(info2);
//2. 发放小礼品
AwardInfo info3 = new AwardInfo();
info3.setUid("1003");
info3.setAwardType(3);
info3.setAwardNumber("SM12345");
Map<String,String> map2 = new HashMap<>();
map2.put("username","大远");
map2.put("phone","13512341234");
map2.put("address","北京天安门");
info3.setExtMap(map2);
deliverController.awardToUser(info3);
}
}
1.3 简单工厂模式
1.3.1 简单工厂模式介绍
简单工厂不是一种设计模式,反而比较像是一种编程习惯。
实现方式:
定义一个工厂类,根据传入的参数不同返回不同的实例,被创建的实例具有共同的父类或接口。
适用场景: (1)需要创建的对象较少。 (2)客户端不关心对象的创建过程。
1.3.2 简单工厂原理
- 抽象产品 :定义了产品的规范,描述了产品的主要特性和功能。
- 具体产品 :实现或者继承抽象产品的子类
- 具体工厂 :提供了创建产品的方法,调用者通过该方法来获取产品。
1.3.3 简单工厂模式重构代码
1) service
/**
* 免费商品发放接口
* @author spikeCong
* @date 2022/9/8
**/
public interface IFreeGoods {
ResponseResult sendFreeGoods(AwardInfo awardInfo);
}
/**
* 模拟打折券服务
* @author spikeCong
* @date 2022/9/8
**/
public class DiscountFreeGoods implements IFreeGoods {
@Override
public ResponseResult sendFreeGoods(AwardInfo awardInfo) {
System.out.println("向用户发放一张打折券: " + awardInfo.getUid() + " , " + awardInfo.getAwardNumber());
return new ResponseResult("200","打折券发放成功!");
}
}
/**
* 小礼品发放服务
* @author spikeCong
* @date 2022/9/8
**/
public class SmallGiftFreeGoods implements IFreeGoods {
@Override
public ResponseResult sendFreeGoods(AwardInfo awardInfo) {
SmallGiftInfo smallGiftInfo = new SmallGiftInfo();
smallGiftInfo.setUserPhone(awardInfo.getExtMap().get("phone"));
smallGiftInfo.setUserName(awardInfo.getExtMap().get("username"));
smallGiftInfo.setAddress(awardInfo.getExtMap().get("address"));
smallGiftInfo.setOrderId(UUID.randomUUID().toString());
System.out.println("小礼品发放成,请注意查收: " + JSON.toJSON(smallGiftInfo));
return new ResponseResult("200","小礼品发送成功",smallGiftInfo);
}
}
/**
* 优酷 会员服务
* @author spikeCong
* @date 2022/9/8
**/
public class YouKuMemberFreeGoods implements IFreeGoods {
@Override
public ResponseResult sendFreeGoods(AwardInfo awardInfo) {
String phone = awardInfo.getExtMap().get("phone");
System.out.println("发放优酷会员成功,绑定手机号: " + phone);
return new ResponseResult("200","优酷会员发放成功!");
}
}
2) factory
/**
* 具体工厂: 生成免费商品
* @author spikeCong
* @date 2022/9/9
**/
public class FreeGoodsFactory {
public static IFreeGoods getInstance(Integer awardType){
IFreeGoods iFreeGoods = null;
if(awardType == 1){ //打折券
iFreeGoods = new DiscountFreeGoods();
}else if(awardType == 2){ //优酷会员
iFreeGoods = new YouKuMemberFreeGoods();
}else if(awardType == 3){ //小礼品
iFreeGoods = new SmallGiftFreeGoods();
}
return iFreeGoods;
}
}
3)controller
public class DeliverController {
//发放奖品
public ResponseResult awardToUser(AwardInfo awardInfo){
try {
IFreeGoods freeGoods = FreeGoodsFactory.getInstance(awardInfo.getAwardTypes());
ResponseResult responseResult = freeGoods.sendFreeGoods(awardInfo);
return responseResult;
} catch (Exception e) {
e.printStackTrace();
return new ResponseResult("201","奖品发放失败!");
}
}
}
4) 测试
public class TestApi02 {
DeliverController deliverController = new DeliverController();
@Test
public void test01(){
//1. 发放打折券优惠
AwardInfo info1 = new AwardInfo();
info1.setUid("1001");
info1.setAwardTypes(1);
info1.setAwardNumber("DEL12345");
ResponseResult result = deliverController.awardToUser(info1);
System.out.println(result);
}
@Test
public void test02(){
//2. 发放优酷会员
AwardInfo info2 = new AwardInfo();
info2.setUid("1002");
info2.setAwardTypes(2);
info2.setAwardNumber("DW12345");
Map<String,String> map = new HashMap<>();
map.put("phone","13512341234");
info2.setExtMap(map);
ResponseResult result1 = deliverController.awardToUser(info2);
System.out.println(result1);
}
@Test
public void test03(){
//3. 发放小礼品
AwardInfo info3 = new AwardInfo();
info3.setUid("1003");
info3.setAwardTypes(3);
info3.setAwardNumber("SM12345");
Map<String,String> map2 = new HashMap<>();
map2.put("username","大远");
map2.put("phone","13512341234");
map2.put("address","北京天安门");
info3.setExtMap(map2);
ResponseResult result2 = deliverController.awardToUser(info3);
System.out.println(result2);
}
}
1.3.4 简单工厂模式总结
优点:
封装了创建对象的过程,可以通过参数直接获取对象。把对象的创建和业务逻辑层分开,这样以后就避免了修改客户代码,如果要实现新产品直接修改工厂类,而不需要在原代码中修改,这样就降低了客户代码修改的可能性,更加容易扩展。
缺点:
增加新产品时还是需要修改工厂类的代码,违背了“开闭原则”。
1.4 工厂方法模式
1.4.1 工厂方法模式介绍
定义一个用于创建对象的接口,让子类决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类。
1.4.2 工厂方法模式原理
工厂方法模式的目的很简单,就是封装对象创建的过程,提升创建对象方法的可复用性。
- 抽象工厂:提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
- 具体工厂:主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
- 抽象产品:定义了产品的规范,描述了产品的主要特性和功能。
- 具体产品:实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
1.4.3 工厂方法模式重构代码
抽象工厂
public interface FreeGoodsFactory {
IFreeGoods getInstance();
}
具体工厂
public class DiscountFreeGoodsFactory implements FreeGoodsFactory {
@Override
public IFreeGoods getInstance() {
return new DiscountFreeGoods();
}
}
public class SmallGiftFreeGoodsFactory implements FreeGoodsFactory {
@Override
public IFreeGoods getInstance() {
return new SmallGiftFreeGoods();
}
}
Controller
public class DeliverController {
/**
* 按照类型的不同发放商品
*/
public ResponseResult awardToUser(AwardInfo awardInfo){
FreeGoodsFactory freeGoodsFactory = null;
if(awardInfo.getAwardType() == 1){
freeGoodsFactory = new DiscountFreeGoodsFactory();
}else if(awardInfo.getAwardType() == 2){
freeGoodsFactory = new SmallGiftFreeGoodsFactory();
}
IFreeGoods freeGoods = freeGoodsFactory.getInstance();
System.out.println("=====工厂方法模式========");
ResponseResult result = freeGoods.sendFreeGoods(awardInfo);
return result;
}
}
工厂类对象的创建逻辑又耦合进了 awardToUser() 方法中,跟我们最初的代码版本非常相似,引入工厂方法非但没有解决问题,反倒让设计变得更加复杂了。
我们可以为工厂类再创建一个简单工厂,也就是工厂的工厂,用来创建工厂类对象。
/**
* 用简单方法模式实现: 工厂的工厂,作用是不需要每次创建新的工厂对象
* @author spikeCong
* @date 2022/9/9
**/
public class FreeGoodsFactoryMap {
private static final Map<Integer,FreeGoodsFactory> cachedFactories = new HashMap<>();
static{
cachedFactories.put(1, new DiscountFreeGoodsFactory());
cachedFactories.put(2, new SmallGiftFreeGoodsFactory());
}
public static FreeGoodsFactory getParserFactory(Integer type){
if(type == 1){
FreeGoodsFactory freeGoodsFactory = cachedFactories.get(1);
return freeGoodsFactory;
}else if(type ==2){
FreeGoodsFactory freeGoodsFactory = cachedFactories.get(2);
return freeGoodsFactory;
}
return null;
}
}
Controller
/**
* 发放奖品接口
* @author spikeCong
* @date 2022/9/7
**/
public class DeliverController {
/**
* 按照类型的不同发放商品
*/
public ResponseResult awardToUser(AwardInfo awardInfo){
//根据类型获取工厂
FreeGoodsFactory goodsFactory = FreeGoodsFactoryMap.getParserFactory(awardInfo.getAwardType());
//从工厂中获取对应实例
IFreeGoods freeGoods = goodsFactory.getInstance();
System.out.println("=====工厂方法模式========");
ResponseResult result = freeGoods.sendFreeGoods(awardInfo);
return result;
}
}
1.4.4 工厂方法模式总结
工厂方法模优缺点
优点:
- 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程;
- 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则;
缺点:
- 每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。
什么时候使用工厂方法模式
- 需要使用很多重复代码创建对象时,比如,DAO 层的数据对象、API 层的 VO 对象等。
- 创建对象要访问外部信息或资源时,比如,读取数据库字段,获取访问授权 token 信息,配置文件等。
- 创建需要统一管理生命周期的对象时,比如,会话信息、用户网页浏览轨迹对象等。
- 创建池化对象时,比如,连接池对象、线程池对象、日志对象等。这些对象的特性是:有限、可重用,使用工厂方法模式可以有效节约资源。
- 希望隐藏对象的真实类型时,比如,不希望使用者知道对象的真实构造函数参数等。
1.5 抽象工厂模式
1.5.1 抽象工厂模式
抽象工厂模式比工厂方法模式的抽象程度更高. 在工厂方法模式中每一个具体工厂只需要生产一种具体产品,但是在抽象工厂模式中一个具体工厂可以生产一组相关的具体产品,这样一组产品被称为产品族.产品族中的每一个产品都分属于某一个产品继承等级结构.
1) 产品等级结构与产品族
2) 抽象工厂模式概述
1.5.2 抽象工厂模式原理
- 抽象工厂(Abstract Factory):它声明了一种用于创建一族产品的方法,每一个方法对应一种产品.
- 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建.
- 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
- 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它 同具体工厂之间是多对一的关系。
1.5.3 抽象工厂模式实现
抽象工厂
/**
* 抽象工厂: 在一个抽象工厂中可以声明多个工厂方法,用于创建不同类型的产品
* @author spikeCong
* @date 2022/9/15
**/
public interface AppliancesFactory {
AbstractTV createTV();
AbstractFreezer createFreezer();
}
具体工厂: 每一个具体工厂方法,可以返回一个特定的产品对象,而同一个具体工厂所创建的产品对象构成了一个产品族.
public class HairFactory implements AppliancesFactory {
@Override
public AbstractTV createTV() {
return new HairTV();
}
@Override
public AbstractFreezer createFreezer() {
return new HairFreezer();
}
}
public class HisenseFactory implements AppliancesFactory {
@Override
public AbstractTV createTV() {
return new HisenseTV();
}
@Override
public AbstractFreezer createFreezer() {
return new HisenseFreezer();
}
}
抽象产品
public interface AbstractFreezer {}
public interface AbstractTV {}
具体产品
public class HairFreezer implements AbstractFreezer {}
public class HisenseFreezer implements AbstractFreezer {}
public class HairTV implements AbstractTV {}
public class HisenseTV implements AbstractTV {}
客户端
public class Client {
private AbstractTV tv;
private AbstractFreezer freezer;
public Client(AppliancesFactory factory){
//在客户端看来就是使用抽象工厂来生产家电
this.tv = factory.createTV();
this.freezer = factory.createFreezer();
}
public AbstractTV getTv() {
return tv;
}
public void setTv(AbstractTV tv) {
this.tv = tv;
}
public AbstractFreezer getFreezer() {
return freezer;
}
public void setFreezer(AbstractFreezer freezer) {
this.freezer = freezer;
}
public static void main(String[] args) {
Client client = new Client(new HisenseFactory());
AbstractTV tv = client.getTv();
System.out.println(tv);
AbstractFreezer freezer = client.getFreezer();
System.out.println(freezer);
}
}
1.5.4 抽象工厂模式总结
抽象工厂模式优点
-
对于不同产品系列有比较多共性特征时,可以使用抽象工厂模式,有助于提升组件的复用性.
-
当需要提升代码的扩展性并降低维护成本时,把对象的创建和使用过程分开,能有效地将代码统一到一个级别上
- 解决跨平台带来的兼容性问题
抽象工厂模式缺点
增加新的产品等级结构麻烦,需要对原有结构进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大不变,违背了开闭原则.