代码之家  ›  专栏  ›  技术社区  ›  User1

何时以及为什么应该使用策略模式?

  •  27
  • User1  · 技术社区  · 16 年前

    什么时候 Strategy Pattern 使用吗?

    我看到客户机代码片段如下:

    
    class StrategyExample {
    
        public static void main(String[] args) {
    
            Context context;
    
            // Three contexts following different strategies
            context = new Context(new ConcreteStrategyAdd());
            int resultA = context.executeStrategy(3,4);
    
            context = new Context(new ConcreteStrategySubtract());
            int resultB = context.executeStrategy(3,4);
    
            context = new Context(new ConcreteStrategyMultiply());
            int resultC = context.executeStrategy(3,4);
    
        }
    
    }
    
    

    看起来你可以把它重构为:

    
    class StrategyExample {
    
        public static void main(String[] args) {
             // Three contexts following different strategies
            int resultA =new ConcreteStrategyAdd().execute(3,4);
            int resultB =new ConcreteStrategySubtract().execute(3,4);
            int resultC =new ConcreteStrategyMultiply().execute(3,4);
        }
    
    }
    
    

    代码的第一部分直接取自维基百科页面。一个很大的区别是上下文消失了,但无论如何它并没有在示例中做任何事情。也许有人有一个更好的例子,在这个例子中战略是有意义的。我通常喜欢设计模式,但这一个似乎增加了复杂性而没有增加有用性。

    10 回复  |  直到 16 年前
        1
  •  56
  •   Tendayi Mawushe    16 年前

    像这样的玩具例子的问题是,它往往容易错过要点。在这种情况下,代码确实可以如您所示实现。在策略模式中,主要的价值在于能够针对不同的情况切换不同的实现。

    您的示例只是说明模式中的对象以及它们之间的交互。想象一下,你有一个组件,根据一个网站是一个真正的网络浏览器还是另一端的智能手机来呈现图形,你会有一些代码来检测创建的浏览器的类型,并在另一个组件上设置策略,该组件可以在一些不需要的复杂代码中使用策略对象。在这两种情况下都要复制并完成工作,将实际图形的细节留给适当的策略对象:

    interface GraphStrategy {
        Image renderGraph(Data graphData);
    }
    
    class BigGraphStratedy implements GraphStrategy {
        ...
    }
    
    class SmallGraphStrategy implements GraphStrategy {
        ...
    }
    

    然后在其他代码中:

    GraphStrategy graphStrategy;
    
    if (phoneBrowser == true) { 
        graphStrategy = new SmallGraphStrategy();
    } else {
        graphStrategy = new BigGraphStrategy();
    }
    

    剩下的应用程序代码就可以使用 graphStrategy.renderGraph() 不需要知道是执行完整还是小图像渲染。

        2
  •  9
  •   Doug T.    16 年前

    想到的领域:

    • 资源分配器。在手动资源管理中,这可能是最小化资源分配所需的时间,或者最小化碎片。这里的每个策略都有一个“分配”方法,该方法具有相同的界面,用户根据要优化的内容来决定要使用哪个策略。
    • 一种连接和发送网络数据的方法。在某些情况下,您可能更喜欢连接和发送UDP数据报,在其他情况下,性能不是您使用TCP/IP发送的因素。
    • 数据格式化/序列化策略。允许代码决定对象是应该用JSON序列化还是用XML序列化。可能一个用于机器,另一个用于人类可读的情况。这两种策略都有一个采用对象的“序列化”方法。每个序列都不同。

    主题是,决定以某种方式还是另一种方式做某件事取决于情境因素,您或您的代码将根据情境选择正确的策略。

    为什么这比类似的东西更有用:

    void DoIt()
    {
        if (... situation1...)
        {
           DoA()
        }
        else
        {
           DoB();
        }
    }
    

    原因是有时你只想做一次决定,然后忘记它。策略模式的另一个重要主题是,您将决定使用哪种策略与需要执行策略的代码分离开来。

    DoItStrategy MakeDoItStrategy()
    {
         if (... situation1...)
         {
               return new DoItStrategyA();
         }
         else
         {
               return new DoItStrategyB();
         }
    }
    

    在上一个示例中,您可以只存储策略,将其作为实现策略接口的另一个对象传递。对于那些执行策略的人,他们只是有一种执行操作的方法。他们不知道引擎盖下的内部工作是什么,只知道界面会满意。战略的使用者不需要知道我们为什么要做决定。他们只需要采取行动。我们做了一次决定,并将该策略传递给使用该策略的类。

    例如,考虑这样一种情况:我们根据给定的网络配置,在程序范围内决定使用UDP连接数据并将其发送到远程主机。我们可以预先创建UDP策略,并将其传递给需要发送网络数据的每个人,而不是网络接口的每个用户都需要知道做出决策的逻辑(上面的“doit”功能)。然后,该策略实现一个具有相同最终结果的简单接口-数据从A到B。

        3
  •  3
  •   Steve B.    16 年前

    您引用的代码片段有点欺骗性,因为它(稍微)脱离了上下文。在下面的示例中,您所写的也是策略模式——您只是更简洁地重写了上面的示例。

    这个例子的要点是,数学运算的细节是从调用方抽象出来的。这样,调用者就可以通过创建新的具体策略(例如,

      int mod = new ConcreteStrategy(){ 
             public int execute(int a, int b){ return a %b; }
      }.execute(3,4);
    
        4
  •  2
  •   M1EK    16 年前

    是的,这个例子是站不住脚的,但是具有不同实施策略的代表的概念是一个基本的、非常古老的设计模式,我已经在很多应用程序中使用过。

    我认为你在这里的问题是一个常见的问题,但是,几乎我所看到的每一个设计模式示例都是不可思议的做作,总是让我像你一样问问题——当以这种方式呈现时,它们总是显得毫无用处。

        5
  •  2
  •   Dave DeLong    16 年前

    Mac和iPhone上使用的Cocoa框架使用策略模式A 许多 ,只不过我们称之为委托模式。以下是我们如何使用它:

    我们有一个具体的对象,比如 NSTableView . 表视图需要知道它有多少行,每行中有多少行,每列等。因此,我们提供一个“委托”对象,而不是对表视图进行子类化以提供这些信息。该委托对象实现了一个特定的接口(Objective-C中的“协议”)。然后,TableView可以简单地询问它的委托对象在某些情况下应该做什么(“我有多少行?”这个牢房里有什么?是否允许用户选择此行?”)。我们可以在运行时交换委托对象,只需分配一个符合 NSTableViewDelegate 协议。

    所以,策略模式是我最喜欢的模式之一,也是我每天使用的模式。

        6
  •  1
  •   DerrickH    16 年前

    策略模式在您(或代码用户)可能希望更改算法计算的情况下非常有用。我使用策略模式的一个简单示例是在A*搜索中建模启发式方法。A*使用启发式方法,这是一种简单的计算,如果在目标节点(NG)的路径上选择某个节点(NI),则可以估计剩余成本。我的界面如下所示:

    class Heuristic {
    public:
        virtual int estimateRemainingCost(const node &Ni, const node &Ng) const = 0;
    };
    

    它们的用法如下:

    ...
    // for each node (Ni) that is adjacent to the current node Nc
    int node_priority = cost(Ni)/* the cost of choosing this node on the path */
                        + heuristic->estimateRemainingCost(Ni, Ng);
    unsearched_nodes_.queue(node_priority, Ni);
    ...
    

    启发式是可以替换的策略或计算。换句话说,改变搜索算法的启发式是一个微不足道的练习。

        7
  •  0
  •   Malaxeur    16 年前

    当我有很多不同的事情要做时,我通常使用策略模式,这是基于情况的。实际上,这是一种将一系列if/else语句转换为几行的方法。

    一种方法(在爪哇):

    Map<String, Strategy> strategyMap = new HashMap<String, Strategy>();
    strategyMap.put("bark", new BarkingStrategy());
    strategyMap.put("meow", new MeowingStrategy());
    strategyMap.put("moo", new MooingStrategy());
    strategyMap.put("giraffeSound", new UnknownStrategy());
    

    你首先要建立某种形式的策略库。

    后来…

    String command = //...some form of input
    strategyMap.get(command).execute();
    

    这样,您就可以“一般地”处理许多不同的情况。

    即。:

    moo
    

    会执行 MooingStrategy()

        8
  •  0
  •   Jonathan Feinberg    16 年前

    这个 Context 知道如何做一些复杂的事情,考虑到您提供给它的一些操作。操作很简单(加两个数字、乘两个数字等)。一 语境 在一个不平凡的例子中,可能需要任何数量的不同行为(不仅仅是一个),最好将其分解为“策略”(因为尝试子类化 语境 将创建子类的组合爆炸)。

        9
  •  0
  •   eulerfx    16 年前

    当上下文对象具有更多的职责,并且策略抽象将这些职责与操作的某些方面分离时,这就更有意义了。一个例子(在C中)是IComparer接口:

    interface IComparer<T>
    {
        int Compare(T a, T b);
    }
    

    可以传递到排序算法中。

        10
  •  0
  •   Joaquim Rendeiro    16 年前

    主要区别在于,在第二个示例中,策略是算法(因此没有模式)。 在第一个示例中,您正在抽象/隔离算法的一部分。

    例如,实现 Context.executeStrategy() 可以是:

    public int executeStrategy(int baseValue, int exponentFactor)
    {
        return (int) Math.Pow(baseValue, this.Strategy.Calculate(2, exponentFactor));
    }
    
    推荐文章