常用设计模式
这篇文章主要是浅谈常用的一些设计模式
1、我们为什么要用设计模式?
业务是变更的,所以需求也是变更的。做软件开发,不变的是变化;这是真理,就像在java中为什么会有interface接口一样。它的存在就是为了隔离变化。使用设计模式也是一样,对我们的代码进行抽象隔离,使代码变得易扩展,易维护。尽量做到需求变更时,我们能少改代码,甚至不改代码。本文章就简单介绍策略模式,工厂模式的结合使用。
策略模式
这基本上是最常用的设计模式之1
有一个经典的场景:双11 、 618 这种促销活动,各种商品有不同的促销活动。什么满减,每满减,数量折扣 2件7折,3件5折 满3件免最低一件等等 这些活动琳琅满目。
后台代码如何实现对应订单的金额计算?一个活动对应的就是一个完整计算订单金额的算法。下面是传统的写法
//计算订单金额的方法,接受一个订单,和一个活动名称,两个参数
public BigDecimal preOrder(Order order,String promotion){
switch(promotion){
case "promotion1":
//活动1订单金额算法
return calPromotion1(order);
case "promotion2":
//活动2订单金额算法
return calPromotion2(order);
case "promotion3":
//活动3订单金额算法
return calPromotion3(order);
...
}
}
这样写固然能实现业务,但是随着活动的增加,这个类的代码会变得无比臃肿、而且我们要实现无数个calPromotion方法,最后代码爆炸难以维护。
这时候我们就可以采用策略模式,定义一个接口,这个接口只有一个计算订单金额的方法:
//计算订单金额的接口
public interface PromotionCalculation{
//计算订单金额的方法
public BigDecimal calculation(Order order);
}
//活动1的订单金额计算方法实现
public class PromotionCalculation1 implements PromotionCalculation{
//计算订单金额的方法
public BigDecimal calculation(Order order){
//具体算法
return orderPrice;
}
}
这样我们就完成了根据活动计算订单金额这个行为的抽象,每个一个具体的活动就只需要实现这个接口,在上面的switch里面,我们只需要new PromotionCalculation1对象就能调用对应活动的计算方法。避免了再service类里面写无数个calPromotion方法,避免了类爆炸。service层代码变成了这样
public BigDecimal preOrder(Order order,String promotion){
switch(promotion){
case "promotion1":
//活动1订单金额算法
return new PromotionCalculation1().calculation(order);
case "promotion2":
//活动2订单金额算法
return new PromotionCalculation2().calculation(order);
case "promotion3":
//活动3订单金额算法
return new PromotionCalculation3().calculation(order);
...
}
}
工厂模式
还是上面的场景,虽然避免service里面不会再有N个 calPromotion方法,但还是避免不了随着活动的增多,switch里面的代码还是会无限扩展。
这个时候工厂模式就有了用武之地。在次改造代码;工厂工厂,就是生产产品。我们创建的一个工厂类,里面还是只有一个方法,就是创建具体实现订单金额计算接口的实现类对象createPromotionCalculationInstance(),
public class PromotionCalculationFactory{
//创建具体 根据活动计算订单金额实现类的实例
PromotionCalculation createPromotionCalculationInstance(String promotion){
switch(promotion){
case "promotion1":
//活动1订单金额算法实现类的实例
return new PromotionCalculation1();
case "promotion2":
//活动2订单金额算法实现类的实例
return new PromotionCalculation2();
case "promotion3":
//活动3订单金额算法实现类的实例
return new PromotionCalculation3();
...
}
}
}
有了上面的工厂,我们的service类里面就变得简单了
public BigDecimal preOrder(Order order,String promotion){
//根据活动创建 根据活动计算订单金额的具体实现类对象 并调用计算订单金额的方法。
return new PromotionCalculationFactory()
.createPromotionCalculationInstance(promotion)
.calculation(order);
}
到了这里,我们service里面的代码似乎就变得很简洁,就一行就搞定了。再思考,现在还有一个问题,就是工厂类里面,随着活动的增加switch里的代码还是会变得臃肿,这时候我们可以利用数据库来实现。
1、创建一张数据库表,里面存的是一个个活动信息,并将实现类的资源路径作为字段保存起来。类似这种:
id | path | name |
---|---|---|
1 | com.service.PromotionCalculation1 | 活动1 |
2 | com.service.PromotionCalculation2 | 活动2 |
然后在工厂里面,根据前端传过来promotion字段去数据库查询对应活动信息。再根据资源路径动态的创建实例,工厂改造过后的代码:
public class PromotionCalculationFactory{
//活动信息dao层
private PromotionDao promotionDao;
//创建具体 根据活动计算订单金额实现类的实例
PromotionCalculation createPromotionCalculationInstance(String promotion){
//根据promotion查询数据库对应的活动信息
PromotionEntity promo = PromotionDao.selectByPromotion(promotion);
//获取活动实现类path
String path = promo.getPath();
//加载类对象
Class cls = Class.forName(path);
//创建实例并返回
return (PromotionCalculation) cls.newInstance();
}
}
这样我们可以只关注活动算法的实现,每次我们新增一个活动,我们只需要实现PromotionCalculation 接口,并把对应实现类的资源路径存到数据库中就可以了,而不再需要改额外的代码。
总结
1.策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们可以相互替换,让算法独立于使用它们的用户而独立变化。
2.工厂模式主要负责实例的创建,工厂模式分为,简单工厂,工厂方法,抽象工厂3种。本文只介绍了简单工厂,感兴趣的朋友可以看看另外两种的写法。
3.设计模式远不止这些,比如装饰者模式,代理模式,责任链模式,桥接模式,适配器模式,外观模式,观察者模式等等。熟练的运用这些设计模式,能极大减少冗余代码。
最后,引用授业恩师一句话:不会偷懒的程序员不是好程序员。多思考,多总结,才能走得更远。