Java 设计模式—策略模式简单使用

怎么理解策略模式?

当一个需求有很多不同方法实现时就可以采取策略模式。策略模式是一种设计模式,定义了一组算法,并将每个算法封装起来,使它们可以互换使用。策略模式让算法的变化独立于使用算法的客户。

在策略模式中,有一个上下文类负责使用策略类。策略类定义了算法的接口,而具体的算法实现则由具体的策略类来实现。这样,上下文类就可以在运行时根

总结为三部分组成:

  • 顶层抽象接口:公共业务方法抽取,条件方法抽取
  • 策略实现类:每个业务有独立的一套算法支持
  • 策略上下文管理类:用来收集策略类,调用策略类等

案例演示

需求:我们都知道 SpringMVC 中的 Controller 参数有很多不同类型注解修饰,每个参数都会有一个专门解析类进行解析。现在假设有一个 show() 方法,如下:


	public static void show(@RequestBody String type
							,@RequestParam Integer age
							,@RequestHeader Long length) {

	}

它有三个参数被不同的注解修饰,现在要对这三个参数进行解析,先不适用策略模式,代码如下:

public class IfElsePolicyTest {
	public static void main(String[] args) {

		try {
			Method method = IfElsePolicyTest.class.getMethod("show", String.class, Integer.class, Long.class);
			MethodParameter parameter = new MethodParameter(method, 0);

			if (parameter.hasParameterAnnotation(PathVariable.class)) {
				// ...逻辑处理
				System.out.println(">>>>>>解析含有 @PathVariable 注解的参数");
			} else if (parameter.hasParameterAnnotation(RequestParam.class)) {
				// ...逻辑处理
				System.out.println(">>>>>>解析含有 @RequestParam 注解的参数");
			} else if (parameter.hasParameterAnnotation(RequestBody.class)) {
				// ...逻辑处理
				System.out.println(">>>>>>解析含有 @RequestBody 注解的参数");
			} else {
				// ...其他逻辑
				System.out.println(">>>>>>解析其他逻辑");
			}


		} catch (Exception e) {
			throw new RuntimeException(e);
		}


	}

	public static void show(@RequestBody String type, @RequestParam Integer age, @RequestHeader Long length) {

	}
}

上述代码中借助了 Spring 工具类 MethodParameter,它可以更便捷获取到 show() 方法所有相关信息。从代码书写来看,不用策略模式,if-else 看起来特别多,而且如果参数上有新注解加进来,又得修改 if-else 比较麻烦,代码看起来也不舒服。代码冗余扩展性不好。

现在通过策略模式进行改造,怎么改造呢?遵从前面说的那三部分分析。

顶层抽象接口:

总的来说就是要解析参数,所以公共业务部分就是解析参数,先命名为 resolve() 方法,方法入参根据实际情况定,一般是封装成 DTO、Wrapper 等组合对象,这里简单就传入 Object 类型。返回值一般也是 DTO、Wrapper 包装对象,这里直接就返回 Object 对象,然后再定义策略条件,只有满足这个条件才能使用当前策略,最终定义出来顶层接口,代码如下:

public interface ParamResolver {

	public boolean supportType(MethodParameter parameter);

	public Object resolve(Object object) throws Exception;
}

策略实现类:

然后实现该接口,在子类中对不同的参数进行解析处理,比如:针对有 @RequestParam 注解修饰的参数的解析,代码如下:

public class RequestParamParamResolver implements ParamResolver{
	@Override
	public boolean supportType(MethodParameter parameter) {

		return Objects.nonNull(parameter) && parameter.hasParameterAnnotation(RequestParam.class);
	}

	@Override
	public Object resolve(Object object) throws Exception {
		System.out.println(">>>>>>解析含有 @RequestParam 注解的参数");
		return null;
	}
}

针对有 @RequestBody 注解修饰的参数的解析,代码如下:

public class RequestBodyParamResolver implements ParamResolver{
	@Override
	public boolean supportType(MethodParameter parameter) {

		return Objects.nonNull(parameter) && parameter.hasParameterAnnotation(RequestBody.class);
	}

	@Override
	public Object resolve(Object object) throws Exception {
		System.out.println(">>>>>>解析含有 @RequestBody 注解的参数");
		return null;
	}
}

针对有 @PathVariable 注解修饰的参数的解析,代码如下:

public class PathVariableParamResolver implements ParamResolver{
	@Override
	public boolean supportType(MethodParameter parameter) {
		return Objects.nonNull(parameter) && parameter.hasParameterAnnotation(PathVariable.class);
	}

	@Override
	public Object resolve(Object object) throws Exception {
		System.out.println(">>>>>>解析含有 @PathVariable 注解的参数");
		return null;
	}
}

策略上下文管理类:

因为子类会比较多,所以对于这些子类就应该提供一个管理类来管理。而这个管理类通常被称之为 Context 上下文,在这个上下文中会去收集子类,调用子类,代码如下:

public class ParamResolverContext {

	private static List<ParamResolver> paramResolvers = new ArrayList<>();

