JAVA设计模式——策略模式

发布时间 2023-10-11 18:25:35作者: BlogMemory

策略模式是一种行为型设计模式,它允许在运行时选择算法的行为。它定义了一系列算法,将每个算法封装起来并使它们可以相互替换。策略模式使算法的变化独立于使用算法的客户端。

在策略模式中,我们有一个上下文对象,该对象包含一个指向策略对象的引用。策略对象实现了一个公共接口,该接口定义了所有策略对象都必须实现的方法。上下文对象在运行时可以将策略对象替换为另一个策略对象,从而改变其行为。

以下是策略模式的一些关键要素:

  1. 上下文(Context):包含一个指向策略对象的引用。

  2. 策略(Strategy):定义了所有策略对象都必须实现的方法。

  3. 具体策略(Concrete Strategy):实现了策略接口的具体算法。

下面是一个简单的示例,说明如何使用策略模式:

// 策略接口
interface Strategy {
    int doOperation(int num1, int num2);
}

// 具体策略1
class OperationAdd implements Strategy {
    public int doOperation(int num1, int num2) {
        return num1 + num2;
    }
}

// 具体策略2
class OperationSubtract implements Strategy {
    public int doOperation(int num1, int num2) {
        return num1 - num2;
    }
}

// 具体策略3
class OperationMultiply implements Strategy {
    public int doOperation(int num1, int num2) {
        return num1 * num2;
    }
}

// 上下文
class Context {
    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public int executeStrategy(int num1, int num2) {
        return strategy.doOperation(num1, num2);
    }
}

// 测试
public class StrategyPatternDemo {
    public static void main(String[] args) {
        Context context = new Context(new OperationAdd());
        System.out.println("10 + 5 = " + context.executeStrategy(10, 5));

        context = new Context(new OperationSubtract());
        System.out.println("10 - 5 = " + context.executeStrategy(10, 5));

        context = new Context(new OperationMultiply());
        System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
    }
}

在上面的示例中,我们定义了一个策略接口 Strategy,并实现了三个具体策略类 OperationAddOperationSubtractOperationMultiply。我们还定义了一个上下文类 Context,它包含一个指向策略对象的引用,并提供了一个执行策略的方法 executeStrategy。在测试代码中,我们创建了一个上下文对象,并使用不同的策略对象来执行不同的算法。

 

项目实践:使用策略模式,实现接口回调时支持多种回调方式(http、feign调用等)

第一步:定义回调策略

回调接口:


import com.midea.mbf.api.request.CommonReq;

import java.util.List;
import java.util.Map;

/**
 * @since 2023/7/18
 */
public interface CallbackStrategy {

    /**
     * 获取支持的回调类型
     *
     * @return
     */
    String getSupportCallbackType();

    /**
     * 执行回调
     */
    Map<String, Object> executeCallback(String callbackUrl, CommonReq<List<Map<String, Object>>> commonReq);
}

 回调抽象类:


import cn.hutool.core.text.CharSequenceUtil;
import com.*.CallbackConstants;
import com.*.AbstractAccessor;
import com.*.Logger;
import com.*.LoggerFactory;
import com.*.CommonReq;

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

/**
 * @since 2023/7/18
 */
public abstract class AbstractCallbackStrategy implements CallbackStrategy {

    private static final Logger log = LoggerFactory.getLogger(AbstractCallbackStrategy.class);

    /**
     * accessor
     *
     * @return
     */
    protected abstract AbstractAccessor getAccessor();

    /**
     * 执行回调
     */
    @Override
    public Map<String, Object> executeCallback(String callbackUrl, CommonReq<List<Map<String, Object>>> commonReq) {

        Map map = new HashMap();
        try {
            map = getAccessor().defaultPostForValue(callbackUrl, commonReq, Map.class);
        } catch (Exception e) {
            map.put(CallbackConstants.CODE, CallbackConstants.ERROR_CODE);
            map.put(CallbackConstants.MSG, e.getMessage());
            log.error("服务调用异常,异常信息如下:{}", e);
        }
        return map;
    }
}

 feign调用回调策略类:


import com.*.CallbackTypeEnum;
import com.*.utils.AbstractAccessor;
import com.*.FeignAccessor;
import lombok.RequiredArgsConstructor;

