1.1. 行为模型:模板方法

设计一个系统时知道了算法所需的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的实现与具体的环境相关 如:去银行办理业务一般要经过以下4个流程:取号、排队、办理具体业务、对银行工作人员进行评分等,其中取号、排队和对银行工作人员进行评分的业务对每个客户是一样的,可以在父类中实现,但是办理具体业务却因人而异,它可能是存款、取款或者转账等,可以延迟到子类中实现

1.1.1. 模式定义与特点

  • 该模式的主要优点
    1. 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
    2. 它在父类中提取了公共的部分代码,便于代码复用。
    3. 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。
  • 该模式主要缺点
    1. 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,间接地增加了系统实现的复杂度。
    2. 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度
    3. 由于继承关系自身的缺点,如果父类添加新的抽象方法,则所有子类都要改一遍。

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);
    }

}

上面为手机号方式注册,重写了可变的方法

Copyright & copy lviter@163.com            updated 2024-02-06 09:54:55

results matching ""

    No results matching ""