持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第29天,点击查看活动详情。
在策略模式中,存在多个策略对象和一个行为随着策略对象改变而改变的 context(上下文) 对象。策略对象的改变会影响context的执行结果。一般来说对于一系列业务如果说他们的业务流程相似,但其中的一些数据处理规则不同,一般都会使用策略模式。而且使用策略模式可以减少大量的if。。else的出现。对于接口而言,使用策略模式可以减少接口暴露,通过运行期间获取不同的service来处理不同的业务场景,使得业务更加存粹。
在生活中其实也是的,我们遇到问题针对不同的问题会制定一系列的解决措施,并且一个人解决问题的习惯或是流程是大致相同的,这也是策略模式的体现。
策略模式
策略模式(Strategy OPattern),属于行为模式。指的是一个类的行为或算法可以在运行期间改变。
涉及角色:
- 策略对象
- 策略转发器 (Context)
在策略模式中,策略对象会改变Context运行的结果,也就是Context会随着策略对象的改变而改变。
实现
定义抽象策略对象接口
有各自策略类实现对应逻辑或算法
public interface Strategy {
/**
* 抽象方法(处理同一类相似逻辑)
*/
void doBusiness();
}
实现
public class StrategyImpl1 implements Strategy{
@Override
public void doBusiness() {
System.out.println("调用:"+this.getClass().getName());
}
}
public class StrategyImpl2 implements Strategy{
@Override
public void doBusiness() {
System.out.println("调用:"+this.getClass().getName());
}
}
策略转发器Context
根据注入不同的策略对象执行对应不同算法
public class Context {
/**
* 将需要转发的策略类组合进来
*/
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public void doForWard(){
strategy.doBusiness();
}
}
测试
public class StrategyTest {
@Test
public void test() {
Context context1 = new Context(new StrategyImpl1());
context1.doForWard();
Context context2 = new Context(new StrategyImpl2());
context2.doForWard();
}
}
说明
策略模式专注提供统一处理入口,统一处理实现同一接口的存在相似算法的类。
应用
在开发过程中不饿避免的会遇到一些处理流程相似的的接口,此刻就可以使用策略模式,结合配置可以实现统一处理。
我们以对用户进行增删改查为例
为了方便处理这里的equals只比对name和age。
@Data
@AllArgsConstructor
public class Person {
private String name;
private Integer age;
private Date birthDae;
@Override
public boolean equals(Object obj) {
Person target = (Person) obj;
return
this.getAge().equals(target.getAge())
&&
this.getName().equals(target.getName());
}
}
定义增删改查接口的抽象业务接口:
public interface ActionInterface {
/**
* 抽象业务
* @param person
*/
void doAction(Person person);
}
定义增删改拆接口:
//增
public class AddAction implements ActionInterface{
@Override
public void doAction(Person person ) {
Context.addAction(person);
}
}
//删
public class DeleteAction implements ActionInterface{
@Override
public void doAction(Person person ) {
Context.deleteAction(person);
}
}
定义策略转发器:
- 初始化用户数据PersonList,以及接口数据InterfaceNos
- 使用静态代码块模拟从数据库加载数据
- 定义静态方法对PersonList进行增删改查,模拟数据库增删改查
这里我们使用反射来获取具体的策略,实际开发中会将具体策略注入到ioc容器中,并将注入的BeanName持久化的形式保存在数据库中,然后使用SpringContextd对象来获取具体的策略。
public class Context {
private final static List<Person> personList = new ArrayList<>();
private final static Map<String, String> interfaceNos = new HashMap<>();
static {
//初始化一些数据,模仿数据库查询
personList.add(new Person("yyc1", 21, new Date(System.currentTimeMillis())));
personList.add(new Person("yyc2", 22, new Date(System.currentTimeMillis())));
//接口号数据初始化,接口号,接口名称(这个数据可以配置到数据库内)
interfaceNos.put("22061601", AddAction.class.getName());
interfaceNos.put("22061602", DeleteAction.class.getName());
}
//利用反射创建实例
private ActionInterface getInstance(String interfaceNo) throws ClassNotFoundException,
InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
final String interfaceName = interfaceNos.get(interfaceNo);
final Class<?> aClass = Class.forName(interfaceName);
final Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
return (ActionInterface) declaredConstructor.newInstance();
}
//转发策略
public void doStrategy(String interfaceNo, Person person) throws ClassNotFoundException,
InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
ActionInterface instance = getInstance(interfaceNo);
instance.doAction(person);
}
/**
* 对外暴露方法
*/
public static void deleteAction(Person person) {
final boolean remove = personList.remove(person);
System.out.println("======" + "deleteAction" + "======");
if (remove) {
System.out.println("success");
} else {
System.out.println("faild");
}
personList.forEach(System.out::println);
}
public static void searchAction(Person person) {
final Person person1 = personList.get(personList.lastIndexOf(person));
if (ObjectUtils.isEmpty(person1)) {
System.out.println("没找到");
} else {
System.out.println("找到了:" + person1);
}
personList.forEach(System.out::println);
}
}
测试:
public class DemoTest {
public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
Context context = new Context();
Person personAdd = new Person("yyc5", 25, new Date(System.currentTimeMillis()));
Person personModify = new Person("yyc2", 252123, new Date(System.currentTimeMillis()));
Person personSearch = new Person("yyc3", 23, new Date(System.currentTimeMillis()));
context.doStrategy("22061603", personModify);
context.doStrategy("22061604", personSearch);
}
//测试反射
@Test
public void test() throws ClassNotFoundException {
Class<?> aClass = Class.forName("com.roily.designpatterns.dpmain.strategy.strategydemo."+"AddAction");
}
}
.
小结
这里我们使用的是反射+多态来创建对象实例的,在大多数使用场景下我们会使用SpringIoc容器去实现,通过ApplicationContext.getBean()方式来获取对象实例。
总结
策略模式属于行为模式,类的具体调用决策权在客户端,所有的实例都是在运行期间生成的。如此实现对于程序来说,如果需要扩展一个接口,只需要增加实现类并配置接口号即可。
缺点就在于过多的策略类的出现。