设计模式
定义:
- 拥有相同的物料,但是组合方式不同产生不同的结果,就是建造者模式需要解决的问题
吊顶、涂料、地板、瓷砖都有不同的品牌可以选择
ifelse实现
工程目录
只有一个类,几千行代码的实现:
public class DecorationPackageController {
public String getMatterList(BigDecimal area, Integer level) {
List<Matter> list = new ArrayList<Matter>(); // 装修清单
BigDecimal price = BigDecimal.ZERO; // 装修价格
// 豪华欧式
if (1 == level) {
LevelTwoCeiling levelTwoCeiling = new LevelTwoCeiling(); // 吊顶,二级顶
DuluxCoat duluxCoat = new DuluxCoat(); // 涂料,多乐士
ShengXiangFloor shengXiangFloor = new ShengXiangFloor(); // 地板,圣象
list.add(levelTwoCeiling);
list.add(duluxCoat);
list.add(shengXiangFloor);
price = price.add(area.multiply(new BigDecimal("0.2")).multiply(levelTwoCeiling.price()));
price = price.add(area.multiply(new BigDecimal("1.4")).multiply(duluxCoat.price()));
price = price.add(area.multiply(shengXiangFloor.price()));
}
// 轻奢田园
if (2 == level) {
LevelTwoCeiling levelTwoCeiling = new LevelTwoCeiling(); // 吊顶,二级顶
LiBangCoat liBangCoat = new LiBangCoat(); // 涂料,立邦
MarcoPoloTile marcoPoloTile = new MarcoPoloTile(); // 地砖,马可波罗
list.add(levelTwoCeiling);
list.add(liBangCoat);
list.add(marcoPoloTile);
price = price.add(area.multiply(new BigDecimal("0.2")).multiply(levelTwoCeiling.price()));
price = price.add(area.multiply(new BigDecimal("1.4")).multiply(liBangCoat.price()));
price = price.add(area.multiply(marcoPoloTile.price()));
}
// 省略...所有的代码都挤在一个类里面完成,后面需要更改物料,或者是添加新的组合需要改动的地方很多。不满足单一职责,可复用的原则。
建造者模式实现
工程结构:
IMenu、DecorationPackageMenu、Builder三个类分别为包装接口类、包装接口实现类、组装实现类。
创建者模型结构如下:
public interface IMenu {
/**
* 吊顶
*/
IMenu appendCeiling(Matter matter);
/**
* 涂料
*/
IMenu appendCoat(Matter matter);
/**
* 地板
*/
IMenu appendFloor(Matter matter);
/**
* 地砖
*/
IMenu appendTile(Matter matter);
/**
* 明细
*/
String getDetail();
}IMenu定义了组合的方法:添加吊顶、涂料、地板、地砖,返回具体信息
public class DecorationPackageMenu implements IMenu {
private List<Matter> list = new ArrayList<Matter>(); // 装修清单
private BigDecimal price = BigDecimal.ZERO; // 装修价格
private BigDecimal area; // 面积
private String grade; // 装修等级;豪华欧式、轻奢田园、现代简约
private DecorationPackageMenu() {
}
public DecorationPackageMenu(Double area, String grade) {
this.area = new BigDecimal(area);
this.grade = grade;
}
public IMenu appendCeiling(Matter matter) {
list.add(matter);
price = price.add(area.multiply(new BigDecimal("0.2")).multiply(matter.price()));
return this;
}
public IMenu appendCoat(Matter matter) {
list.add(matter);
price = price.add(area.multiply(new BigDecimal("1.4")).multiply(matter.price()));
return this;
}
public IMenu appendFloor(Matter matter) {
list.add(matter);
price = price.add(area.multiply(matter.price()));
return this;
}
public IMenu appendTile(Matter matter) {
list.add(matter);
price = price.add(area.multiply(matter.price()));
return this;
}
public String getDetail() {
StringBuilder detail = new StringBuilder("\r\n-------------------------------------------------------\r\n" +
"装修清单" + "\r\n" +
"套餐等级:" + grade + "\r\n" +
"套餐价格:" + price.setScale(2, BigDecimal.ROUND_HALF_UP) + " 元\r\n" +
"房屋面积:" + area.doubleValue() + " 平米\r\n" +
"材料清单:\r\n");
for (Matter matter: list) {
detail.append(matter.scene()).append(":").append(matter.brand()).append("、").append(matter.model()).append("、平米价格:").append(matter.price()).append(" 元。\n");
}
return detail.toString();
}
}DecorationPackageMenu 是接口的具体实现类
public class Builder {
public IMenu levelOne(Double area) {
return new DecorationPackageMenu(area, "豪华欧式")
.appendCeiling(new LevelTwoCeiling()) // 吊顶,二级顶
.appendCoat(new DuluxCoat()) // 涂料,多乐士
.appendFloor(new ShengXiangFloor()); // 地板,圣象
}
public IMenu levelTwo(Double area){
return new DecorationPackageMenu(area, "轻奢田园")
.appendCeiling(new LevelTwoCeiling()) // 吊顶,二级顶
.appendCoat(new LiBangCoat()) // 涂料,立邦
.appendTile(new MarcoPoloTile()); // 地砖,马可波罗
}
public IMenu levelThree(Double area){
return new DecorationPackageMenu(area, "现代简约")
.appendCeiling(new LevelOneCeiling()) // 吊顶,二级顶
.appendCoat(new LiBangCoat()) // 涂料,立邦
.appendTile(new DongPengTile()); // 地砖,东鹏
}
}Builder 则是具体的组合类,定义了不同的装修风格
后续的具体测试类:
System.out.print(new Builder.levelOne(13.5).getDetail())如果基本的物料不会变,但是组合模式经常发生变化,这可以使用建造者模式来实现,通过包装接口来实现物料的装配,通过建造者来实现具体的组合,后面更改组合的变化则只需要更改建造者类即可。
解决idea出现 Solved Cannot resolve symbol 原文链接:
- 出现这个原因是因为idea的缓存出问题了,将缓存清理一下:File -> Invalidate Caches -> checked “clear file system cache and Local History”, and then click “Invalidate and Restart”.
定义:
- 抽象工厂模式和工厂方法模式类似,也是为了解决接口选择的问题,但是抽象⼯⼚是⼀个中⼼⼯⼚,用于创建其他⼯⼚。
例子:
- 一个软件可能在不同的操作系统上面运行,不同的操作系统都有菜单、鼠标移动、键盘输入等功能,但是这些功能的实现逻辑都不一样。比如windows、linux、mac的操作系统的回车分别是\n,\n\r,\r。抽象工厂就是用于创建windows、linux、mac三种不同的工厂的设计模式。
ifelse方式
代码结构:
public class CacheServiceImpl implements CacheService {
private RedisUtils redisUtils = new RedisUtils();
private EGM egm = new EGM();
private IIR iir = new IIR();
public String get(String key, int redisType) {
if (1 == redisType) {
return egm.gain(key);
}
if (2 == redisType) {
return iir.get(key);
}
return redisUtils.get(key);
}
public void set(String key, String value, int redisType) {
if (1 == redisType) {
egm.set(key, value);
return;
}
if (2 == redisType) {
iir.set(key, value);
return;
}
redisUtils.set(key, value);
}
// 省略...
}这样子后面需要拓展的时候非常头疼,需要修改原来的代码,并且需要修改每一个方法,修改的时候容易导致原来的功能不可用。
抽象工厂模式
工程结构:
结构:
ICacheAdapter是适配器接口,因为集群A和集群B的方法名称都不相同,因此需要有一个适配器来适配方法。JDKProxy,JDKInvocationHandler是代理类的实现。通过传入不同的CacheAdapter来完成不同集群的接入。
public class EGMCacheAdapter implements ICacheAdapter {
private EGM egm = new EGM();
public String get(String key) {
return egm.gain(key);
}
public void set(String key, String value) {
egm.set(key, value);
}
public void set(String key, String value, long timeout, TimeUnit timeUnit) {
egm.setEx(key, value, timeout, timeUnit);
}
public void del(String key) {
egm.delete(key);
}
}调用:
public void test_CacheService() throws Exception {
CacheService proxy_EGM = JDKProxy.getProxy(CacheServiceImpl.class, new EGMCacheAdapter());
proxy_EGM.set("user_name_01", "小傅哥");
String val01 = proxy_EGM.get("user_name_01");
System.out.println("测试结果:" + val01);
CacheService proxy_IIR = JDKProxy.getProxy(CacheServiceImpl.class, new IIRCacheAdapter());
proxy_IIR.set("user_name_01", "小傅哥");
String val02 = proxy_IIR.get("user_name_01");
System.out.println("测试结果:" + val02);
}定义:
- 创建型设计模式,其在父类中提供一个创建对象的方法,允许子类决定实例化对象的类型。
优点:
- 增加代码的拓展性、可读性
- 屏蔽每一个功能类中的具体实现逻辑,外部只需要知道如何调用即可
- 可以去掉众多的ifelse语句,但是需要实现的类非常多,每个类只关注自身功能的实现(满足单一职责)
- 减少了代码的耦合,无需更改调用方法就可以在程序中引入新的产品类型(满足开闭原则)
缺点:
- 如果新商品类型很多,那么实现的子类会极速扩张
eg:模拟发奖多种商品,优惠券、实物商品、第三方爱奇艺兑换卡
工程结构:
- AwardReq 为入参对象、AwardRes为出参对象、PrizeController为接口类
public class PrizeController {
private Logger logger = LoggerFactory.getLogger(PrizeController.class);
public AwardRes awardToUser(AwardReq req) {
String reqJson = JSON.toJSONString(req);
AwardRes awardRes = null;
try {
if (req.getAwardType() == 1) {
// 具体业务1
}
else if(req.getAwardType() == 2) {
// 具体业务2
}
else if(req.getAwardType() == 3) {
// 具体业务3
}
}
}
}- 可以看到所有的业务都写在了一起,并且后期拓展时,增加商品类型需要先阅读这一大坨代码,让人很痛苦
ICommodity为接口父类,其定义了一个公共的方法
public interface ICommodity {
void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception;
}CardCommodityService.java、CouponCommodityService.java、GoodsCommodityService.java为优惠券、实体商品、爱奇艺的具体实现类
StoreFactory.java为商店工厂,其按照类型实现各种商品的服务。可以非常干净地处理你的代码。
public class StoreFactory {
public ICommodity getCommodityService(Integer commodityType) {
if (null == commodityType) return null;
if (1 == commodityType) return new CouponCommodityService();
if (2 == commodityType) return new GoodsCommodityService();
if (3 == commodityType) return new CardCommodityService();
throw new RuntimeException("不存在的商品服务类型");
}
}