多态与策略模式

63

策略模式和Java中的多态有什么区别?

我很困惑,无论是通过策略模式还是多态都可以实现相同的效果。如果我在这方面错了,请纠正我。

请举例说明以消除我的疑惑。


6
对我来说,这个问题让人感到困惑,因为你不能没有多态性就使用策略模式,因为必须实现接口。所以这就像是比较苹果和橙子一样不同。 - Hovercraft Full Of Eels
2
我认为他只是想要一个示例,其中策略模式比多态性更有用,这发生在您希望为多个对象重用不同的策略时。否则,这真的没有意义,您应该使用多态性。 - Joshua Byer
1
关于编程的内容:Collections.sort使用哪种设计模式 - Chetan Kinger
10个回答

53

对我来说,来自CKing的帖子链接和Wikipedia上的示例已经足够清晰了,但我会尝试给您举一个新的例子。正如他们所说,策略模式主要是一种在运行时更改算法行为的方法。当然,你可以用许多不同的方式实现这一点(比如保存一个值并使用switch-case,但这并不像策略模式那样好)。

假设你正在开发一个回合制战略游戏,有两种类型的单位步兵坦克(它们都是 Unit 的子类)。你的地形可能是平原铁路森林

class Unit{
    MovementStrategy ms;      
    final int baseMovement;
    int x,y;

    public Unit(int baseMovement){
        this.baseMovement = baseMovement;
    }

    abstract void fire();

    void moveForward(){
        x = x + ms.getHexagonsToMove(baseMovement);
    }

    void setMovementStrategy(MovementStrategy ms){
        this.ms = ms;
    }
}

任何Unit子类都必须实现fire()方法,因为对于它们来说这个方法将会完全不同(坦克发射重型长距离炮弹,而步兵则发射几颗轻型近距离子弹)。在这个例子中,我们使用正常的多态/继承,因为fire()方法对于任何单位来说都会非常不同,在游戏过程中不会改变

class Infantry extends Unit{
    public Infantry(){
        super(2);
    }

    void fire(){
        //whatever
    }
}

class Tank extends Unit{
    public Tank(){
        super(5);
    }

    void fire(){
        //whatever
    }
}

单位也能够移动,有一个字段baseMovement存储它们可以走的六边形数量。我们正在开发一款策略游戏,而不是真实世界模拟,所以我们不关心它们如何移动,我们只想为它们的坐标(在我的例子中,我仅使用X坐标以获得更简单的代码)添加一个值。如果所有地形都相同,我们就不需要任何策略对象...但是我们需要在运行时更改move()方法的行为!

因此,我们为每种地形实现一个不同的MovementStrategy类,并编写我们的游戏以触发setMovementStrategy()对于每个在六边形上移动的单位。而且,我们甚至不需要在我们的Unit子类中编写任何其他内容。

interface MovementStrategy{
    public int getHexagonsToMove(int base);
}

class PlainMovementStrategy implements MovementStrategy{
    public int getHexagonsToMove(int base){
        return base;
    }
}

class RailroadMovementStrategy implements MovementStrategy{
    public int getHexagonsToMove(int base){
        return base*3;
    }
}

class ForestMovementStrategy implements MovementStrategy{
    public int getHexagonsToMove(int base){
        return (int)(base/2);
    }
}   
现在,当任何单位森林内移动时,我们称之为
unit.setMovementStrategy(new ForestMovementStrategy());

一旦涉及到 Plain ,我们就会:

unit.setMovementStrategy(new PlainMovementStrategy());

现在我们能够根据地形的不同来改变我们的单位移动的距离,而且我们不需要在任何子类中重新编写。

希望这可以帮助您更好地理解差异。


2
只是想知道:相比每次位置改变时创建一个新对象,使用枚举在这种情况下是否有优势? - D. Ben Knoble
1
枚举在这里可以起作用,甚至可以向枚举添加抽象方法。如果你选择这条路线,我建议让枚举实现一个接口。这样,你就可以随时将枚举实现与另一个实现进行替换,而不需要对调用类进行太多的重构工作。 - aglassman
正如他们所说,策略模式主要是一种在运行时改变算法行为的方法。如果算法在运行时不会发生变化,你应该选择简单的继承吗? - Mehdi Charife

32
我很困惑策略模式所实现的内容基本上可以通过多态实现。
不能没有方向盘驾驶汽车。这并不意味着方向盘就是汽车。同样地,策略模式依赖于多态,但这并不意味着它们是同一件事情。
策略模式的目的是促进使用组合(has-a)而非继承(is-a)。你不再从超类中继承行为,而是在一个单独的类中定义行为,并且你的类有一个对它的引用。
至于示例,请看this的回答,做得很好。

