Java 策略模式 - 我可以在 Context 类中委托策略实例化吗?

OLe*_*ert 5 java design-patterns strategy-pattern

我目前正在自学设计模式。当我研究策略模式时,我发现一些对我来说看起来很奇怪的东西。我查找了有关此模式的讨论,但没有人回答我的问题...这就是我如何实现策略模式以使其干净、保持封装并使添加新策略变得容易。这里解释我的问题是“规范”策略模式:

public interface Strategy {
    public void run();
}

public class stratConcrt1 implements Strategy {/*run() implementation*/}
public class stratConcrt2 implements Strategy {/*run() implementation*/}

public class Context {
    private Strategy strategy;

    public Context(Strategy strat) {
        this.strategy = strat;
    }

    public void runStrategy() {
        this.strategy.run()
    }
}


public class Client {
    public void main(Strings[] args) {
        Context cx;

        cx = new Context(new stratConcrt1())
        cx.runStrategy();

        cx = new Context(new stratConcrt2())
        cx.runStrategy();
    }
}
Run Code Online (Sandbox Code Playgroud)

我明白发生了什么,但让客户了解可以应用的不同策略,我感觉很奇怪。对我来说,让 Context 实例化不同的策略而不是 Client 会更干净,因为 Context 处理策略,它应该(至少在我看来)是唯一能够实例化策略的策略。

我使用 JavaFx 实现了一个小示例,与上面的代码有一些差异:

我有一个实例化坐标列表的 Field 类,该类有一个对列表进行排序的方法。对坐标列表进行排序的方法是有多种策略的方法。

public class Field {

    // a field contains rectangles described in a list through their coordinates
    private ObservableList<Coordinate> mpv_coordinateList = FXCollections
                    .observableArrayList();

    private Context mpv_sortContext;

    // Constructor
    public Field() {
        //the rectangles are randomly created
        Random rd = new Random();
        for (int i = 0; i < 100; i++) {
            mpv_coordinateList.add(new Coordinate(rd.nextInt(490), rd.nextInt(490)));
        }

        //a context to dynamically modify the sort algorithm
        mpv_sortContext = new Context();
    }

    //returns the list with all rectangle
    public ObservableList<Coordinate> getField() {
        return this.mpv_coordinateList;
    }

    //sort elements (depending on different algorithms)
    public ObservableList<Coordinate> sortElements(String p_sortToApply) {
        return mpv_sortContext.launchSort(p_sortToApply,
                        this.mpv_coordinateList);
    }
}
Run Code Online (Sandbox Code Playgroud)

正如模型所说,我创建了一个接口,并让具体策略继承自该接口:

public interface SortStrategy {
    ObservableList<Coordinate> sort(ObservableList<Coordinate> p_listToSort);
}

public class EvenSort implements SortStrategy {
    @Override
    public ObservableList<Coordinate> sort(
                ObservableList<Coordinate> p_listToSort) {

        ObservableList<Coordinate> oddCoordList = FXCollections
                        .observableArrayList();

        for (Coordinate coord : p_listToSort) {
            if (coord.x % 2 == 0) {
                oddCoordList.add(coord);
            }
        }
        return oddCoordList;
    }
}

public class OddSort implements SortStrategy {
    @Override
    public ObservableList<Coordinate> sort(
                ObservableList<Coordinate> p_listToSort) {

        ObservableList<Coordinate> oddCoordList = FXCollections
                        .observableArrayList();

        for (Coordinate coord : p_listToSort) {
            if (coord.x % 2 == 1) {
                oddCoordList.add(coord);
            }
        }
        return oddCoordList;
    }
}
Run Code Online (Sandbox Code Playgroud)

具体类仅返回一个列表,其中包含具有偶数或奇数 x 坐标的所有坐标。

然后我创建了一个类上下文:

public class Context {
    //private SortStrategy mpv_sortStrategy; //never used

    private EvenSort mpv_evenSort = new EvenSort();
    private OddSort mpv_oddSort = new OddSort();
    private StandardSort mpv_standardSort = new StandardSort();

