多态性与策略模式

问题描述 投票:35回答:9

Java中的Strategy模式和Polymorphism有什么区别?

我很困惑,通过策略模式实现的任何事情基本上都可以通过多态实现。如果我在这方面错了,请纠正我。

请给我一个根除我的困惑的例子。

java design-patterns polymorphism strategy-pattern
9个回答
33
投票

对我来说,来自CKing帖子和维基百科中的例子的链接已经足够清楚,但我会尝试给你一个新的例子。正如他们所说,策略模式主要是一种在运行时更改算法行为的方法。当然,你可以通过许多不同的方式实现这一点(比如持有一个值并使用switch-case,但它不如Strategy Pattern那么好)。

假设您正在开发一种基于回合制的战略游戏,其中包括两种单位:步兵和坦克(单位的子类)。你的地形可能是平原,铁路或森林。

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()方法的行为!

因此,我们为每种Terrain实现了一个不同的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());

一旦它进入平原,我们做:

unit.setMovementStrategy(new PlainMovementStrategy());

现在我们可以根据地形改变我们单位移动的距离,我们不需要在任何子类中重写。

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


22
投票

我很困惑,通过策略模式实现的任何事情基本上都可以通过多态实现。

没有方向盘你就无法开车。这并不意味着方向盘就是汽车。同样,策略模式依赖于多态,但这并不意味着它们是同一个东西。

策略模式的目的是促进组合(has-a)的使用而不是继承(is-a)。您可以在单独的类中定义行为,而不是您的类从超类继承行为,而您的类具有对它的引用。

就一个例子而言,看看this做得很好的答案。


9
投票

具有核心java示例的多态性与策略模式

  • 基本区别:多态性是编程语言的概念,策略模式是behavioral design pattern of GoF之一。
  • 多态性是为不同类型的实体提供单一接口。 示例:无论使用何种类型的实际转向机构,方向盘(即接口)都是相同的。也就是说,无论您的汽车是手动转向,动力转向还是齿条齿轮转向,方向盘都能正常工作。因此,一旦您知道如何操作方向盘,您就可以驾驶任何类型的汽车。
  • 在编程中,多态性以两种方式实现: 早期绑定/静态/编译时多态性(例如:函数重载) 后期绑定/动态/运行时多态性(例如:函数重写)

编译时:您(开发人员)编译代码的时间段。 运行时间:用户运行您的软件的时间段。 Source

  • Strategy pattern定义了一组可互换使用的算法。 策略模式是一种动态模式(您希望如何在软件中运行行为?)。 核心java的例子:java.util.Comparator#compare(),由Collections#sort()等人执行。 运输方式类似于战略设计模式。我们使用汽车,自行车,公共汽车,当地火车等..日常办公的不同策略。

7
投票

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

问题肯定令人困惑,因为最初这两个想法之间似乎没有任何关系。

多态性在编程中是一个更广泛的概念,是的,Java中的策略模式使用一种多态性形式编目为inclusion polymorphism来实现其意图,但这绝不是唯一存在的多态性类型,也不是唯一的实现方式我即将展示的战略模式。

多态性也不仅仅存在于Java或面向对象的编程语言中。在所有编程范例中都存在不同形式的多态,而在所有语言中,您不得不使用多态来实现策略模式(例如,函数式语言)。

有关此主题的进一步讨论,请阅读this other answer,其中我们讨论了多态性是否可能没有继承,我提供了有趣的参考和示例到其他类型的多态,如参数和ad-hoc多态。

理想情况下,这将向您揭示多态性是一个更大的概念,它超越了面向对象编程的界限,甚至超越了继承和子类型。

问:我很困惑,通过策略模式实现的任何事情基本上都可以通过多态实现。如果我在这方面错了,请纠正我。

从我的观点来看,这两个概念之间的关系是:策略模式利用Java等语言中可用的多态性的力量来实现其意图,并且多态性本身可以被视为一种模式。

例如,请考虑GoF书中的这句话:

如果我们假设过程语言,我们可能会包含称为“继承”,“封装”和“多态”的设计模式。

只是我们很少将多态性视为一种模式,首先是因为它意味着许多事情,并且因为它在不同语言中的实现方式不同,而且因为它通常表现为某种形式的语言特征。

杰森麦克史密斯在他的“元素设计模式”一书中评论了上面的GoF引用:

