【问题标题】:Strategy Pattern on Heterogeneous Objects异构对象的策略模式
【发布时间】:2021-06-04 22:59:33
【问题描述】:

我正在学习策略模式,它似乎是继承的一个了不起的替代方案。我一直在思考它的用例,但是当我尝试连接这些点时,我一直在努力解决这个问题:

当我的对象是异构的时,这是如何工作的。

那么,我的意思是什么?最简单的解释方法是用我听到的一个例子。有人试图通过告诉我“假设您有一个 List 并且该列表应该是可排序的。您可以实现接口 Sortable 并在那里添加逻辑,但更好的选择是使用组合而不是继承并将可排序行为传递给列表构造时,可以复用不同类型列表之间的排序逻辑,并且可以在运行时切换列表的排序策略”

很酷,所以这个解释有我在策略模式中看到的 3 个要点:

  1. 它有利于组合而不是继承,从而使代码更加灵活和可维护。
  2. 因为我们没有实现接口,所以我们遵循 DRY。如果两个不同的列表需要相同的排序逻辑,它们就不必在 @Override 方法中重复代码。由于组合,我们可以重复使用。
  3. 我们可以在运行时交换行为,这遵循打开/关闭原则。

困扰我的是,为了对列表进行排序,我们需要大量关于列表的信息。列表的类型很多,它们都有不同的内部结构。如果对象是异构的,那么很难有一种可以应用于两种不同类型列表的排序策略。

想象一下,我有一个 LinkedList、一个 ArrayList 和一个 DoublyLinkedList。为了对其中的每一个进行排序,我需要深入了解每个实现的实际状态、结构或内部结构(无论您想如何称呼它们)。因此,很难创建一个可以在所有这些列表上运行的策略。

  • 在这些情况下,策略模式实际上是如何工作的?
  • 或者它根本不适合用于异构对象?
  • 是否应该仅在策略不需要了解对象的任何内部时才使用策略?
  • 是否应该仅在所有对象都可以为策略提供相同信息才能工作时才使用它?

如果策略依赖于对象的内部结构,它就不能重用......那么,有没有任何现实生活中的例子可以阐明这种模式的实际价值?

我本身没有最后一个问题(我问了很多问题),但我认为我的观点和关注点很明确。

【问题讨论】:

  • 这样的异构对象的例子会很方便。
  • @NikolasCharalambidis 添加了一些列表示例
  • 感谢您的更新。 ArrayListLinkedList 都实现了相同的接口List(我不知道DoublyLinkedList 是什么意思,因为它正是JDK 中的LinkedList)。排序行为使用通过List 接口可用的方法。
  • @NikolasCharalambidis 嗯好的,所以列表会自己传递给策略。好的,我明白你的意思。但具体到那个例子,使用这些接口不是很无用吗?如果我正在使用接口,为什么不创建一个使用 List 接口对列表进行排序的实用程序类呢?每当我需要不同的排序行为时,只需创建一个新的实用程序排序类。这样我的列表类就不必依赖 SortStrategy 对象了。
  • @NikolasCharalambidis DoublyLinkedList 不是普通的LinkedList.. 至少在Java中,LinkedLists 单独 @ 987654330@s 在 Java 中。

标签: java design-patterns strategy-pattern


【解决方案1】:

使用的策略模式最简单的例子是简单的 JSK 方法Collections#sort(List, Comparator),它实际上使用Comparator 作为影响排序行为的注入行为(暂时将排序算法本身放在一边)。此方法旨在对声明为 List 的 anything 进行排序,而不管其泛型类型如何,因为这是 Comparator 处理的内容。因此,我会说它已准备好对任何异构对象进行排序。

如果您觉得方法不够,请随意扩展策略的责任。这是一个例子:

<T extends List<E>, E> T sortList(T list, SortingStrategy<T, E> strategy) {
    return strategy.sortList(list);
}
interface SortingStrategy<T extends List<E>, E> {
    T sortList(T list);
}

现在是策略实施。我使用 JDK 集合,但您不限于此层次结构,您可以定义自己的集合。

class ArrayListSortingStrategy<E> implements SortingStrategy<ArrayList<E>, E> {

    @Override
    public ArrayList<E> sortList(final ArrayList<E> list) {
        return /* ... implementation ... */;
    }
}
class LinkedListSortingStrategy<E> implements SortingStrategy<LinkedList<E>, E> {

    @Override
    public LinkedList<E> sortList(final LinkedList<E> list) {
        return /* ... implementation ... */;
    }
}

用法:

List<String> arrayList = sortList(new ArrayList<>(), new ArrayListSortingStrategy<>());
Deque<String> linkedList = sortList(new LinkedList<>(), new LinkedListSortingStrategy<>());

我发现策略模式相当灵活。您可以将进一步的行为注入特定策略的构造函数并嵌套策略。:

public ArrayListSortingStrategy(Comparator<E> comparator) { /* ... */ }

【讨论】:

  • 我认为这是最好的,但这种方法的问题是我们事先知道我们正在使用 ArrayList,所以我们需要使用 ArrayListSortingStrategy。如果我们必须在运行时找出列表的类型,那将是行不通的。否则我们将不得不打破多态性。
  • 您可以定义class ListSortingStrategy&lt;E&gt; implements SortingStrategy&lt;List&lt;E&gt;, E&gt;,它将对List 的所有实现进行排序,但是返回类型List。我不知道它对你有帮助。我建议您取消选中我的回答已被接受,然后等待其他回答者,因为有人可能有更好的主意。
【解决方案2】:

...为了对列表进行排序,我们需要有关列表的大量信息。

不是真的。事实上,没有关于列表的信息对于排序是绝对必要的。可以对任意两个Comparable 元素进行排序。特定数据结构是否包含元素并不重要。 Java 中的排序已经采用这种方式。

基本上,面向对象编程的整个前提是在不知道彼此具体类型的情况下进行通信的对象。面向对象的设计是一种创建适当抽象以促进这种通信的练习。

是否应该仅在所有对象都可以为策略提供相同信息才能工作时才使用它?

暂时搁置策略模式。所有的 OOP 都依赖于相互提供一致信息的对象。为了促进这一点,我们创建了适当的抽象,在本质上使异构对象看起来相同。

探索一些 Java 的排序实现应该会更清楚这一点。首先,TreeSet 是一个很好的策略示例,因为它构成了它的Comparator。请注意,ComparatorTreeSet 的了解为零,实际上,相同的Comparator 可以在列表中使用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-06
    • 1970-01-01
    • 2012-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多