一、策略模式
1.1策略模式定义
在策略模式(Strategy Pattern)中一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
在策略模式定义了一系列算法或策略,并将每个算法封装在独立的类中,使得它们可以互相替换。通过使用策略模式,可以在运行时根据需要选择不同的算法,而不需要修改客户端代码。
在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
1.2 使用场景
1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
2、一个系统需要动态地在几种算法中选择一种。
3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
二、工厂模式
2.1工厂模式定义
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
工厂模式提供了一种将对象的实例化过程封装在工厂类中的方式。通过使用工厂模式,可以将对象的创建与使用代码分离,提供一种统一的接口来创建不同类型的对象。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
2.2 使用场景
1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
3、设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。
注意事项:作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
三、实现
需求为用户在美团上购买物品后的核销业务,根据不同核销完成后的返回结果进行核销限制添加积分等,例如是否是本公司app下的会员,是否添加本公司企微,是否关注本公司公众号来进行对应操作,(后续可能会添加其他核销方式),所以该处可利用策略模式实现,而不是一长串的if..else进行功能的实现,违反了开闭原则(对扩展开放,对修改封闭),
3.1 使用if...else实现功能伪代码
过多嵌套,后续扩展功能不利于维护,实现简单
public class StrategyTest {
@Resource
private TbUserDao tbUserDao;
@Resource
private TbActivityDao tbActivityDao;
@Resource
private MtCouponService mtCouponService;
@Test
public Object verify (String userId,String receipt_code) {
Map<String,Object> result = new HashMap<>();
//1. 获取用户信息
TbUser tbUser = tbUserDao.selectUserByUserId(userId);
if (null == tbUser) {
return null;
}
// 根据receipt_code 和 skuId 查询出需要核销的类型
// 2. 根据不同的核销类型进行核销
String callBackMsg = mtCouponService.getCoinByReceiptCode(receipt_code, userId, "杭州");
Map callBackMap = JSON.parseObject(callBackMsg, Map.class);
if ("200".equals(callBackMap.get("code"))) {
//2.1. 判断核销记录增加活动对应参数
TbActivity activity = new TbActivity();
activity.setModifyTime(new Date());
if ("企微".equals(callBackMap.get("type"))) {
activity.setActivitySource("企微");
activity.setActivityCurrency(tbUser.getUserCurrency() + 200);
tbActivityDao.insert(activity);
result.put("code", "200");
result.put("msg", "核销成功");
return result;
}else if ("加入企微群".equals(callBackMap.get("type"))) {
activity.setActivitySource("加群");
activity.setActivityCurrency(tbUser.getUserCurrency() + 230);
tbActivityDao.insert(activity);
result.put("code", "200");
result.put("msg", "核销成功");
return result;
}else if ("关注公众号".equals(callBackMap.get("type"))) {
activity.setActivitySource("关注公众号");
activity.setActivityCurrency(tbUser.getUserCurrency() + 300);
tbActivityDao.insert(activity);
result.put("code", "200");
result.put("msg", "核销成功");
return result;
}else if ("开通会员".equals(callBackMap.get("type"))) {
activity.setActivitySource("开通会员");
activity.setActivityCurrency(tbUser.getUserCurrency() + 400);
tbActivityDao.insert(activity);
result.put("code", "200");
result.put("msg", "核销成功");
return result;
}
}
result.put("code", "505");
result.put("msg", callBackMap.get("msg"));
return result;
}
}
3.2 使用策略模式+工厂模式
涉及到实际业务,服务实现直接简化为打印相应核销策略
抽象策略类接口
public interface VerificationService {
String verificationName();
void verify();
}
实现具体策略服务
@Service
public class OfficialAccountsServiceImpl implements VerificationService{
@Override
public String verificationName() {
return VerificationEnum.OfficialAccounts.getName();
}
@Override
public void verify() {
System.out.println("公众号核销 = ");
}
}
@Service
public class VipVerificationServiceImpl implements VerificationService{
@Override
public String verificationName() {
return VerificationEnum.Vip.getName();
}
@Override
public void verify() {
System.out.println("会员核销 = ");
}
}
@Service
public class WeComGroupServiceImpl implements VerificationService{
@Override
public String verificationName() {
return VerificationEnum.WeComGroup.getName();
}
@Override
public void verify() {
System.out.println("企微加群核销 = ");
}
}
@Service
public class WeComVerificationServiceImpl implements VerificationService{
@Override
public String verificationName() {
return VerificationEnum.WeCom.getName();
}
@Override
public void verify() {
System.out.println("企微核销 = ");
}
}
环境类
/**
* 核销枚举类
*/
public enum VerificationEnum {
WeCom( "WeCom"),
WeComGroup("WeComGroup"),
OfficialAccounts( "OfficialAccounts"),
Vip("Vip");
private String name;
VerificationEnum(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/**
* 初始化,并获取VerificationService所有服务bean
*/
@Component
@Slf4j
public class StrategyHandler implements InitializingBean, ApplicationContextAware {
/**
* 存放策略的map,可以理解为策略的注册中心
*/
@Resource
private final Map<String, VerificationService> strategyServiceMap = new ConcurrentHashMap<>(16);
/**
* spring的上下文
*/
private ApplicationContext applicationContext;
@Override
public void afterPropertiesSet() {
//初始化把所有的策略bean放进ioc,使用的时候获取
Map<String, VerificationService> matchBeans = applicationContext.getBeansOfType(VerificationService.class);
//策略注入的bean做key,策略实现类做value
matchBeans.forEach((key, value) -> {
strategyServiceMap.put(value.verificationName(), value);
log.info("项目启动时初始化核销策略模式为: key={},value={}", key, value);
});
}
/**
*
* @param applicationContext spring的上下文
* @throws BeansException 获取bean异常
*/
@Override
public void setApplicationContext(@NotNull(message = "bean不能为空") ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* 通过key获取对应的策略实现
*
* @param verificationName key 核销名称 (String类型或者整形都行,保持和策略接口一致就行)
* @return VerificationService 具体的核销服务
*/
public VerificationService getStrategy(String verificationName) throws RuntimeException{
if (null == strategyServiceMap.get(verificationName)) {
//默认策略
throw new RuntimeException("核销策略异常...");
}
return strategyServiceMap.get(verificationName);
}
}
*初始化项目时直接注册服务,方便调用
调用方便,易扩展,但是实现过程较繁琐,
public class StrategyTest {
@Resource
private StrategyHandler strategyHandler;
@Test
public void test() {
Map<String,Object> result = new HashMap<>();
//1. 获取用户信息
TbUser tbUser = tbUserDao.selectUserByUserId(userId);
if (null == tbUser) {
return null;
}
// 根据receipt_code 和 skuId 查询出需要核销的类型
// 2. 根据不同的核销类型进行核销
String callBackMsg = mtCouponService.getCoinByReceiptCode(receipt_code, userId, "杭州");
Map callBackMap = JSON.parseObject(callBackMsg, Map.class);
if ("200".equals(callBackMap.get("code"))) {
//服务实现类里直接实现具体操作
strategyHandler.getStrategy(callBackMap.get("type")).verify();
result.put("code", "200");
result.put("msg", "核销成功");
return result;
}
result.put("code", "505");
result.put("msg", callBackMap.get("msg"));
return result;
}
四 总结
4.1 优点
- 提高了代码的复用性和可维护性,将算法的定义与其具体实现进行解耦。
- 可以在运行时动态替换算法,提高了程序的灵活性。
- 符合开闭原则,新增算法无需修改现有代码。
4.2 缺点
- 客户端需要知道所有的策略类,并根据具体场景选择合适的策略,增加了客户端的复杂度。
- 如果策略类较多,会导致类的数量增多,增加系统的复杂度。
4.3 适用场景
- 当一个系统中存在多个类只有它们的行为或算法不同时。
- 当一个类定义了多种行为,而这些行为在这个类的操作中以多个条件语句的形式出现,可以将相关的条件分支移入它们各自的策略类中,以替换这些条件语句。
- 当系统需要动态地在几种算法中选择一种时,如根据不同的配置、用户选择或者环境条件等。