14
  • 基本差异:多态是编程语言的概念,策略模式则是GoF中的一种行为型设计模式之一。

  • 多态提供了一个单一的接口,可以用于多个不同的底层数据类型。

    • 例如:方向盘(即接口)无论实际使用的转向机制是手动转向、动力转向还是齿轮齿条转向都是一样的。因此,一旦您知道如何操作方向盘,就可以驾驶任何类型的汽车。

    • 在编程中,多态有两种实现方式:

      • 早期绑定/静态/编译时多态性(如:函数重载)
      • 后期绑定/动态/运行时多态性(如:函数覆盖)
  • 策略模式定义了一组可相互替换使用的算法。

    • 策略模式是一种动态模式(如何在软件中运行行为?)。

    • Java核心中的示例:java.util.Comparator#compare(),由Collections#sort()等执行。

    • 交通工具的不同出行策略类比于策略设计模式。我们每天使用汽车、自行车、公交车、地铁等不同的策略去上班。


2
java.util.Comparator#compare() 是一个很好的例子!现在我感到羞愧,因为我没有首先想到它。我在我的项目中广泛使用它。 - rolgalan
如果java.util Comparator#compare是策略模式的一个例子,那么策略模式和多态是同一件事吗?如果是,那么多态和策略模式之间有什么区别? - Chetan Kinger

10
如果你打算建立这样一个类比:在一个情况下你有多个可覆盖的方法,在另一个情况下你有一个具有多个实现的策略接口,那么区别在于耦合程度,第一种情况中耦合程度非常强,而在第二种情况中,任何外部代码都可以通过贡献自己的策略实现来参与你的类逻辑。

9
问:Java中策略模式和多态之间有什么区别?
这个问题确实令人困惑,因为最初似乎这两个概念之间没有任何关系。
多态是编程中一个更广泛的概念,策略模式在Java中使用一种多态形式(即所谓的包含多态)来实现其意图,但这绝不是唯一存在的多态类型,也不是实现策略模式的唯一方式,我很快就会证明这一点。
此外,多态不仅存在于Java或面向对象编程语言中。所有编程范式中都存在不同形式的多态性,并且并非所有语言都必须使用多态性来实现策略模式(例如函数式语言)。
有关此主题的进一步讨论,请阅读另一个答案,我们已经讨论了是否可以在没有继承的情况下实现多态性,并提供了有趣的参考和其他类型的多态性示例,如参数多态性和特定场景多态性。
理想情况下,这将向您展示多态性是一个更大的概念,超越了面向对象编程的界限,甚至超越了继承和子类型。
“问:我很困惑,通过策略模式实现的任何内容基本上都可以通过多态性实现。如果我在这方面有错误,请纠正我。”
在我看来,这两个概念之间的关系是:策略模式利用Java等语言中可用的多态性的强大功能来实现其意图,而多态性本身可以被认为是一种模式。
例如,请考虑GoF书中的这句话:
“如果我们假设过程式语言,我们可能会包括名为'继承'、'封装'和'多态性'的设计模式。”
只是我们很少把多态性视为一种模式,首先是因为它涉及许多事情,因为它在不同的语言中以不同的方式实现,而且通常呈现为某种语言特性的形式。
在他的书《元素设计模式》中,Jason Mc C. Smith评论了上述GoF引用说:
模式是与语言无关的概念;当您在特定语言中使用一组给定的语言功能和构造实现它们时,它们会变得具体并成为解决方案[...]这意味着谈论 "Java设计模式"、"C++设计模式"、"Websphere设计模式"等有点奇怪,即使我们都这样做。这是一种略微懒惰的简写形式,代表我们真正的意思,或者应该意味着:Java、C++、WebSphere等实现的设计模式,不考虑语言或API。

因此,正如您所看到的,您从Java实现的角度考虑策略模式,但在其他语言范例中,这种模式可能以不同的方式实现,例如,在纯函数式编程语言中,这通常使用高阶函数函数组合实现。

