用枚举类写策略模式,codeReview被喷了

news/2024/7/5 2:15:15

前言

策略模式在日常开发经常使用,但是每个人的写法不一样,之前用枚举类写策略模式做充值卡推荐被喷,这里简单总结下策略模式的几种写法。我们简单以学生排序展示作为demo,前端根据age,name,score来进行排序展示。

经典写法

先定义一个排序接口

public interface SortStudent {
    List<Student> sort(List<Student> studentSource);
}

根据年龄排序

/**
 * @author 爱琴孩
 */
public class SortStudentByAge implements SortStudent {
    @Override
    public List<Student> sort(List<Student> studentSource) {
        return studentSource.stream().sorted(Comparator.comparing(Student::getAge))
                .collect(Collectors.toList());
    }
}

根据成绩分数排序

/**
 * @author 爱琴孩
 */
public class SortStudentByScore implements SortStudent {
    @Override
    public List<Student> sort(List<Student> studentSource) {
        return studentSource.stream().sorted(Comparator.comparing(Student::getScore))
                .collect(Collectors.toList());
    }
}

根据姓名排序

/**
 * @author 爱琴孩
 */
public class SortStudentByName implements SortStudent {
    @Override
    public List<Student> sort(List<Student> studentSource) {
        return studentSource.stream().sorted(Comparator.comparing(Student::getName))
                .collect(Collectors.toList());
    }
}

构建一个上下文类

/**
 * @author 爱琴孩
 */
public class SortStudentContext {

    private SortStudent sortStudent;

    public SortStudentContext(SortStudent sortStudent) {
        this.sortStudent = sortStudent;
    }

    public SortStudentContext() {
    }

    public List<Student> sortStudent(List<Student> studentList) {
        return sortStudent.sort(studentList);
    }
}

业务调用方法

    /**
     * 经典策略模式
     *
     * @param sortType 排序模式
     * @return java.util.List<com.example.study.model.Student>
     * @author 爱琴孩
     */
    public List<Student> testStrategy(String sortType) {
        SortStudentContext sortStudentContext;
        switch (sortType) {
            case "sortByName":
                sortStudentContext = new SortStudentContext(new SortStudentByName());
                break;
            case "sortByScore":
                sortStudentContext = new SortStudentContext(new SortStudentByScore());
                break;
            default:
                sortStudentContext = new SortStudentContext(new SortStudentByAge());
        }
        return sortStudentContext.sortStudent(students);

    }

我们可以看到经典方法,创建了一个接口、三个策略类,还是比较啰嗦的。调用类的实现也待商榷,新增一个策略类还要修改榜单实例(可以用抽象工厂解决,但是复杂度又上升了)。加之我们有更好的选择,所以此处不再推荐经典策略模式。

枚举策略类写法

/**
 * @author 爱琴孩
 */
public enum SortStudentEnum {
    SortByAge {
        @Override
        public List<Student> sortStudents(List<Student> studentList) {
            return studentList.stream().sorted(Comparator.comparing(Student::getAge)).
                    collect(Collectors.toList());
        }
    },
    SortByName {
        @Override
        public List<Student> sortStudents(List<Student> studentList) {
            return studentList.stream().sorted(Comparator.comparing(Student::getName)).
                    collect(Collectors.toList());
        }
    },
    SortByScore {
        @Override
        public List<Student> sortStudents(List<Student> studentList) {
            return studentList.stream().sorted(Comparator.comparing(Student::getScore)).
                    collect(Collectors.toList());
        }
    };

    public abstract List<Student> sortStudents(List<Student> studentList);
}

业务调用方法

    /**
     * 枚举类策略模式
     *
     * @param sortType 排序模式
     * @return java.util.List<com.example.study.model.Student>
     * @author 爱琴孩
     */
    public List<Student> testStrategyByEnum(String sortType) {
        SortStudentEnum sortStudentEnum;
        try {
            sortStudentEnum = Enum.valueOf(SortStudentEnum.class, sortType);
        } catch (IllegalArgumentException e) {
            throw e;
        }
        return sortStudentEnum.sortStudents(students);
    }

可以看到,如果策略简单的话,基于枚举的策略模式优雅许多,调用方也做到了0修改,但正确地使用枚举策略模式需要额外考虑以下几点。

  • 枚举的策略类是公用且静态,这意味着这个策略过程不能引入非静态的部分,扩展性受限
  • 策略模式的目标之一,是优秀的扩展性和可维护性,最好能新增或修改某一策略类时,对其他类是无改动的。而枚举策略如果过多或者过程复杂,维护是比较困难的,可维护性受限

基于工厂的策略模式

为了解决良好的扩展性和可维护性,我更推荐以下利用spring自带beanFactory的优势,实现一个基于工厂的策略模式。策略类改动只是添加了@Service注解,并指定了Service的value属性

/**
 * @author 爱琴孩
 */
@Service("SortByAge")
public class SortStudentByAge implements SortStudent {
    @Override
    public List<Student> sort(List<Student> studentSource) {
        return studentSource.stream().sorted(Comparator.comparing(Student::getAge))
                .collect(Collectors.toList());
    }
}


/**
 * @author 爱琴孩
 */
@Service("SortByName")
public class SortStudentByName implements SortStudent {
    @Override
    public List<Student> sort(List<Student> studentSource) {
        return studentSource.stream().sorted(Comparator.comparing(Student::getName))
                .collect(Collectors.toList());
    }
}

/**
 * @author 爱琴孩
 */
@Service("SortByScore")
public class SortStudentByScore implements SortStudent {
    @Override
    public List<Student> sort(List<Student> studentSource) {
        return studentSource.stream().sorted(Comparator.comparing(Student::getScore))
                .collect(Collectors.toList());
    }
}

