设计模式在项目中是如何用的?模板方法模式篇(Template Method Pattern)

一、模板方法模式是什么?有哪些应用场景?

  • 所谓模板方法模式,就是定义一个操作中的算法的骨架(这个将具体逻辑步骤汇总起来的方法就是所谓的模板方法),而将一些步骤延迟到子类中去实现
  • 模板方法模式是类的行为模式,它的应用场景是同一个接口的不同实现类存在公共代码,这样就可以用模板方法模式将不变部分进行实现、封装在模板类中,将可变部分作为抽象方法留给子类扩展,从而复用顶级逻辑的代码。

二、模板方法模式的实现

  • 模板方法模式的实现,关键代码是定义一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现
  • 为防止恶意操作,一般模板方法都加上 final 关键词,可以避免子类重写模板方法

三、模板方法模式的应用举例

  1. 使用模板方法模式统一调用外部服务接口
    统一接口调用
import com.success.uniformApiInvoker.req.OuterBaseRequest;
import com.success.uniformApiInvoker.res.OuterBaseResponse;
import com.success.util.HttpClientUtil;

/**
 * @Title:调用外部服务基类
 * @Author:wangchenggong
 * @Date 2020/9/25 18:42
 * @Description
 * @Version
 */
public abstract class AbstractInvokeOuterSystemService<T extends OuterBaseRequest,R extends OuterBaseResponse> implements InvokeOuterSystemService<T,R>{


    @Override
    public R request(T t) throws Exception {
        //1. 创建请求报文(不用的服务接口可能要进行的入参校验等,需要子类实现)
        String reqMessage = this.createMessage(t);
        //2. 发送请求报文(这个步骤可以共用)
        String respMessage = HttpClientUtil.doPostJson("http://url", reqMessage);
        //3. 将响应报文解析为响应信息对象(不用的服务接口需要响应不同的信息,需要子类实现)
        R resp = this.parseResponse(respMessage);
        return resp;
    }

    protected abstract String createMessage(T t);

    protected abstract R parseResponse(String responseMsg);

}
  1. 使用模板方法模式对外提供的统一接口服务

对外提供统一接口

import com.alibaba.fastjson.JSONObject;
import com.success.uniformApiProvider.req.BaseRequest;

/**
 * @Title:对外提供的统一接口服务基类
 * @Author:wangchenggong
 * @Date 2020/9/26 8:12
 * @Description   JSONObject是用来明确FacadeApiService<T>中的泛型类型,而AbstractFacadeApiService<T extends BaseRequest,R>中的泛型类型需要子类明确
 * @Version
 */
public abstract class AbstractFacadeApiService<T extends BaseRequest,R> implements FacadeApiService<JSONObject>{


    /**
     * 处理业务请求
     * @param requestMessage 请求报文对象(这个请求报文对象需要在Controller中对请求json报文做初步的解析,才可以得到)
     * @return
     */
    @Override
    public String handle(RequestMessage<JSONObject> requestMessage) {
        //这里可以做一些入参校验、验签等,主要针对请求头信息

        //1. 解析请求体信息
        T t = this.parseJSONObject(requestMessage.getData());
        //2. 执行业务逻辑(不同的服务接口处理逻辑不同,所以)
        ResponseMessage<R> responseMessage = this.doHandle(t);
        //3.将响应信息序列化为json串(此步骤可以共用)
        String responseMsg = JSONObject.toJSONString(responseMessage);
        return responseMsg;
    }

    protected abstract ResponseMessage<R> doHandle(T req);

    protected abstract T parseJSONObject(JSONObject jsonObject);


}
  1. 使用模板方法模式实现通知服务
    通知消费方业务处理结果
import com.alibaba.fastjson.JSON;
import com.success.notice.vo.NoticeMessage;
import com.success.notice.vo.NoticeResult;
import com.success.util.HttpClientUtil;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;

/**
 * @Title:通知服务基类
 * @Author:wangchenggong
 * @Date 2020/9/27 13:41
 * @Description
 * @Version
 */
public abstract class AbstractNoticeService  implements NoticeService<NoticeResult>{