模式是与语言无关的概念;当你用一组给定的语言特性和结构在特定的语言中实现它们时,它们采取形式并成为具体的解决方案[...]这意味着谈论“Java设计模式”,“C ++设计模式”有点奇怪“,”Websphere设计模式“等等,即使我们都这样做。对于我们真正的意思,或者应该意味着:设计模式是用Java,C ++,WebSphere等实现的,无论语言或API如何,它都是一种温和的懒惰形式。

因此,正如您所看到的,您正在从Java实现的角度思考策略模式,但在其他语言范例中,这种模式可能已经以不同的方式实现,可能根本不使用继承,例如,在纯函数中编程语言这肯定是使用high order functionsfunction composition实现的。

因此,这将是一种策略模式实现,而根本不依赖于包含多态性。在函数组合策略中,我们可能仍在使用其他形式的多态(例如参数),但这不是策略模式的要求

问:请给我一个根除我的困惑的例子。

如上所述,在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等。


5
投票

如果您要建立一个类比,其中:

  • 在一种情况下,你有几种可覆盖的方法;
  • 在另一种情况下,您有一个包含多个实现的Strategy接口,

那么差异就是耦合程度,在第一种情况下非常强,而在第二种情况下,任何外国代码都可以通过贡献其战略实施来参与你的阶级逻辑。


3
投票

首先。多态性可能意味着两种不同的东西。最常见的多态性是指多态类型。但是,你要求的模式。

多态代码每次运行时都可以自行更改,而代码的功能保持不变。一个简单的例子是1 + 3 = 4而不是5-1 = 4。两者都使用不同的代码实现相同的结果。这对于不希望被识别的代码很有用,即计算机病毒或加密代码。

另一方面,策略模式使用一系列可以互换的算法。在翻译文本时可能会使用此选项。首先,一些代码确定语言。如果语言是瑞典语或西班牙语,则该文本将由同一系列的不同功能,translateSwedish()或translateSpanish()处理。

把事情搞砸了。多态代码使用不同的代码来实现相同的结果。虽然策略使用不同的代码来实现更好的结果。


2
投票

考虑一下

我们有动物和战略模式对象来描述他们如何移动......例如

飞/游泳/步行

鉴于使用这些方法中的大量动物(即成千上万种不同的动物飞行),我们需要对许多不同的动物使用相同的代码。该代码应该只存在于一个地方,因此它很容易更改,不会占用任何不需要的空间。

在此示例中,直接的多态方法将导致大量代码重复。一种更为复杂的方法是在动物和知更鸟之间放置一个中间阶层而不考虑动物如何移动并不是真正定义动物的方法。此外,动物可能具有其他策略对象,并且它们不能通过中间类来制作多态。


2
投票

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

考虑到这一点,你说你有一个“鸟”界面,你所有的鸟类都必须实现“laysEggs()”方法,没有大问题可行。当你继续编写你的“鸟类天堂程序”时,你现在添加“fly()”并意识到企鹅和猕猴桃的重载和覆盖是不必要的,因为在现实生活中它们无法飞行,但你仍然必须实现这种方法。当你面对鸵鸟和其他无法飞行的人时,这会变得单调乏味而且毫无意义。甚至在添加“游泳()”方法时最糟糕的是因为更少的鸟类可以游泳。您可能已经知道,策略模式解决了这个问题。

在跛足者的术语中,您可以将多态性视为实践的集合体,而策略模式是特定案例的最佳实践。示例:当需要在运行时选择算法的行为时(通过可互换的算法),将使用策略模式。尽管通过多样性基本上可以实现通过策略模式实现的任何事情都是正确的,但是如果不了解策略模式,这将使您“重新发明轮子”问题来解决这个特定问题。总之,即使一个基于另一个,它们也是非常不同的。我会让你看到“Ender Muab'Dib”代码,因为如果你仍然想要我的代码示例,请问,欢呼和希望我帮助,这是很好的解释。


2
投票

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

来自oracle文档page

多态性的字典定义是指生物学中的原理,其中生物体或物种可以具有许多不同的形式或阶段。这个原则也可以应用于面向对象的编程和Java语言之类的语言。类的子类可以定义它们自己的唯一行为,但仍然共享父类的一些相同功能。

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

Strategy_pattern

  1. 定义一系列算法,
  2. 封装每个算法,和
  3. 使算法在该族中可互换。

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

策略模式在其URL图中还有一个名为Context的组件。请参阅以下SE帖子:

Real World Example of the Strategy Pattern

Does this Java Strategy pattern have a redundant Context class?

几篇有用的文章:

来源制造的strategy

© www.soinside.com 2019 - 2024. All rights reserved.