业务调用方法

@Service
public class SpringTestService {

    private static final Logger LOGGER = LoggerFactory.getLogger(SpringTestService.class);

    private static List<Student> students;

    @Resource
    private Map<String, SortStudent> sortStudentMap;


    static {
        students = new ArrayList<Student>() {{
            add(new Student("霸都爱琴孩", 23, 99));
            add(new Student("青山剑客", 26, 99));
            add(new Student("西域刀郎", 43, 89));
            add(new Student("西湖法海", 33, 98));
            add(new Student("华山令狐冲", 22, 100));
        }};
    }

    /**
     * 通过Spring工厂来实现策略模式
     * @author 爱琴孩
     * @param sortType 排序类型
     * @return java.util.List<com.example.study.model.Student>
     */
    public List<Student> testStrategyByFactory(String sortType) {
        if (!sortStudentMap.containsKey(sortType)) {
            throw new ParameterException(RetCode.PARAM_ERROR.getCode());
        }
        LOGGER.info("sortStudentMap ={}", JSONObject.toJSON(sortStudentMap));
        SortStudent sortStudent = sortStudentMap.get(sortType);
        return sortStudent.sort(students);
    }
}

  @Resource
   private Map<String, SortStudent> sortStudentMap;

上面这种写法使用了Spring的注解方式进行依赖注入。其中,@Resource注解用于标注需要注入的组件,Map<String, SortStudent> sortStudentMap则表示将Spring容器中所有类型为SortStudent的Bean注入到该Map中,并以Bean的name作为key存储。具体原理如下:

  1. Spring容器在启动时会将所有带有@Component、@Service、@Repository和@Controller注解的类实例化成Bean,并以Bean的name存储到容器中。
  2. 当使用@Resource注解进行依赖注入时,Spring通过类型匹配的方式,在容器中查找与该属性类型相同的Bean,将其注入到对应的属性中。
  3. 对于一个Map类型的属性,Spring将会将所有类型相同的Bean注入到该Map中,并以Bean的name作为key存储。

总结

经典模式显然不合适了,如果逻辑比较简单,比如说文中这种简单的排序展示,并且策略比较多的时候,选用枚举策略模式是很合适的,这样可以减少策略类,避免造成类泛滥。如果每个策略的业务逻辑比较复杂,策略类比较少,那么结合spring 的工厂模式是比较合适的。


http://lihuaxi.xjx100.cn/news/1269628.html

相关文章

哈夫曼树——数组实现

构造n个给定值节点构成的森林&#xff1b; 选择权值最小的两个构成叶子节点&#xff0c;根节点权值为两叶子节点之和&#xff0c; 删除原有的两棵树&#xff0c;将这棵树加入森林中&#xff1b; 重复这两部直到只有一棵树为止&#xff0c;此树就是哈夫曼树&#xff1b; #pr…

微服务项目Linux环境搭建

linux环境搭建 阿里云镜像地址&#xff1a;http://mirrors.aliyun.com 下载linux镜像文件地址&#xff1a; http://mirrors.aliyun.com/centos/7.9.2009/isos/x86_64/?spma2c6h.25603864.0.0.7810f5adugpU3h 选择CentOS-7-x86_64-Everything-2009.iso&#xff0c;点击下载。…

ROS:计算图

目录 一、ROS计算图简介二、节点&#xff08;Node&#xff09;三、节点管理器&#xff08;Master&#xff09;四、消息&#xff08;Message&#xff09;五、话题&#xff08;Topic&#xff09;六、服务&#xff08;Service&#xff09;七、动作&#xff08;Action&#xff09;八…

到底还有谁学不会 MySQL 中的视图?

文章目录 MySQL中的视图视图的概念视图的用法简化查询操作提高查询效率保护数据的安全性 视图的代码示例总结 MySQL中的视图 在MySQL中&#xff0c;视图是一种虚拟表&#xff0c;它是由一个或多个基本表的行或列组成的。视图并不实际存储数据&#xff0c;而是根据定义的查询语…

Word技巧之【文档自动保存】

打工人的噩梦—电脑突然坏掉&#xff0c;文档还没保存&#xff01;你是否遇到这种情况&#xff1f; 如果Word文档设置了自动保存&#xff0c;就不用太过担心了&#xff0c;只需要几个简单的操作就能设置好。还不知道的小伙伴&#xff0c;跟着小编一起看看吧。 设置Word文档自动…

Vb+sql医院门诊管理系统(系统+论文+开题报告+任务书+文献综述+参考文献)

信息时代已经来临&#xff0c;计算机应用于医院的日常管理&#xff0c;为医院的现代化带来了从未有过的动力和机遇&#xff0c;为医疗卫生领域的发展提供了无限的潜力。采用计算机管理信息系统已成为医院管理科学化和现代化的标志&#xff0c;给医院带来了明显的经济效益和社会…

丰田汽车投资人要求董事长下台

&#x1f699; 丰田电动车推广不力&#xff0c;股东要求董事长下台 Toyota faced down two proxy votes at its annual general meeting. In an unusual challenge to the management of a Japanese company, activist investors in America and Europe recommended voting aga…

在 Python 中打印度数符号

本篇文章将介绍如何用 Python 编写度数符号。 度数符号 度数符号是表示特定区域温度的符号。 例如&#xff0c;假设卡纳塔克邦的气温为 34 摄氏度&#xff1b; 它表明&#xff0c;在印度卡纳塔克邦&#xff0c;温度为 34 度。 度数也与华氏度和摄氏度一起使用。 使用 chr 函数…