设计模式--行为模式--模板方法
行为模型:模板方法
设计一个系统时知道了算法所需的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的实现与具体的环境相关 如:去银行办理业务一般要经过以下4个流程:取号、排队、办理具体业务、对银行工作人员进行评分等,其中取号、排队和对银行工作人员进行评分的业务对每个客户是一样的,可以在父类中实现,但是办理具体业务却因人而异,它可能是存款、取款或者转账等,可以延迟到子类中实现
模式定义与特点
- 该模式的主要优点
- 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
- 它在父类中提取了公共的部分代码,便于代码复用。
- 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。
- 该模式主要缺点
- 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,间接地增加了系统实现的复杂度。
- 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度
- 由于继承关系自身的缺点,如果父类添加新的抽象方法,则所有子类都要改一遍。
模式的应用实例
- 如下代码,根据业务需求,手机号注册以及账密注册两种方式,注册流程都相同,现业务的区别之处在于存储redis的namespace不同,因此封装了不变部分,扩展可变部分,实现模板方法模式
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); } }
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); } }
llh