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 和客户端更干净了解这些课程。
嗯...我完全错了吗?我在“规范”策略模式中错过了什么吗?“规范”方式是实施策略模式的唯一方式吗?或者是否有更好的方法来实现此模式,只有应该知道的类才知道策略实例,并且可以轻松地添加新策略?
我查找了有关此模式的讨论,但没有人回答我的问题...这就是我如何实现策略模式以使其干净
正如您所描述的那样,您的“策略”不一定是不干净的,我认为您可能会陷入客户是谁的想法中。您的客户正在提供要使用的实现,但这可能是必要的实现细节。例如,java RMI 教程的 ComputeEngine本质上就是使用这种模式。“计算”实现由客户端传递 - 因为只有客户端知道要执行的计算。
然而,更常见的是,该策略用于提供一种在某些上下文中可配置逻辑的方法,或者允许针对特定用途定制公共上下文。它还具有根据需要向客户隐藏内部结构的优点。通常为了做到这一点,要使用的策略将在内部配置到上下文。这可以由以下人员提供:
Class.getResourceAsStream
)。这是班级地图的扩展Context
(即从众所周知的位置加载地图)。这里的一个例子是,您可以提供一个“表示要使用的默认实现的策略,但允许提供新的实现作为替代策略 - 例如 defaultXMLParser对于上面的前两点,您可以考虑使用工厂来导出正确的策略。这将使实施选择问题保持本地化。
嗯...我完全错了吗?我在“规范”策略模式中错过了什么吗?“规范”方式是实施策略模式的唯一方式吗?或者是否有更好的方法来实现此模式,只有应该知道的类才知道策略实例,并且可以轻松地添加新策略?
我想说你没有错。这实际上取决于使用该策略背后的目的。如果这是一个内部系统问题,那么应该有一些规则来推动选择(在工厂后面)。如果它出于某种原因是可配置的,那么它应该由隐藏在上下文中的配置和管理(管理使用该策略的整体逻辑的类)驱动。但是,如果它依赖于用户数据或行为,那么要么数据在内部驱动选择,要么您必须接受客户必须向您传递您的策略。
另请注意,此模式背后的目标是删除条件逻辑,同时保留替代实现。因此,如果您的策略导致您执行大量条件逻辑,那么您可能需要重新考虑它是否能够澄清您的代码。
</warandpeace>