各地农业信息网站的建设,温州微信网站定制,怎么用电脑自带软件做网站页面,推广单页网站免费制作2 工厂方法模式
工厂模式#xff08;Factory Pattern#xff09;是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式#xff0c;它提供了一种创建对象的最佳方式。
在工厂模式中#xff0c;我们在创建对象时不会对客户端暴露创建逻辑#xff0c;并且是通…2 工厂方法模式
工厂模式Factory Pattern是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式它提供了一种创建对象的最佳方式。
在工厂模式中我们在创建对象时不会对客户端暴露创建逻辑并且是通过使用一个共同的接口来指向新创建的对象。
《设计模式》一书中工厂模式被分为了三种简单工厂、工厂方法和抽象工厂。不过在书中作者将简单工厂模式看作是工厂方法模式的一种特例。
接下来我会介绍三种工厂模式的原理及使用
简单工厂模式不属于GOF的23种经典设计模式工厂方法模式抽象工厂模式
2.1 需求: 模拟发放奖品业务
需求: 为了让我们的案例更加贴近实际开发, 这里我们来模拟一下互联网电商中促销拉新下的业务场景, 新用户注册立即参与抽奖活动 ,奖品的种类有: 打折券, 免费优酷会员,小礼品. 2.2 原始开发方式
不考虑设计原则,不使用设计模式的方式进行开发
在不考虑任何代码的可扩展性的前提下,只为了尽快满足需求.我们可以这样去设计这个业务的代码结构:
1) 实体类
名称描述AwardInfo获奖信息对应实体类DiscountInfo打折券信息对应实体类YouKuMember优酷会员对应实体类SmallGiftInfo小礼品信息对应实体类DiscountResult打折券操作响应结果封装类
public class AwardInfo {private String uid; //用户唯一IDprivate Integer awardType; //奖品类型: 1 打折券 ,2 优酷会员,3 小礼品private String awardNumber; //奖品编号MapString, String extMap; //额外信息}public class DiscountInfo {//属性信息省略......
}public class YouKuMember {//属性信息省略......
}public class SmallGiftInfo {private String userName; // 用户姓名private String userPhone; // 用户手机private String orderId; // 订单IDprivate String relAddress; // 收货地址}public class DiscountResult {private String status; // 状态码private String message; // 信息
}2) 服务层
名称功能描述DiscountServiceDiscountResult sendDiscount(String uid,String number)模拟打折券服务YouKuMemberServicevoid openMember(String bindMobile , String number)模拟赠送优酷会员服务SmallGiftServiceBoolean giveSmallGift(SmallGiftInfo smallGiftInfo)模拟礼品服务
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) 控制层
名称功能描述DeliverControllerResponseResult awardToUser(AwardInfo awardInfo)按照类型的不同发放商品奖品类型: 1 打折券 ,2 优酷会员,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);//调用serviceYouKuMemberService 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 {//测试发放奖品接口Testpublic 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);MapString,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);MapString,String map2 new HashMap();map2.put(username,大远);map2.put(phone,13512341234);map2.put(address,北京天安门);info3.setExtMap(map2);deliverController.awardToUser(info3);}
}对于上面的实现方式,如果我们有想要添加的新的奖品时,势必要改动DeliverController的代码,违反开闭原则.而且如果有的抽奖接口出现问题,那么对其进行重构的成本会非常高.
除此之外代码中有一组if分支判断逻辑,现在看起来还可以,但是如果经历几次迭代和拓展,后续ifelse肯定还会增加.到时候接手这段代码的研发将会十分痛苦.
2.3 简单工厂模式
2.3.1 简单工厂模式介绍
简单工厂不是一种设计模式反而比较像是一种编程习惯。简单工厂模式又叫做静态工厂方法模式static Factory Method pattern,它是通过使用静态方法接收不同的参数来返回不同的实例对象.
实现方式:
定义一个工厂类根据传入的参数不同返回不同的实例被创建的实例具有共同的父类或接口。
适用场景 1需要创建的对象较少。 2客户端不关心对象的创建过程。
2.3.2 简单工厂原理
简单工厂包含如下角色
抽象产品 定义了产品的规范描述了产品的主要特性和功能。具体产品 实现或者继承抽象产品的子类具体工厂 提供了创建产品的方法调用者通过该方法来获取产品。 2.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 {Overridepublic 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 {Overridepublic 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 {Overridepublic 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;}
}3controller
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();Testpublic 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);}Testpublic void test02(){//2. 发放优酷会员AwardInfo info2 new AwardInfo();info2.setUid(1002);info2.setAwardTypes(2);info2.setAwardNumber(DW12345);MapString,String map new HashMap();map.put(phone,13512341234);info2.setExtMap(map);ResponseResult result1 deliverController.awardToUser(info2);System.out.println(result1);}Testpublic void test03(){//3. 发放小礼品AwardInfo info3 new AwardInfo();info3.setUid(1003);info3.setAwardTypes(3);info3.setAwardNumber(SM12345);MapString,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);}
}2.3.4 简单工厂模式总结
优点
封装了创建对象的过程可以通过参数直接获取对象。把对象的创建和业务逻辑层分开这样以后就避免了修改客户代码如果要实现新产品直接修改工厂类而不需要在原代码中修改这样就降低了客户代码修改的可能性更加容易扩展。
缺点
增加新产品时还是需要修改工厂类的代码违背了“开闭原则”。
2.4 工厂方法模式
2.4.1 工厂方法模式介绍
工厂方法模式 Factory Method pattern,属于创建型模式.
概念: 定义一个用于创建对象的接口让子类决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类。
2.4.2 工厂方法模式原理
工厂方法模式的目的很简单就是封装对象创建的过程提升创建对象方法的可复用性。
工厂方法模式的主要角色
抽象工厂提供了创建产品的接口调用者通过它访问具体工厂的工厂方法来创建产品。具体工厂主要是实现抽象工厂中的抽象方法完成具体产品的创建。抽象产品定义了产品的规范描述了产品的主要特性和功能。具体产品实现了抽象产品角色所定义的接口由具体工厂来创建它同具体工厂之间一一对应。
我们直接来看看工厂方法模式的 UML 图 2.4.3 工厂方法模式重构代码
为了提高代码扩展性,我们需要将简单工厂中的if分支逻辑去掉,通过增加抽象工厂(生产工厂的工厂)的方式,让具体工厂去进行实现,由具体工厂来决定实例化哪一个具体的产品对象.
抽象工厂
public interface FreeGoodsFactory {IFreeGoods getInstance();
}具体工厂
public class DiscountFreeGoodsFactory implements FreeGoodsFactory {Overridepublic IFreeGoods getInstance() {return new DiscountFreeGoods();}
}public class SmallGiftFreeGoodsFactory implements FreeGoodsFactory {Overridepublic 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 MapInteger,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;}
}现在我们的代码已经基本上符合了开闭原则,当有新增的产品时,我们需要做的事情包括:
创建新的产品类,并且让该产品实现抽象产品接口创建产品类对应的具体工厂,并让具体工厂实现抽象工厂将新的具体工厂对象,添加到FreeGoodsFactoryMap的cachedFactories中即可,需要改动的代码改动的非常少.
2.4.4 工厂方法模式总结
工厂方法模优缺点
优点
用户只需要知道具体工厂的名称就可得到所要的产品无须知道产品的具体创建过程在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类无须对原工厂进行任何修改满足开闭原则
缺点
每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类这增加了系统的复杂度。
什么时候使用工厂方法模式
需要使用很多重复代码创建对象时比如DAO 层的数据对象、API 层的 VO 对象等。创建对象要访问外部信息或资源时比如读取数据库字段获取访问授权 token 信息配置文件等。创建需要统一管理生命周期的对象时比如会话信息、用户网页浏览轨迹对象等。创建池化对象时比如连接池对象、线程池对象、日志对象等。这些对象的特性是有限、可重用使用工厂方法模式可以有效节约资源。希望隐藏对象的真实类型时比如不希望使用者知道对象的真实构造函数参数等。 文章转载自: http://www.morning.bypfj.cn.gov.cn.bypfj.cn http://www.morning.myhpj.cn.gov.cn.myhpj.cn http://www.morning.pgcmz.cn.gov.cn.pgcmz.cn http://www.morning.nqbpz.cn.gov.cn.nqbpz.cn http://www.morning.bqxxq.cn.gov.cn.bqxxq.cn http://www.morning.bkslb.cn.gov.cn.bkslb.cn http://www.morning.crdtx.cn.gov.cn.crdtx.cn http://www.morning.wcrcy.cn.gov.cn.wcrcy.cn http://www.morning.rmfwh.cn.gov.cn.rmfwh.cn http://www.morning.bktzr.cn.gov.cn.bktzr.cn http://www.morning.rjtmg.cn.gov.cn.rjtmg.cn http://www.morning.cmhkt.cn.gov.cn.cmhkt.cn http://www.morning.sqhtg.cn.gov.cn.sqhtg.cn http://www.morning.rykmf.cn.gov.cn.rykmf.cn http://www.morning.swkzr.cn.gov.cn.swkzr.cn http://www.morning.nbqwr.cn.gov.cn.nbqwr.cn http://www.morning.ppdr.cn.gov.cn.ppdr.cn http://www.morning.fglth.cn.gov.cn.fglth.cn http://www.morning.yqmmh.cn.gov.cn.yqmmh.cn http://www.morning.rhsr.cn.gov.cn.rhsr.cn http://www.morning.mjbjq.cn.gov.cn.mjbjq.cn http://www.morning.bpmfl.cn.gov.cn.bpmfl.cn http://www.morning.ypjjh.cn.gov.cn.ypjjh.cn http://www.morning.plxhq.cn.gov.cn.plxhq.cn http://www.morning.hrgxk.cn.gov.cn.hrgxk.cn http://www.morning.jglqn.cn.gov.cn.jglqn.cn http://www.morning.rfzbm.cn.gov.cn.rfzbm.cn http://www.morning.fwnyz.cn.gov.cn.fwnyz.cn http://www.morning.fnfxp.cn.gov.cn.fnfxp.cn http://www.morning.yskhj.cn.gov.cn.yskhj.cn http://www.morning.mwwnz.cn.gov.cn.mwwnz.cn http://www.morning.ydyjf.cn.gov.cn.ydyjf.cn http://www.morning.mxlwl.cn.gov.cn.mxlwl.cn http://www.morning.xqmd.cn.gov.cn.xqmd.cn http://www.morning.jfjqs.cn.gov.cn.jfjqs.cn http://www.morning.brkrt.cn.gov.cn.brkrt.cn http://www.morning.xkyfq.cn.gov.cn.xkyfq.cn http://www.morning.bqnhh.cn.gov.cn.bqnhh.cn http://www.morning.xhgcr.cn.gov.cn.xhgcr.cn http://www.morning.yxbdl.cn.gov.cn.yxbdl.cn http://www.morning.qrpx.cn.gov.cn.qrpx.cn http://www.morning.madamli.com.gov.cn.madamli.com http://www.morning.drrt.cn.gov.cn.drrt.cn http://www.morning.zxzgr.cn.gov.cn.zxzgr.cn http://www.morning.rkmsm.cn.gov.cn.rkmsm.cn http://www.morning.nmlpp.cn.gov.cn.nmlpp.cn http://www.morning.fkyqm.cn.gov.cn.fkyqm.cn http://www.morning.ndyrb.com.gov.cn.ndyrb.com http://www.morning.tqbqb.cn.gov.cn.tqbqb.cn http://www.morning.tgts.cn.gov.cn.tgts.cn http://www.morning.jwxmn.cn.gov.cn.jwxmn.cn http://www.morning.gqwpl.cn.gov.cn.gqwpl.cn http://www.morning.nqlx.cn.gov.cn.nqlx.cn http://www.morning.rdzgm.cn.gov.cn.rdzgm.cn http://www.morning.nswcw.cn.gov.cn.nswcw.cn http://www.morning.cfrz.cn.gov.cn.cfrz.cn http://www.morning.jtmql.cn.gov.cn.jtmql.cn http://www.morning.ktbjk.cn.gov.cn.ktbjk.cn http://www.morning.jtjmz.cn.gov.cn.jtjmz.cn http://www.morning.zgdnz.cn.gov.cn.zgdnz.cn http://www.morning.iterlog.com.gov.cn.iterlog.com http://www.morning.gqfjb.cn.gov.cn.gqfjb.cn http://www.morning.kmlmf.cn.gov.cn.kmlmf.cn http://www.morning.yrblz.cn.gov.cn.yrblz.cn http://www.morning.qhydkj.com.gov.cn.qhydkj.com http://www.morning.kpcxj.cn.gov.cn.kpcxj.cn http://www.morning.ndxss.cn.gov.cn.ndxss.cn http://www.morning.ffbl.cn.gov.cn.ffbl.cn http://www.morning.lwhsp.cn.gov.cn.lwhsp.cn http://www.morning.qfzjn.cn.gov.cn.qfzjn.cn http://www.morning.hsrpr.cn.gov.cn.hsrpr.cn http://www.morning.qtzk.cn.gov.cn.qtzk.cn http://www.morning.ahscrl.com.gov.cn.ahscrl.com http://www.morning.bnfrj.cn.gov.cn.bnfrj.cn http://www.morning.fqljq.cn.gov.cn.fqljq.cn http://www.morning.qqhersx.com.gov.cn.qqhersx.com http://www.morning.ftwlay.cn.gov.cn.ftwlay.cn http://www.morning.yrdkl.cn.gov.cn.yrdkl.cn http://www.morning.pqcbx.cn.gov.cn.pqcbx.cn http://www.morning.rszbj.cn.gov.cn.rszbj.cn