    /**
     * 模拟数据字典中 不同系统的通知地址
     */
    private static Map<String,String> noticeUrlMap = new HashMap<>();
    static {
        noticeUrlMap.put("aaa","http://xxx/aaa/");
        noticeUrlMap.put("bbb","http://xxx/bbb/");
    }

    @Override
    public NoticeResult notice(NoticeMessage noticeMessage) throws Exception {

        NoticeResult noticeResult = null;

        //1.获取通知地址(不同系统的通知地址一般可以配置在数据字典中,可以从数据字典中获取)
        String noticeUrl = noticeUrlMap.get(noticeMessage.getSystemCode());

        //2.构建通知内容
        String noticeContent = this.buildNoticeContent(noticeMessage.getUniqueNo());

        //3.发送通知内容
        String result = HttpClientUtil.doPostJson(noticeUrl, noticeContent);
        if(!StringUtils.isEmpty(result)){
            try{
                noticeResult = JSON.parseObject(result, NoticeResult.class);
            }catch (Exception e){
                noticeResult = new NoticeResult("9001", "The result of notice is empty!");
            }
        }else{
            noticeResult = new NoticeResult("9001", "The noticeUrl is empty!");
        }

        //4.更新通知状态
        this.updateNoticeStatus(noticeResult, noticeMessage);
        return  noticeResult;
    }

    /**
     * 更新通知状态
     * @param noticeResult 通知结果
     * @param noticeMessage 通知的消息信息
     */
    protected abstract void updateNoticeStatus(NoticeResult noticeResult, NoticeMessage noticeMessage);

    /**
     * 根据通知消息ID构建消息通知内容
     * @param uniqueNo
     * @return
     */
    protected abstract String buildNoticeContent(String uniqueNo);
}

  1. 使用模板方法模式实现数据访问接口
    数据访问接口
import com.success.dataAccess.dao.BaseMapper;

import java.util.List;

/**
 * @Title:数据操作服务基类(继承了BaseService中的泛型类型)
 * @Author:wangchenggong
 * @Date 2020/9/26 22:42
 * @Description 增删改查的实现需要两步:1.获取对应的Mapper 2.调用Mapper对应的方法实现增删改查
 * @Version
 */
public abstract class AbstractService<K,T> implements BaseService<K,T>{

    protected abstract BaseMapper<K,T> getBaseMapper();


    @Override
    public T selectByPrimaryKey(K k){
        return (T) getBaseMapper().selectByPrimaryKey(k);
    }

    @Override
    public List<T> selectList(T t){
        return getBaseMapper().selectList(t);
    }


    @Override
    public int deleteByPrimaryKey(K k){
        return getBaseMapper().deleteByPrimaryKey(k);
    }

    @Override
    public int insert(T t){
        return getBaseMapper().insert(t);
    }

    @Override
    public int updateByPrimaryKeySelective(T t){
        return getBaseMapper().updateByPrimaryKeySelective(t);
    }

}

模版类已经实现了父接口中的抽象方法,子类只需要继承模板类,专注实现子接口中定义的抽象方法即可

import com.success.dataAccess.bean.AccountInfo;
import com.success.dataAccess.dao.BaseMapper;
import com.success.dataAccess.service.AbstractService;
import com.success.dataAccess.service.AccountInfoService;
import org.springframework.stereotype.Service;

/**
 * @Title:账户信息服务接口实现类
 * @Author:wangchenggong
 * @Date 2020/9/27 10:18
 * @Description
 * @Version
 */
@Service
public class AccountInfoServiceImpl extends AbstractService<Long, AccountInfo> implements AccountInfoService {
    //这里需要注入AccountInfoMapper

    @Override
    protected BaseMapper<Long, AccountInfo> getBaseMapper() {
        return null;
    }
}

  1. 使用模板方法模式实现redis分布式锁
    redis分布式锁
        // 获取账户锁
        long lockId = rCustomerAccountLock.nextId();
        String customerIdStr = String.valueOf(myCustomerId);
        if (!rCustomerAccountLock.tryLock(customerIdStr, lockId, tryTimeout, TimeUnit.MILLISECONDS)) {
            throw new KnowledgeException("该账户已经被锁定,请稍后");
        }
        try {
            // 具体业务处理
            ...
        } finally {
            rCustomerAccountLock.unlock(customerIdStr, lockId);
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值