设计模式——策略模式

业务场景:商城系统场景中,有多种会员实现不同的营销策略。

业务枚举类

@Getter
@AllArgsConstructor
public enum VipType {
    /**
     * 会员类型
     */
    LEVEL_VIP("等级会员", "LEVEL_VIP"),
    PAY_VIP("付费会员", "PAY_VIP"),
    TEMP_VIP("临时会员", "TEMP_VIP");

    private final String name;
    private final String type;

}

接下来介绍几种开发中常见的实现策略模式方式,仅供参考。

方式一:接口+构造注入

/**
 * 会员活动接口
 *
 * @author Yeah
 * @date 2024/10/30 19:37
 **/
public interface VipActivity {

    /**
     * 会员活动
     */
    void activity();

    /**
     * 获取会员类型
     *
     * @return 会员类型
     */
    VipType getType();
}

/**
 * 等级会员活动
 *
 * @author Yeah
 * @date 2024/10/30 19:39
 **/
@Component
public class LevelVipActivity implements VipActivity {

    @Override
    public void activity() {
        System.out.println("等级会员活动");
    }

    @Override
    public VipType getType() {
        return VipType.LEVEL_VIP;
    }
}

/**
 * 付费会员活动
 *
 * @author Yeah
 * @date 2024/10/30 19:40
 **/
@Component
public class PayVipActivity implements VipActivity {
    @Override
    public void activity() {
        System.out.println("付费会员活动");
    }

    @Override
    public VipType getType() {
        return VipType.PAY_VIP;
    }
}

/**
 * 临时会员活动
 *
 * @author Yeah
 * @date 2024/10/30 19:42
 **/
@Component
public class TempVipActivity implements VipActivity {
    @Override
    public void activity() {
        System.out.println("临时会员活动");
    }

    @Override
    public VipType getType() {
        return VipType.TEMP_VIP;
    }
}

/**
 * 会员活动策略工厂
 *
 * @author Yeah
 * @date 2024/10/30 19:43
 **/
@Component
public class VipActivityStrategyFactory {

    private static final Map<VipType, VipActivity> VIP_ACTIVITY_MAP = new ConcurrentHashMap<>(16);

    @Autowired
    public VipActivityStrategyFactory(List<VipActivity> vipActivities) {
        vipActivities.forEach(vipActivity -> VIP_ACTIVITY_MAP.put(vipActivity.getType(), vipActivity));
    }


    public VipActivity getVipActivity(VipType vipType) {
        VipActivity vipActivity = VIP_ACTIVITY_MAP.get(vipType);
        if (vipActivity == null) {
            throw new RuntimeException("未找到对应的会员类型");
        }
        return vipActivity;
    }
}

方式二:抽象类实现InitializingBean接口

/**
 * 抽象会员活动策略类
 *
 * @author Yeah
 * @date 2024/10/30 20:21
 **/
public abstract class AbstractVipActivity implements InitializingBean {

    /**
     * 会员活动
     */
    public abstract void activity();

    /**
     * 获取会员类型
     *
     * @return 会员类型
     */
    public abstract VipType getType();


    /**
     * 初始化
     *
     * @throws Exception 异常
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        VipActivityStrategyFactory.registerVipActivity(this);
    }
}


/**
 * 等级会员活动
 *
 * @author Yeah
 * @date 2024/10/30 19:39
 **/
@Component
public class LevelVipActivity extends AbstractVipActivity {

    @Override
    public void activity() {
        System.out.println("等级会员活动");
    }

    @Override
    public VipType getType() {
        return VipType.LEVEL_VIP;
    }
}


/**
 * 付费会员活动
 *
 * @author Yeah
 * @date 2024/10/30 19:40
 **/
@Component
public class PayVipActivity extends AbstractVipActivity {
    @Override
    public void activity() {
        System.out.println("付费会员活动");
    }

    @Override
    public VipType getType() {
        return VipType.PAY_VIP;
    }
}


/**
 * 临时会员活动
 *
 * @author Yeah
 * @date 2024/10/30 19:42
 **/
@Component
public class TempVipActivity extends AbstractVipActivity {
    @Override
    public void activity() {
        System.out.println("临时会员活动");
    }