import org.springframework.stereotype.Component;

/**
 * @since 2023/7/18
 */
@Component
@RequiredArgsConstructor
public class FeignCallbackStrategy extends AbstractCallbackStrategy implements CallbackStrategy {

    private final FeignAccessor feignAccessor;

    @Override
    public String getSupportCallbackType() {
        return CallbackTypeEnum.FEIGN.getCode();
    }

    @Override
    protected AbstractAccessor getAccessor() {
        return feignAccessor;
    }
}

 http回调策略类:

import com.*.CallbackTypeEnum;
import com.*.AbstractAccessor;
import com.*.HttpAccessor;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

/**
 * @since 2023/7/18
 */
@Component
@RequiredArgsConstructor
public class HttpCallbackStrategy extends AbstractCallbackStrategy implements CallbackStrategy {

    private final HttpAccessor httpAccessor;

    @Override
    public String getSupportCallbackType() {
        return CallbackTypeEnum.HTTP.getCode();
    }

    @Override
    protected AbstractAccessor getAccessor() {
        return httpAccessor;
    }
}

 

第二步:定义访问方式抽象类

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.midea.logging.Logger;
import com.midea.logging.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

/**
 * @since 2023/7/18
 */
public abstract class AbstractAccessor {

    private static final Logger LOG = LoggerFactory.getLogger(AbstractAccessor.class);

    /**
     * <pre>
     * 远程调用微服务,返回ApiResponse对象
     * 默认的数据请求格式为application/json
     * </pre>
     *
     * @param url   微服务地址
     * @param param 请求参数
     * @param clazz 返回类型
     */
    public <T> T defaultPostForValue(String url, Object param, Class<T> clazz) {
        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        HttpEntity<?> entity = new HttpEntity<>(param, headers);
        return postForValue(url, entity, clazz);
    }

    /**
     * <pre>
     * 远程调用微服务,返回ApiResponse对象
     * 默认的数据请求格式为application/json
     * </pre>
     *
     * @param url   微服务地址
     * @param param 请求参数
     * @param clazz 返回类型
     */
    public <T> ResponseEntity<T> defaultPostForEntity(String url, Object param, Class<T> clazz) {
        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        HttpEntity<?> entity = new HttpEntity<>(param, headers);
        return postForEntity(url, entity, clazz);
    }

    /**
     * <pre>
     * 远程调用微服务,返回ApiResponse后的Data值对象
     * </pre>
     *
     * @param url    微服务地址
     * @param entity 请求实体
     * @param clazz  返回类型
     */
    public <T, R> T postForValue(String url, HttpEntity<R> entity, Class<T> clazz) {
        ResponseEntity<T> responseEntity = this.postForEntity(url, entity, clazz);
        return responseEntity.getBody();
    }