	public static void addPolicy(ParamResolver resolver) {
		paramResolvers.add(resolver);
	}

	public static ParamResolver getPolicy(MethodParameter parameter) {

		for (ParamResolver paramResolver : paramResolvers) {
			if (paramResolver.supportType(parameter)) {
				return paramResolver;
			}
		}

		throw new NotFoundSuitablePolicyException("未匹配到合适策略");
	}
}

测试类如下:

public class PolicyTest {
	public static void main(String[] args) {

		try {
			Method method = PolicyTest.class.getMethod("show", String.class, Integer.class, Long.class);
			MethodParameter parameter = new MethodParameter(method, 1);

			ParamResolverContext.addPolicy(new PathVariableParamResolver());
			ParamResolverContext.addPolicy(new RequestParamParamResolver());
			ParamResolverContext.addPolicy(new RequestBodyParamResolver());


			ParamResolver policy = ParamResolverContext.getPolicy(parameter);
			Object aNull = policy.resolve("null");


		} catch (Exception e) {
			throw new RuntimeException(e);
		}


	}

	public static void show(@RequestBody String type, @RequestParam Integer age, @RequestHeader Long length) {

	}
}

直接通过 ParamResolverContext.getPolicy() 一行代码就可以对参数进行解析,而之前的会有大量的 if-else 分支,现在通过策略模式把大量的 if-else 消除掉,代码简洁,扩展性也比较好。

最后将上述所有对象交给 Spring 管理,先将三个策略实现加上 @Component 注解,代码如下:

@Component
public class RequestParamParamResolver implements ParamResolver{
	@Override
	public boolean supportType(MethodParameter parameter) {

		return Objects.nonNull(parameter) && parameter.hasParameterAnnotation(RequestParam.class);
	}

	@Override
	public Object resolve(Object object) throws Exception {
		System.out.println(">>>>>>解析含有 @RequestParam 注解的参数");
		return null;
	}
}

@Component
public class PathVariableParamResolver implements ParamResolver{
	@Override
	public boolean supportType(MethodParameter parameter) {
		return Objects.nonNull(parameter) && parameter.hasParameterAnnotation(PathVariable.class);
	}

	@Override
	public Object resolve(Object object) throws Exception {
		System.out.println(">>>>>>解析含有 @PathVariable 注解的参数");
		return null;
	}
}

@Component
public class RequestBodyParamResolver implements ParamResolver{
	@Override
	public boolean supportType(MethodParameter parameter) {

		return Objects.nonNull(parameter) && parameter.hasParameterAnnotation(RequestBody.class);
	}

	@Override
	public Object resolve(Object object) throws Exception {
		System.out.println(">>>>>>解析含有 @RequestBody 注解的参数");
		return null;
	}
}

然后将策略上下文交给 Spring 管理,并实现 InitializingBean, ApplicationContextAware 接口,在 afterPropertiesSet() 方法中将策略子类添加到策略上下文中,实现 ApplicationContextAware 接口是为了能够拿到 BeanFactory 工厂,因为这里要使用到 BeanFactory 的 API,代码如下:

@Component
public class ParamResolverContext implements InitializingBean, ApplicationContextAware {

	private ApplicationContext context;

	private static List<ParamResolver> paramResolvers = new ArrayList<>();

	public static void addPolicy(ParamResolver resolver) {
		paramResolvers.add(resolver);
	}

	public static ParamResolver getPolicy(MethodParameter parameter) {

		for (ParamResolver paramResolver : paramResolvers) {
			if (paramResolver.supportType(parameter)) {
				return paramResolver;
			}
		}

		throw new NotFoundSuitablePolicyException("未匹配到合适策略");
	}

	@Override
	public void afterPropertiesSet() throws Exception {

		String[] beanNames = context.getBeanNamesForType(ParamResolver.class);

		for (String beanName : beanNames) {
			paramResolvers.add(context.getBean(beanName,ParamResolver.class));
		}
	}


	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.context = applicationContext;
	}
}

交给 Spring 管理之后,就算有 @RequestHeader 这样的注解加进来,这些代码都不用改动,只需要针对 @RequestHeader 注解实现对应的策略子类即可。

然后再 Controller 中就可以使用 ParamResolverContext 上下文管理类拿到相对于的策略类,代码如下:

@RestController
public class RequestBodyController {


	@PostMapping("/person")
	public Person person(@RequestBody Person person) throws Exception {

		Method method = RequestBodyController.class.getMethod("person",Person.class);
		MethodParameter parameter = new MethodParameter(method, 0);

		ParamResolver policy = ParamResolverContext.getPolicy(parameter);

		Object resolve = policy.resolve(parameter);

		System.out.println(">>>>>>person = " + person);
		return person;
	}
}

结果输出如下:

>>>>>>解析含有 @RequestBody 注解的参数
>>>>>>person = Person{name='gongwm', age=100}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

魔道不误砍柴功

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

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

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

打赏作者

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

抵扣说明:

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

余额充值