策略模式
业务场景:商城系统场景中,有多种会员实现不同的营销策略。
业务枚举类
@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);
}
}
方式四通过注解灵活组合场景,几乎可以解决任何场景下涉及到策略复杂的问题。