    @Override
    public VipType getType() {
        return VipType.TEMP_VIP;
    }
}


/**
 * 会员活动策略工厂
 *
 * @author Yeah
 * @date 2024/10/30 19:43
 **/
public class VipActivityStrategyFactory {

    private static final Map<VipType, AbstractVipActivity> VIP_ACTIVITY_MAP = new ConcurrentHashMap<>(16);

    /**
     * 注册会员活动
     *
     * @param activity 会员活动
     */
    public static void registerVipActivity(AbstractVipActivity activity) {
        VIP_ACTIVITY_MAP.put(activity.getType(), activity);
    }

    /**
     * 获取会员活动
     *
     * @param vipType 会员类型
     * @return 会员活动
     */
    public static AbstractVipActivity getVipActivity(VipType vipType) {
        return VIP_ACTIVITY_MAP.get(vipType);
    }
}

方式三:继承Spring插件Plugin< S >

/**
 * 会员活动接口
 *
 * @author Yeah
 * @date 2024/10/30 20:21
 **/
public interface VipActivity extends Plugin<VipType> {

    /**
     * 会员活动
     */
    void activity();
}

/**
 * 等级会员活动
 *
 * @author Yeah
 * @date 2024/10/30 19:39
 **/
@Component
public class LevelVipActivity implements VipActivity {

    @Override
    public void activity() {
        System.out.println("等级会员活动");
    }


    @Override
    public boolean supports(@NotNull VipType vipType) {
        return VipType.LEVEL_VIP.equals(vipType);
    }
}


/**
 * 付费会员活动
 *
 * @author Yeah
 * @date 2024/10/30 19:40
 **/
@Component
public class PayVipActivity implements VipActivity {
    @Override
    public void activity() {
        System.out.println("付费会员活动");
    }

    @Override
    public boolean supports(@NotNull VipType vipType) {
        return VipType.PAY_VIP.equals(vipType);
    }

}


/**
 * 临时会员活动
 *
 * @author Yeah
 * @date 2024/10/30 19:42
 **/
@Component
public class TempVipActivity implements VipActivity {
    @Override
    public void activity() {
        System.out.println("临时会员活动");
    }

    @Override
    public boolean supports(@NotNull VipType vipType) {
        return VipType.TEMP_VIP.equals(vipType);
    }
}


/**
 * 会员活动策略工厂
 *
 * @author Yeah
 * @date 2024/10/30 19:43
 **/
@Component
@EnablePluginRegistries(VipActivity.class)
public class VipActivityStrategyFactory {

    @Resource
    private PluginRegistry<VipActivity, VipType> pluginRegistry;

    /**
     * 获取会员活动策略
     *
     * @param vipType 会员类型
     * @return 会员活动策略
     */
    public VipActivity getVipActivity(VipType vipType) {
        return pluginRegistry.getPluginFor(vipType)
                .orElseThrow(() -> new RuntimeException("未找到对应的会员活动策略"));
    }
}

以上方法适用于单一场景,不过在日常开发中已经够用了。但是如果现在业务需求更改,不仅有会员类型还有商城类型。比如:京东商城、小米商城、天猫商城分别实现不同的会员营销活动,该怎么处理呢?这里不卖关子了,请诸君接着往下看。

方式四:自定义注解+实现BeanPostProcessor

/**
 * 商城类型枚举类
 *
 * @author Yeah
 * @date 2024/10/30 21:18
 **/
@Getter
@AllArgsConstructor
public enum MallType {

    /**
     * 商城类型
     */
    JD("京东商城", "JD"),
    MI("小米商城", "MI"),
    TM("天猫商城", "TM");

    private final String name;
    private final String type;
}


/**
 * 活动处理器注解
 *
 * @author Yeah
 * @date 2024/10/30 21:21
 **/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Component
public @interface ActivityProcess {

    VipType[] vipType();

