【问题标题】:Design pattern for configurable system可配置系统的设计模式
【发布时间】:2015-11-10 23:00:44
【问题描述】:

我有一个有趣的设计问题,我将尝试在下面的玩具问题中进行简化:

我希望设计一个系统,其输出将是基于某些输入和中间处理的学生对象。流程如下:我有一个教室列表作为一种输入。要生成输出,处理步骤是:

  1. 按 X 岁以下的学生(比如说 10 岁)过滤每个教室
  2. 按以下层次顺序的任何排列对过滤结果进行排序:身高、体重、臂长
  3. 返回前 8 名学生。

另一个输入可以是我已经拥有并希望包含在结果中的学生列表。例如:输入 1:3 名学生的列表,输入 2:将运行上述处理步骤的 2 个教室的列表。

设计这样一个输入系统的最佳方法是:

  • 输入类型{student list|classroom list}
  • 过滤器类型{age|height|etc},
  • 排序顺序{any ordering of height,weight,arm length},
  • returnNum{how many students to return}

系统应该足够灵活,以适应更多的输入类型和更多的排序条目{即。按鞋码分类学生}。我可以使用什么数据结构来对本节的每个部分进行建模(即表示排序标准的最佳方式是什么?)是否有任何设计模式可以满足这些需求?任何有关架构设计的帮助将不胜感激!

【问题讨论】:

标签: java sorting design-patterns


【解决方案1】:

嗯,您的建议可以使用 Java 8 流轻松完成,所以我想您可以遵循的一种模式是 pipeline。您也可以使用internal iterators 来实现它:

List<Student> found = Stream.of(student1, student2, student3, ..., studentn)
    .filter(s -> s.getAge() > 100)
    .sorted(Comparator.comparing(Student::getHeight).thenComparing(Student::getWeight))
    .limit(10)
    .collect(Collectors.toList());

【讨论】:

    【解决方案2】:

    根据要求,尽管 StudentClassroom 都是 StudentSources,但过滤和排序始终作用于 Student(在您的示例输入中,它们永远不会按教室过滤或排序)。过滤非常简单:

    // interface with a single method, reduces to a lambda too
    interface Filter<T> {
        boolean accept(T candidate);
    }
    

    排序是规范的:

    package java.util;
    // interface with a single method, reduces to a lambda too
    interface Comparable<T> {
        int compareTo(T a, T b);
    }
    

    以上两个都是Visitor 设计模式的应用。与@Edwin 的简洁回答一样,您将访问者排列在管道中(配置阶段),然后访问他们(执行阶段)。请注意,访客模式有“原因”,学生们应该在他们的“Gang of 4”书中阅读。

    你没有说太多:

    • 输入如何呈现给程序(例如,作为需要解析的文本)
    • 同一个学生是否可能出现在多个教室或学生列表中...这与您可能选择将 Comparator 传递给哪个 Java 集合有关;

    所以手头的任务归结为:

    1. 阅读过滤器、分类器、限制器的定义,为这些创建访问者
    2. 读取数据(教室/学生),对发现的每个学生执行if passesAllFilters(student) okstudents.add(student);,其中okstudentsjava.util.TreeSet,以Comparator 开头,达到限制时停止。

    您可能会争辩说,采用“定义过滤器的输入”的步骤是一个“工厂方法”,但这真的没有帮助..List&lt;Filter&lt;Student&gt;&gt; getFilters(String filterSpec) 并没有真正让您到达工厂方法的任何地方很有用。解析过滤器并提出引用学生特定属性、应用表达式等的代码可能不是一项简单的任务。根据您的过滤器需要允许的表达式类型,您可能需要研究像 ANTLR 4 这样的编译器生成器。您可能需要使用反射。

    【讨论】:

      【解决方案3】:

      看看https://docs.oracle.com/javase/7/docs/api/java/util/SortedMap.html

      您可以使用多个 SortedMap(每个键一个),其中您将列表中的每个元素与每个映射的相应键放在一起(es:sortedMapAge.put(carl, 18) ..sortedMapHeight(carl,"1.75 ") ...)。

      因此,使用迭代器,您可以使用适当的 SortedMap 通过各种键访问您的列表成员。

      如果您想将所有映射存储在进一步的抽象层中,您可以将它们存储在哈希映射中,并将键标识符用作键(sortedMapAge...key Age, sortedMapHeight height ....)

      这很棘手,但地图为您提供了一种使用键集组织对象的好方法。

      【讨论】:

        【解决方案4】:

        就个人而言,我会这样做:

        public <E> List<E> query(List<E> dataset, Filter<E> filter, Comparator<E> sortOrder, int maxResults);
        

        也就是说,我会使用泛型来抽象输入类型、过滤器和订单的命令模式,以及要返回的结果数量的普通 int。

        【讨论】:

        • 您能否简要介绍一下您对过滤器使用命令模式的具体设想?
        • 他的意思是“访客”:-)
        • 不,我是说我写的。解析后,您创建适当的命令对象,并将其传递给查询引擎,查询引擎使用它来确定是否应包含特定行。
        • @meriton 很好地描述了“Gang of 4”模式,该模式用于“表示要对对象结构的元素执行的操作”。然而,本书称其为“访问者”,并为一种模式保留“命令”,该模式用于“将请求封装为对象,从而让您可以使用不同的请求参数化客户端、排队或记录请求,并支持可撤消的操作”。粗略地说,根据这本书:访客得到了合作者,指挥部没有提供合作者,有一种“以后再做”的感觉。两者都封装了行为,但您的意思是访问者:-)
        猜你喜欢
        • 1970-01-01
        • 2023-03-04
        • 1970-01-01
        • 2010-11-21
        • 1970-01-01
        • 2011-11-23
        • 1970-01-01
        • 1970-01-01
        • 2021-01-11
        相关资源
        最近更新 更多