因此,这将是一种不使用包含多态的策略模式实现。在函数组合策略中,我们仍然可以使用其他形式的多态(例如参数化),但这并不是策略模式的要求。
Q:请还提供一个示例以消除我的困惑。
如上所述,在Java中,我们可能被迫使用包含多态来实现策略模式,但正如上面所解释的,设计模式不属于特定的语言,因此如果我们将策略模式视为存在于任何语言边界之外的概念,那么您很容易看到其他语言以不同的方式实现这个模式。
在某些假设的函数式语言中,我可能有一个从文件中读取一些数据的函数,也许文件是加密的,你需要提供一个解密策略:
function readFile(path: String, decrypt: string -> string) {
    return decrypt(loadFromDisk(path));
}

而那个decrypt参数是一个函数,用于实现策略模式,它封装了可互换的算法。

现在你可以执行

readFile("customers.txt", aes)
readFile("finance.txt", blowfish)

aesblowfish是解密函数策略。

有数十种像SML、Haskell、JavaScript等的编程语言都可以这样工作。


3
首先,多态可以有两种不同的含义。最常见的多态是指多态类型。但是,您要求的是模式。
多态代码在每次运行时可以更改自身,而代码的功能保持不变。一个简单的例子是将1 + 3 = 4替换为5-1 = 4。两者使用不同的代码实现了相同的结果。这对于不想被识别的代码非常有用,例如计算机病毒或加密代码。
另一方面,策略模式使用可以互换的一组算法。这可能在翻译文本时使用。首先,一些代码确定语言。如果语言是瑞典语或西班牙语,则将使用相同家族的不同函数translateSwedish()或translateSpanish()处理文本。
总之,多态代码使用不同的代码实现相同的结果。而策略使用不同的代码以获得更好的结果。

2
考虑以下这个例子,我们有动物和一个策略模式对象来描述它们的移动方式... 例如:飞行/游泳/步行。
考虑到使用任何这些方法的动物数量很大(即成千上万种不同的动物会飞行),我们需要使用相同的代码来处理许多不同的动物。该代码应该只存在于一个地方,以便轻松更改并且不占用任何不必要的空间。
在这个例子中,简单的多态方法将导致大量的代码重复。一个更复杂的方法是将动物类和中间类(例如鸟)放在一起,但这种方法未考虑动物的移动方式并不是真正定义动物的因素。此外,动物可能有其他策略对象,而它们不能通过中间类使所有策略对象多态化。

2

多态是一种原则,策略模式是一种设计模式

来自Oracle文档页面

多态的词典定义指的是生物学中一个生物或物种可以有许多不同的形态或阶段。这个原则也可以应用于面向对象的编程和像Java语言这样的语言中。类的子类可以定义自己独特的行为,但仍然共享父类的某些功能。

多态可以在编译时(方法重载)和运行时(方法重写)实现。

策略模式

  1. 定义了一组算法,
  2. 封装每个算法,并且
  3. 使它们可以互换。

策略可以使用运行时多态原则来实现所需的功能。

策略模式还有一个称为上下文的组件。参考下面的SE帖子:

策略模式的真实世界例子

这种Java策略模式是否有多余的上下文类?

更多有用的文章:

sourcemaking的策略


简而言之,策略模式可以使用多态性。这不是一个取舍的问题。 - Kokodoko

2

多态的一种定义是为不同类型的实体提供单一接口。

以此为背景,假设你有一个“鸟”接口,所有的鸟类都必须实现“laysEggs()”方法,那么这没问题。随着你继续编写“鸟乐园程序”,你现在添加了“fly()”,并意识到对企鹅和几维鸟进行重载和覆盖是不必要的,因为它们不能飞,但仍然必须实现该方法。当你面对鸵鸟等无法飞行的鸟类时,这可能变得烦琐和无意义。更糟糕的是,当添加“swim()”方法时,因为能游泳的鸟类更少。正如你可能已经知道的那样,策略模式解决了这个问题。

简单地说,你可以将多态看作一系列实践,而策略模式则是特定情况下的最佳实践。例如:当需要在运行时选择算法行为(通过可互换的算法)时,应使用策略模式。虽然基本上可以通过多态实现策略模式所实现的任何功能,但如果没有策略模式的知识,这会让你陷入“重复制造轮子”的问题来解决这个特定的问题。总之,即使一个基于另一个,它们也是非常不同的。

如果你还需要一个代码示例,可以查看“Ender Muab'Dib”的代码,因为这是很好解释的。希望我能有所帮助,祝好!


1

以下是简明回答:

  1. 多态性是面向对象编程的基本特征之一。
  2. 策略模式是一种语言无关的模式,可以由所有编程语言范式实现,例如命令式、函数式和面向对象。

因此,如果您要使用Java(即OOP)实现策略模式,则只需使用多态性。

换句话说,策略模式是多态性的许多形式之一。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接