    MallType[] mallType() default {};
}

/**
 * 活动上下文
 *
 * @author Yeah
 * @date 2024/10/30 22:12
 **/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ActivityContext<C> {

    /**
     * 会员类型
     */
    private VipType vipType;

    /**
     * 商城类型
     */
    private MallType mallType;

    /**
     * 活动上下文
     */
    private C context;
}


/**
 * 抽象活动类
 *
 * @author Yeah
 * @date 2024/10/30 20:21
 **/
public abstract class AbstractVipActivity {

    /**
     * 会员活动
     */
    public abstract void activity();
}


/**
 * 等级会员活动
 *
 * @author Yeah
 * @date 2024/10/30 19:39
 **/
@ActivityProcess(vipType = {VipType.LEVEL_VIP, VipType.PAY_VIP,VipType.TEMP_VIP},mallType = MallType.JD)
public class LevelVipActivity extends AbstractVipActivity {

    @Override
    public void activity() {
        System.out.println("京东商城会员活动");
    }
}


/**
 * 付费会员活动
 *
 * @author Yeah
 * @date 2024/10/30 19:40
 **/
@ActivityProcess(vipType = VipType.PAY_VIP, mallType = {MallType.MI, MallType.TM})
public class PayVipActivity extends AbstractVipActivity {
    @Override
    public void activity() {
        System.out.println("小米商城、天猫商城付费会员活动");
    }
}


/**
 * 临时会员活动
 *
 * @author Yeah
 * @date 2024/10/30 19:42
 **/
@ActivityProcess(vipType = VipType.TEMP_VIP)
public class TempVipActivity extends AbstractVipActivity {
    @Override
    public void activity() {
        System.out.println("临时会员活动");
    }
}



/**
 * @author Yeah
 * @date 2024/10/30 21:38
 **/
@Component
public class ActivityProcessRegistry implements BeanPostProcessor {

    private static final String DEFAULT_FLAG = "#";

    private static final Map<String, AbstractVipActivity> ACTIVITY_PROCESS_MAP = new ConcurrentHashMap<>(16);


    @Override
    public Object postProcessAfterInitialization(@NotNull Object bean, @NotNull String beanName) throws BeansException {
        if (bean instanceof AbstractVipActivity && bean.getClass().isAnnotationPresent(ActivityProcess.class)) {
            ActivityProcess annotation = bean.getClass().getAnnotation(ActivityProcess.class);
            List<String> vipTypes = Arrays.stream(annotation.vipType()).map(VipType::toString).collect(Collectors.toList());
            List<String> mallTypes = annotation.mallType().length == 0 ? Collections.singletonList(DEFAULT_FLAG) : Arrays.stream(annotation.mallType()).map(MallType::toString).collect(Collectors.toList());
            initProcessMap(vipTypes, mallTypes, (AbstractVipActivity) bean);
        }
        return bean;
    }

    private void initProcessMap(List<String> vipTypes, List<String> mallTypes, AbstractVipActivity bean) {
        vipTypes.forEach(vipType ->
                mallTypes.forEach(mallType ->
                        registerHandlers(vipType, mallType, bean)
                ));
    }

    private void registerHandlers(String vipType, String mallType, AbstractVipActivity bean) {
        String key = key(vipType, mallType);
        if (ACTIVITY_PROCESS_MAP.get(key) != null) {
            throw new RuntimeException("ActivityProcess type of " + key + " has bean exist!");
        }
        ACTIVITY_PROCESS_MAP.put(key, bean);
    }

    private String key(String vipType, String mallType) {
        return vipType.concat(mallType);
    }


    public AbstractVipActivity getActivityProcessor(ActivityContext<?> context) {
        String vipType = context.getVipType().toString();
        String mallType = DEFAULT_FLAG;
        if (context.getMallType() != null) {
            mallType = context.getMallType().toString();
        }
        AbstractVipActivity supportProcessor = getSupportProcessor(vipType, mallType);
        if (supportProcessor == null) {
            throw new RuntimeException("未找到对应的处理器");
        }
        return supportProcessor;
    }

    private AbstractVipActivity getSupportProcessor(String vipType, String mallType) {
        return ACTIVITY_PROCESS_MAP.getOrDefault(key(vipType, mallType), null);
    }

}

方式四通过注解灵活组合场景,几乎可以解决任何场景下涉及到策略复杂的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值