策略模式的应用——应对频繁的需求变更

秋招结束后,间接性堕落了一段时间,学习几乎停止下来了。内心甚是焦灼,感觉生活很无趣!为了在参加工作后能够快速上手和成为一名优秀的中级开发者,从这篇文章开始将不断学习优秀的编码经验,学习是永无止境的。需要静下来,慢慢来!下面进入新篇章,技术提升篇。

应用情景

在工作中,往往我们的需求是多变的,那么如果我们只是简单的硬编码解决暂时的需求,那么当需求进行变更的时候我们的接口就需要变更来应对新的需求。但是有些情况下我们可以写一些通用接口来因对这种变化,即把频繁变化的东西提取出来,交给接口调用者来设计。在这种情况下,我们的接口就无需变化,而当需求变化了只需要重写频繁变化的对象即可。这就是策略模型的一种应用,接下来将用具体的例子来说明这种编码方式的具体实现。

此时,我们有个实体集合,需要通过对实体类的属性进行过滤选择出符合我们条件的一些实体。例如,第一周产品经理要求对属性一进行过滤,第二周需求变更又要把属性二加进去过滤,第三周又要把属性三考虑进去。。。。。于是,为了应对这种变化,我们的接口应该使用策略模式。如下:

具体实现

假设我们的实体简单一些,使用经典Student作为实体,代码如下:

java 复制代码
package strategy.demo.entity;

public class Student {
    private String name;
    private int age;
    private int score;
    private String address;
    public Student(String name, int age, int score, String address){
        this.name = name;
        this.age = age;
        this.score = score;
        this.address = address;
    }
    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
    public int getScore() {
        return score;
    }
    public String getAddress() {
        return address;
    }
    public String toString(){
        return "Student [name=" + name + ", age=" + age + ", score=" + score + ", address=" + address + "]";
    }
}

首先,我们看下如果是硬编码的话,两个不同的需求应该会写下如下代码:

java 复制代码
/**
     * 需求1:根据分数过滤学生
     * @param students
     * @param age
     * @return
     */
    public List<Student> filterStudentByAge(List<Student> students, int age){
        ArrayList<Student> result = new ArrayList<>();
        for (Student student : students) {
            if (student.getAge() > age){
                result.add(student);
            }
        }
        return result;
    }

    /**
     * 需求2:根据分数和年龄过滤学生
     * @param students
     * @param age
     * @param score
     * @return
     */
    public List<Student> filterStudentByScore(List<Student> students,int age , int score){
        ArrayList<Student> result = new ArrayList<>();
        for (Student student : students) {
            if (student.getScore() > score && student.getAge() > age){
                result.add(student);
            }
        }
        return result;
    }

上面的代码,需求2的代码是对需求1的升级维护,如果后面继续改动这个接口呢?所以,我们需要把频繁变动的东西提取出来作为一个策略对象,这里频繁变动的东西就是对实体Student属性的判断,因此我们抽取出一个接口:

java 复制代码
package strategy.demo.service;
import strategy.demo.entity.Student;
public interface filterStrategy {
	/**
     * 根据stduent的不同属性进行谓词过滤
     * @param student
     * @return
     */
    public boolean filterStudent(Student student);
}

这个接口只有一个待实现的方法,接受一个Student对象,返回是否符合过滤条件。而这个接口是由调用者实现的,其实这个设计思路在JDK中非常常见。我们需要对一个对象集合 sort 排序,需要按照自己的排序策略重写Comparator接口,还记得嘛?如下:

java 复制代码
List<Student> allStudents = StudentHelps.getAllStudents();
allStudents.sort(new Comparator<Student>() {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.getAge()-o2.getAge;
    }
});

因此,使用策略模式,我们只需要写下一个过滤方法即可,其他的过滤策略由调用者去实现,如下:

java 复制代码
public List<Student> filterStudentByStrategy(List<Student> students, filterStrategy strategy){
        ArrayList<Student> result = new ArrayList<>();
        for (Student student : students) {
            if (strategy.filterStudent(student)){
                result.add(student);
            }
        }
        return result;
    }

调用者实现时如下:

java 复制代码
List<Student> allStudents = StudentHelps.getAllStudents();
List<Student> students3 = filterStudentByStrategy(allStudents, new filterStrategy() {
    @Override
    public boolean filterStudent(Student student) {
        return student.getScore() > 80 && student.getAge() > 20;
    }
});

如此一来,无论对student的过滤需求如何变化,始终可以通过 filterStrategy 接口实现,因此就更大程度上遵守高内聚低耦合的开发原则。这就是设计模式的美妙之处。

扩展

其实,在很多情况下,接口开发者会提供一个基本功能的 filterStrategy 接口实现类,里面有常用的过滤策略,调用者只需要用即可不需要每次都自己定义。这在JDK源码和SpringBoot中有太多的例子了,简单常用的由接口方提供,复杂不常用的我允许调用者自定义!

另外一个扩展就是,调用者在这里写匿名类可以使用Lambda语法,具体参考我的博客 Lambda表达式常见用法Lambda语法也是提供工作效率的神器,有时间推荐学习!因此,调用者使用Lambda可以这样编写代码:

java 复制代码
List<Student> students4 = filterStudentByStrategy(allStudents, student -> student.getScore() > 80 && student.getAge() > 20);

也就相当于把判断条件参数化了,这种做法叫做自定义函数接口(看起来是函数作为一个参数传递),看不懂的话去看下我的博客就会了,以上就是本期内容!共勉加油!

相关推荐
Java小白笔记3 分钟前
Windows系统免软件命令激活
java·网络·人工智能·windows·ai·ai编程
爱敲键盘的猴子12 分钟前
深入理解 Java HashMap
java·hashmap
霸道流氓气质12 分钟前
Java开发者AI编程常用MCP、Skills、Rules全景汇总
java·开发语言·ai编程
天涯醉青楼24 分钟前
Spring Boot 接口返回 Bean 时字段名“变形”的深度剖析与解决方案
java
步行cgn41 分钟前
MyBatis 高级映射与延迟加载完全指南
java·tomcat·mybatis
thefool11226642 分钟前
从中序与后序遍历序列构造二叉树
java
吴声子夜歌1 小时前
Java面试题——JVM(二)
java·开发语言·jvm
CRMEB1 小时前
商城大促活动策划全流程:从定目标到复盘四阶段
java·开发语言·人工智能·ai·开源·php
denglinqings1 小时前
Java开发从入门到精通所有课程
java·开发语言
星空1 小时前
maven镜像
java·maven