1.1. 行为模型:模板方法
设计一个系统时知道了算法所需的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的实现与具体的环境相关 如:去银行办理业务一般要经过以下4个流程:取号、排队、办理具体业务、对银行工作人员进行评分等,其中取号、排队和对银行工作人员进行评分的业务对每个客户是一样的,可以在父类中实现,但是办理具体业务却因人而异,它可能是存款、取款或者转账等,可以延迟到子类中实现
1.1.1. 模式定义与特点
- 该模式的主要优点
- 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
- 它在父类中提取了公共的部分代码,便于代码复用。
- 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。
- 该模式主要缺点
- 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,间接地增加了系统实现的复杂度。
- 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度
- 由于继承关系自身的缺点,如果父类添加新的抽象方法,则所有子类都要改一遍。
1.1.2. 模式的应用实例
- 如下代码,根据业务需求,手机号注册以及账密注册两种方式,注册流程都相同,现业务的区别之处在于存储redis的namespace不同,因此封装了不变部分,扩展可变部分,实现模板方法模式 ```java import com.dadi01.scrm.foundation.model.error.ErrorEnum; import com.dadi01.scrm.foundation.model.exception.ScrmException; import com.dadi01.scrm.foundation.utils.date.DateRelatedUtils; import com.dadi01.scrm.service.member.api.dto.request.AbstractRegisterRq; import com.dadi01.scrm.service.member.provider.domain.MemberDomain; import com.dadi01.scrm.service.member.provider.factory.AuthenticatorFactory; import com.dadi01.scrm.service.member.provider.service.base.IBaseMemberService; import com.dadi01.scrm.service.member.provider.util.CheckModelUtils; import com.dadi01.scrm.service.member.provider.util.KeyGenerateUtil; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Component;
import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.HashMap; import java.util.Map;
/**
@author lviter */ @Component public abstract class RegisterTemplate {
private final AuthenticatorFactory authenticatorFactory; private final IBaseMemberService baseMemberService; private final KeyGenerateUtil keyGenerateUtil;
public RegisterTemplate(AuthenticatorFactory authenticatorFactory, IBaseMemberService baseMemberService, KeyGenerateUtil keyGenerateUtil) {
this.authenticatorFactory = authenticatorFactory; this.baseMemberService = baseMemberService; this.keyGenerateUtil = keyGenerateUtil;
}
/**
- 注册统一模板方法 *
- @param tenantId
- @param account
@param abstractRegisterRq */ public void registerTemplate(Integer tenantId, String account, AbstractRegisterRq abstractRegisterRq) { Long cacheSize = getCache(tenantId, account); if (null == cacheSize || cacheSize <= 0) {
//创建账号 Long id = generateKey(); saveMember(id, abstractRegisterRq); //放入缓存 saveCache(abstractRegisterRq.getTenantId(), account, id);
} else {
registerMemberDomainValidate(cacheSize, tenantId);
}
}
/**
* 生成分布式主键
*
* @return
*/
public Long generateKey() {
return keyGenerateUtil.generate();
}
/**
* 获取缓存内手机号/账号对应的id
*
* @param tenantId
*/
public Long getCache(Integer tenantId, String account) {
return (Long) getLoginUserFromCache(tenantId).get(account);
}
/**
* 结果存入缓存
*
* @param tenantId
* @param account
* @param id
*/
public void saveCache(Integer tenantId, String account, Long id) {
Map<String, Object> cache = new HashMap<>(2);
cache.put(account, id);
saveLoginUserToCache(tenantId, cache);
}
/**
* 从缓存内获取hash集合
*
* @param tenantId
* @return
*/
public abstract Map<Object, Object> getLoginUserFromCache(Integer tenantId);
/**
* 存储缓存
*
* @param tenantId
* @param cache
*/
public abstract void saveLoginUserToCache(Integer tenantId, Map<String, Object> cache);
/**
* 保存会员信息
*
* @param id
* @param abstractRegisterRq
*/
public void saveMember(Long id, AbstractRegisterRq abstractRegisterRq) {
MemberDomain memberDomain = new MemberDomain(id);
BeanUtils.copyProperties(abstractRegisterRq, memberDomain);
CheckModelUtils.checkField(memberDomain);
if (StringUtils.isNotBlank(abstractRegisterRq.getBirth())) {
DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
DateRelatedUtils.convertStringToLongDateString(abstractRegisterRq.getBirth());
try {
memberDomain.setBirth(fmt.parse(abstractRegisterRq.getBirth()));
} catch (ParseException e) {
throw new ScrmException(ErrorEnum.MEMBER_DATE_FORMAT_ERROR.build());
}
}
baseMemberService.save(memberDomain);
}
/**
* 校验账户
*
* @param id
* @param tenantId
*/
public void registerMemberDomainValidate(Long id, Integer tenantId) {
authenticatorFactory.registerMemberDomainValidate(id, tenantId);
}
}
上面代码为抽象类,封装了不变的方法实现
```java
import com.dadi01.scrm.foundation.model.constant.RedisConstant;
import com.dadi01.scrm.service.member.provider.factory.AuthenticatorFactory;
import com.dadi01.scrm.service.member.provider.service.base.IBaseMemberService;
import com.dadi01.scrm.service.member.provider.util.KeyGenerateUtil;
import com.dadi01.scrm.service.member.provider.util.RedisUtil;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author lviter
*/
@Component
public class PasswordRegister extends RegisterTemplate {
private final RedisUtil redisUtils;
public PasswordRegister(AuthenticatorFactory authenticatorFactory, IBaseMemberService baseMemberService, KeyGenerateUtil keyGenerateUtil, RedisUtil redisUtils) {
super(authenticatorFactory, baseMemberService, keyGenerateUtil);
this.redisUtils = redisUtils;
}
/**
* 获取登录账户从缓存中是否存在
*
* @param tenantId
* @return
*/
@Override
public Map<Object, Object> getLoginUserFromCache(Integer tenantId) {
return redisUtils.hmget(RedisConstant.REDIS_PASSWORD_NAMESPACE.concat(String.valueOf(tenantId)));
}
/**
* 保存账户到缓存内
*
* @param tenantId
* @param memberPhoneCache
*/
@Override
public void saveLoginUserToCache(Integer tenantId, Map<String, Object> memberPhoneCache) {
redisUtils.hmset(RedisConstant.REDIS_PASSWORD_NAMESPACE.concat(String.valueOf(tenantId)), memberPhoneCache);
}
}
上面为账密方式注册,重写了可变的方法
import com.dadi01.scrm.foundation.model.constant.RedisConstant;
import com.dadi01.scrm.service.member.provider.factory.AuthenticatorFactory;
import com.dadi01.scrm.service.member.provider.service.base.IBaseMemberService;
import com.dadi01.scrm.service.member.provider.util.KeyGenerateUtil;
import com.dadi01.scrm.service.member.provider.util.RedisUtil;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author lviter
*/
@Component
public class SmsRegister extends RegisterTemplate {
private final RedisUtil redisUtils;
public SmsRegister(AuthenticatorFactory authenticatorFactory, IBaseMemberService baseMemberService, KeyGenerateUtil keyGenerateUtil, RedisUtil redisUtils) {
super(authenticatorFactory, baseMemberService, keyGenerateUtil);
this.redisUtils = redisUtils;
}
@Override
public Map<Object, Object> getLoginUserFromCache(Integer tenantId) {
return redisUtils.hmget(RedisConstant.REDIS_PHONE_NAMESPACE.concat(String.valueOf(tenantId)));
}
@Override
public void saveLoginUserToCache(Integer tenantId, Map<String, Object> cache) {
redisUtils.hmset(RedisConstant.REDIS_PHONE_NAMESPACE.concat(String.valueOf(tenantId)), cache);
}
}
上面为手机号方式注册,重写了可变的方法