设计模式: 策略模式

一、什么是策略模式

策略模式是一种行为型设计模式,它允许定义一组算法,并将每个算法封装在独立的类中,使它们可以互相替换。策略模式通过将算法的使用与算法的实现分离,使得算法可以独立于客户端而变化。

  • 在策略模式中,通常会有一个上下文(Context)类,该类包含一个策略接口(Strategy),以及具体的策略类(Concrete Strategies)。上下文类将具体的任务委托给策略接口,在运行时可以根据需要切换不同的策略类,从而达到动态改变算法行为的目的。
  • 策略模式的核心思想是面向接口编程,而不是面向实现编程。这种设计模式提供了一种灵活的方式来管理和复用算法,同时使得算法的扩展变得更加容易。它适用于需要根据不同情况选择不同算法的场景,例如排序、搜索、计算等问题。

总结起来,策略模式通过将算法封装成独立的类,使得可以在运行时动态地选择和切换算法,从而提高代码的可维护性、扩展性和复用性。

二、策略模式结构

在这里插入图片描述

三、使用场景+案例分析

1、使用场景

策略模式通常适用于以下场景:

  • 多算法选择:当需要在运行时根据情况选择不同的算法时,可以使用策略模式。例如,对于排序算法,根据数据量的不同可能选择快速排序、冒泡排序或插入排序等。

  • 消除条件分支:当代码中存在大量的条件分支语句,并且这些条件分支都是根据相同的输入来选择不同的行为时,可以考虑使用策略模式来消除这些条件分支。

  • 算法的封装和复用:当系统中存在多个类似的算法,但它们的实现细节不同,可以将这些算法封装成独立的策略类,以便复用和维护。

  • 可扩展性:当需要为系统提供一种灵活、可拓展的方式来添加新的算法或行为时,策略模式可以帮助实现这一点,而无需修改现有的代码。

  • 单一职责原则:当需要遵循单一职责原则,即每个类应该只负责一种功能时,策略模式可以将不同的算法分离到单独的策略类中,使得每个类都专注于一种算法。

总的来说,策略模式适用于需要动态地切换算法、消除条件分支、提高可维护性和可扩展性的场景。通过策略模式,可以更好地管理和组织算法,使系统更加灵活和易于维护。

2、案例分析

(1)消除条件分支

在元数据管理系统中有可视化建表的功能,在向底层建表的过程中不同类型的数据源有不同的分区形式,原有代码的处理形式是使用if语句判断数据源类型使用对应的方式对分区进行处理。每次新接进来的数据源如果有分区的特性需要需要if语句进行处理,这样的处理方式并不符合开闭原则(对扩展开放,对修改关闭)。现在我们基于BeanPostProcessor+注解+注册管理器形式的策略模式来取消条件分支。

  • 策略(strategy)
public interface IConvertPartition {
    void processPartition(ProcessParam param);
    void partitionParams(PartitionParam param);
}
  • 具体策略(concrete strategy)
    不同的数据源有不同的实现形式,仅仅举例一个数据源的具体策略:
@ConvertPartition(tableType = TableType.KINGBASE_ES, dataType = DataType.KINGBASE_ES)
@Slf4j
public class KingbaseConverPartition implements IConvertPartition {
    @Override
    public void processPartition(ProcessParam param) {
        //具体的分区处理逻辑
    }
    @Override
    public void partitionParams(PartitionParam param){
    //具体的分区处理逻辑
    }
}    
  • 上下文(context)
import com.alibaba.excel.util.CollectionUtils;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
@Slf4j
public class ConvertDraftPartitionUtilsContext {
    /**
     * partition转换 处理器
     */
    private static final Map<TableType, IConvertPartition>  CONVERT_DRAFT_UTILS_MAP = new HashMap<>();
    private static final Map<DataType, IConvertPartition> TABLE_PUBLISH_DATATYPE_MAP = new HashMap<>();

    public static <T> void register(T dataType, IConvertPartition convertPartition) {
        if (dataType instanceof TableType) {
            CONVERT_DRAFT_UTILS_MAP.putIfAbsent((TableType)dataType, convertDraftPartition);
        } else if (dataType instanceof DataType) {
            TABLE_PUBLISH_DATATYPE_MAP.put((DataType) dataType, convertDraftPartition);
        }

    }

    public static IConvertPartition getIConvertPartition(TableType  dataType) {
        return CONVERT_DRAFT_UTILS_MAP.get(dataType);
    }
    public static Set<TableType> getTableTypes() {
        Set<TableType> tableTypes = CONVERT_DRAFT_UTILS_MAP.keySet();
        log.info("convert draft partition table type list: {}", tableTypes);
        if (CollectionUtils.isEmpty(tableTypes)) {
            return Collections.emptySet();
        }
        return tableTypes;
    }

    public static void processPartition(TableType  dataType, ProcessParam param) {
        IConvertPartition iConvertPartition = getIConvertPartition(dataType);
        if (!Objects.isNull(iConvertPartition)) {
            log.info("{} start process partition", dataType.getCode());
            iConvertPartition.processPartition(param);
        }
    }

    public static void processParam(DataType  dataType, PartitionParam param) {
        IConvertPartition iConvertPartition = TABLE_PUBLISH_DATATYPE_MAP.get(dataType);
        if (!Objects.isNull(iConvertPartition)) {
            log.info("{} start process partition params", dataType.getDataTypeName());
            iConvertPartition.partitionParams(param);
        }
    }

    public static Set<TableType> getTableTypeWithPartitions() {
        log.info("The type of a table with partitions: {}", CONVERT_DRAFT_UTILS_MAP.keySet());
        return CONVERT_DRAFT_UTILS_MAP.keySet();
    }

}

  • 注解
@Component
@Inherited
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ConvertPartition {
    DataType dataType();
    TableType tableType();
}
  • BeanPostProcessor
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

import java.util.Objects;
@Component
@Slf4j
public class ConvertPartitionRegisterProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof IConvertPartition) {
            Class<?> aClass = bean.getClass();
            log.info("the convertDraftPartition of [ {} ] begin to register", aClass.getSimpleName());
            ConvertPartition annotation = aClass.getAnnotation(ConvertPartition.class);
            if (Objects.isNull(annotation)) {
                return bean;
            }
            DataType dataType = annotation.dataType();
            TableType tableType = annotation.tableType();
            registerHandler(aClass, bean, dataType, tableType);
        }
        return bean;
    }

    private void registerHandler(Class<?> aClass, Object bean, Object... dataTypes){
        if (ArrayUtils.isNotEmpty(dataTypes)) {
            for (Object dataType : dataTypes) {
                if (!Objects.isNull(dataType)) {
                    ConvertPartitionUtilsContext.register(dataType, (IConvertPartition)bean);
                    log.info("the [ {} ] register success", aClass.getSimpleName());
                }
            }
        }
    }
}
  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

玉成226

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值