    private HashMap<String, SortStrategy> mpv_HashMapStrategies;


    public Context() {

        //creation of a dictionary with all possible strategies
        mpv_HashMapStrategies = new HashMap<String, SortStrategy>();
        mpv_HashMapStrategies.put("Even Sort", mpv_evenSort);
        mpv_HashMapStrategies.put("Odd Sort", mpv_oddSort);
        mpv_HashMapStrategies.put("Standard Sort", mpv_standardSort);
    }

    public ObservableList<Coordinate> launchSort(String p_sortToApply, ObservableList<Coordinate> p_listToSort){
        return mpv_HashMapStrategies.get(p_sortToApply).sort(p_listToSort);
    }
}
Run Code Online (Sandbox Code Playgroud)

通过 GUI,用户可以选择他想要用来对列表进行排序的策略。用户可以单击按钮来启动排序。通过主类(未显示)进行调用,inst_field.mpv_sortContext.sortElements(a_string)并使用字符串作为描述要使用的策略的参数。然后在 sortElements 中使用该字符串来选择用户想要应用的策略的实例。

在我的实现中,我的一侧是客户端,另一侧是处理策略(上下文、接口和具体类)的所有代码。如果我想添加一个新策略,我只需在 Context 类中添加新策略的实例,并在 GUI 中添加该新策略的描述,以便用户了解它。

我知道在我所做的实现中也不是那么好,因为 Context 包含每个可能策略的实例,因此我不需要对接口的引用,但我发现它比让 Field 和客户端更干净了解这些课程。

嗯...我完全错了吗?我在“规范”策略模式中错过了什么吗?“规范”方式是实施策略模式的唯一方式吗?或者是否有更好的方法来实现此模式,只有应该知道的类才知道策略实例,并且可以轻松地添加新策略?

wmo*_*365 3

我查找了有关此模式的讨论,但没有人回答我的问题...这就是我如何实现策略模式以使其干净

正如您所描述的那样,您的“策略”不一定是不干净的,我认为您可能会陷入客户是谁的想法中。您的客户正在提供要使用的实现,但这可能是必要的实现细节。例如,java RMI 教程的 ComputeEngine本质上就是使用这种模式。“计算”实现由客户端传递 - 因为只有客户端知道要执行的计算。

然而,更常见的是,该策略用于提供一种在某些上下文中可配置逻辑的方法,或者允许针对特定用途定制公共上下文。它还具有根据需要向客户隐藏内部结构的优点。通常为了做到这一点,要使用的策略将在内部配置到上下文。这可以由以下人员提供:

  • 根据要处理的数据确定策略的算法
  • 根据系统状态或约束确定策略的算法
  • 允许加载实现的配置文件 ( Class.getResourceAsStream)。这是班级地图的扩展Context(即从众所周知的位置加载地图)。这里的一个例子是,您可以提供一个“表示要使用的默认实现的策略,但允许提供新的实现作为替代策略 - 例如 defaultXMLParser
  • 数据本身的控制器 - 例如,对象类型可能规定使用某种策略来计算其值。

对于上面的前两点,您可以考虑使用工厂来导出正确的策略。这将使实施选择问题保持本地化。

嗯...我完全错了吗?我在“规范”策略模式中错过了什么吗?“规范”方式是实施策略模式的唯一方式吗?或者是否有更好的方法来实现此模式,只有应该知道的类才知道策略实例,并且可以轻松地添加新策略?

我想说你没有错。这实际上取决于使用该策略背后的目的。如果这是一个内部系统问题,那么应该有一些规则来推动选择(在工厂后面)。如果它出于某种原因是可配置的,那么它应该由隐藏在上下文中的配置和管理(管理使用该策略的整体逻辑的类)驱动。但是,如果它依赖于用户数据或行为,那么要么数据在内部驱动选择,要么您必须接受客户必须向您传递您的策略。

另请注意,此模式背后的目标是删除条件逻辑,同时保留替代实现。因此,如果您的策略导致您执行大量条件逻辑,那么您可能需要重新考虑它是否能够澄清您的代码。

</warandpeace>