    /**
     * <pre>
     * 远程调用微服务,返回ApiResponse后的Data值对象
     * </pre>
     *
     * @param url    微服务地址
     * @param entity 请求实体
     * @param clazz  返回类型
     */
    public <T, R> ResponseEntity<T> postForEntity(String url, HttpEntity<R> entity, Class<T> clazz) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("远程地址:{},请求参数:{}", url, JSON.toJSONString(entity.getBody(),
                    SerializerFeature.WRITE_MAP_NULL_FEATURES, SerializerFeature.QuoteFieldNames));
        }
        ResponseEntity<T> responseEntity = getRestTemplate().exchange(url, HttpMethod.POST, entity, clazz);
        if (LOG.isDebugEnabled()) {
            LOG.debug("远程地址:{},返回结果:{}", url, JSON.toJSONString(responseEntity.getBody(),
                    SerializerFeature.WRITE_MAP_NULL_FEATURES, SerializerFeature.QuoteFieldNames));
        }
        return responseEntity;
    }

    /**
     * <pre>
     * 远程调用微服务,返回ApiResponse对象
     * 默认的数据请求格式为application/json
     * </pre>
     *
     * @param url   微服务地址
     * @param param 请求参数
     * @param clazz 返回类型
     */
    public <T> T defaultGetForValue(String url, Object param, Class<T> clazz) {
        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        HttpEntity<?> entity = new HttpEntity<>(param, headers);
        return getForValue(url, entity, clazz);
    }

    /**
     * <pre>
     * 远程调用微服务,返回ApiResponse对象
     * 默认的数据请求格式为application/json
     * </pre>
     *
     * @param url   微服务地址
     * @param param 请求参数
     * @param clazz 返回类型
     */
    public <T> ResponseEntity<T> defaultGetForEntity(String url, Object param, Class<T> clazz) {
        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        HttpEntity<?> entity = new HttpEntity<>(param, headers);
        return getForEntity(url, entity, clazz);
    }

    /**
     * <pre>
     * 远程调用微服务,返回ApiResponse后的Data值对象
     * </pre>
     *
     * @param url    微服务地址
     * @param entity 请求实体
     * @param clazz  返回类型
     */
    public <T, R> T getForValue(String url, HttpEntity<R> entity, Class<T> clazz) {
        ResponseEntity<T> responseEntity = this.getForEntity(url, entity, clazz);
        return responseEntity.getBody();
    }

    /**
     * <pre>
     * 远程调用微服务,返回ApiResponse后的Data值对象
     * </pre>
     *
     * @param url    微服务地址
     * @param entity 请求实体
     * @param clazz  返回类型
     */
    public <T, R> ResponseEntity<T> getForEntity(String url, HttpEntity<R> entity, Class<T> clazz) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("远程地址:{},请求参数:{}", url, JSON.toJSONString(entity.getBody(),
                    SerializerFeature.WRITE_MAP_NULL_FEATURES, SerializerFeature.QuoteFieldNames));
        }
        ResponseEntity<T> responseEntity = getRestTemplate().exchange(url, HttpMethod.GET, entity, clazz);
        if (LOG.isDebugEnabled()) {
            LOG.debug("远程地址:{},返回结果:{}", url, JSON.toJSONString(responseEntity.getBody(),
                    SerializerFeature.WRITE_MAP_NULL_FEATURES, SerializerFeature.QuoteFieldNames));
        }
        return responseEntity;
    }

    protected abstract RestTemplate getRestTemplate();
}

 feign调用方式:


import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

/**
 * @since 2023/7/18
 */
@Component
public class FeignAccessor extends AbstractAccessor {

    @Resource(name = "feignRestTemplate")
    private RestTemplate feignRestTemplate;

    @Override
    protected RestTemplate getRestTemplate() {
        return feignRestTemplate;
    }
}

 http调用方式:


import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

/**
 * @author huangdh
 * @since 2023/7/18
 */
@Component
public class HttpAccessor extends AbstractAccessor {

    @Resource(name = "httpRestTemplate")
    private RestTemplate httpRestTemplate;

    @Override
    protected RestTemplate getRestTemplate() {
        return httpRestTemplate;
    }
}

 

 接口回调:

/**
 * @since 2023/7/17
 */
@Service
@RequiredArgsConstructor
public class CallbackAppServiceImpl implements CallbackAppService {

    private final List<CallbackStrategy> callbackStrategyList;

    /**
     * 回调明细操作
     *
     * @param batchId
     * @param serialNumber
     * @param interfaceCode
     */
    private boolean executeCallbackDetail(Long batchId, String serialNumber, String interfaceCode) {
       
            // 通过 interfaceCode 获取回调地址,并返回List<BaseCallbackConfigBO>
            BaseCallbackConfigBO callbackConfigBO = callbackConfigDomainService.getCallbackUrlByInterfaceCode(interfaceCode);
            String callbackType = callbackConfigBO.getCallbackType();
            String callbackUrl = callbackConfigBO.getCallbackServiceName();
            // 构造回调参数
            List<Map<String, Object>> maps = boList.stream().map(obj -> {
                Map<String, Object> map = new HashMap<>();
                map.put("callBackCode", obj.getProcessStatus());
                map.put("callBackMsg", obj.getProcessMessage());
                return map;
            }).collect(Collectors.toList());
            CommonReq<List<Map<String, Object>>> commonReq = CommonReq.build(maps);

            Map<String, Object> map = callbackStrategyList.stream().filter(obj -> callbackType.equals(obj.getSupportCallbackType())).findFirst().get().executeCallback(callbackUrl, commonReq);
            return true;
    }
}