【问题标题】:Avoiding Nested If Else / Switches - Java避免嵌套 If Else / Switches - Java
【发布时间】:2013-01-10 00:53:17
【问题描述】:

我正在审查一些代码(Java)并根据业务逻辑流程图进行更改。当前的代码依赖于大量的 if 语句——我想尝试摆脱这种情况。我一直在阅读有关多态性的内容,并试图围绕如何将其应用到我的情况来思考。我可以使它适用于单个条件级别,但很难将其进一步扩展到多个条件级别。代码将在运行时执行,此“逻辑”方法将传递上一步中的变量。

人为的例子: 我们有 2 个动物园,“动物园 A”和“动物园 B”,以及“家”。这些中的每一个都是一个“地方”。在每个动物园中,我们有 4 个“位置”、“北”、“南”、“东”和“西”。 “家”只有一个位置。 我们想根据几个变量为一个人分配一个“目的地”,他们应该去哪里。 这些变量是: “地点”,与我们的地点相关(动物园 A、动物园 B、家)。 '方向',与我们的位置相关,(N,S,E,W)。 流程图:

                |----- | 'HOME'
                |Place?| ----- >  *destination = 'home'*
                |----- |
     Zoo A          |                               Zoo B
    |---------------|----------------------------------------|
|----------|                                        |----------| 
|Direction?|                                        |Direction?| 
|----------|                                        |----------|
    |    North                                          |    North
    ----------- *destination = 'Zoo A North'            ----------- *destination = 'Zoo B North'
    |    East                                           |    East
    ----------- *destination = 'Zoo A East'             ----------- *destination = 'Zoo B East'
    |    South                                          |    South
    ----------- *destination = 'Zoo A South'            ----------- *destination = 'Zoo B South'
    |    West                                           |    West
    ----------- *destination = 'Zoo A West'             ----------- *destination = 'Zoo B West'

所以如果人 X 有一个动物园 A 的地点和一个南的方向,他们应该有一个“南动物园”的目的地

我的代码目前使用 If 语句非常难看:

if(Place = 'HOME')
    destination = 'HOME'
if(Place = 'Zoo A')
    if(Direction = North)
        destination = 'Zoo A North')
    if(Direct = East)
        destination = 'Zoo A East')
    ...
if(Place = 'Zoo B')
    if(Direction = North)
        destination = 'Zoo B North')
    if(Direct = East)
        destination = 'Zoo B East')
    ...

我可以将其转换为嵌套开关,其中变量为 ENUM。但我试图避免 if - else / switch 依赖,因为我有一个坏习惯,陷入其中。我尝试使用 Factory Design 来生成 Place 类,然后在每个位置和目的地使用多态,但它开始在我的脑海中变得过于复杂。是否值得远离 if/switches?我只是想过度设计它吗?

关于如何处理这样的逻辑流程有什么建议吗? 谢谢

【问题讨论】:

  • 我并不完全清楚您在示例中要做什么。是“地点”和“方向”字符串还是其他类型的对象?无论哪种方式,一个简单的示例可能是在您的Zoo 类中创建goNorth() 等方法。不过,我不完全确定这会解决您的问题,因为我并不完全清楚您要做什么。
  • @Code-Guru - 为简单起见,假设它们都是字符串。我要做的是从流程的顶部开始,并根据变量(方向和地点)最终得到一个“目的地”。但以最易于管理和维护的方式

标签: java polymorphism if-statement


【解决方案1】:

这可以这样建模:

  1. 使用根类Place,方法为calculateDestination(Person)。地点可以包含其中的其他地点。
  2. ZooZooQuadrant 创建Place 子类(当然,因为这些是实际位置)。
  3. Person 对象具有 currentPlacecurrentDirection 的值

现在您将实例化这些类的对象来代表您的情况:

zooA = new Zoo("ZooA");
zooA.addChild(new ZooQuadrant(Direction.SOUTH));
... so on for every quadrant ...
... same for zooB ...
home = new Place("Home");
world = new Place("World");
world.addChild(home);
world.addChild(zooA);
world.addChild(zooB);

当你想到达目的地时,你会打电话给world.calculateDestination(myPerson)

calculateDestination(Person) 是多态方法。继承层次结构中的每个级别都会根据该类的特定语义覆盖它。

  1. Place 将有一个通用实现来测试Person 实例当前是否在该节点(通过测试PersoncurrentPlace 值),如果不是,它将在每个节点上调用calculateDestination并返回它的孩子。
  2. Zoos 需要检查是否为currentPlace == this,如果是,则在其每个象限上调用calculateDestination,并将任何阳性结果与其自身相结合以返回this.name + quadrantResult
  3. 每个ZooQuadrant只需要检查currentDirection是否等价于自己的方向,并相应地返回一个值。

注意:这只是为了说明多态是如何工作的,可能会有更好的实现。另外,这里我们同时使用了多态性和递归,两者是独立的。


编辑:

至于是否需要增加复杂性,这取决于!在这里,我们使用一个非常小的对象图的简单示例。一旦你有几十个动物园,必须在这些动物园中添加更多象限,或者需要做出额外级别的决策(例如,如果每个象限都有子象限),嵌套的 if-else-if 方法(程序)变得非常毛茸茸的很快,而面向对象的方法仍然可以维护和理解。

像所有事情一样,如果您预见到决策会变得如此复杂,请采用 OO 方法。否则,每次都保持简单胜过:使用正确的工具解决正确的问题。

【讨论】:

  • 谢谢你的建议,我已经建立了一个工作模型(它太长了不能发布:()。看着它,我想我只是想让我正在做的事情过于复杂。不过它确实很好用!嗯..
【解决方案2】:

一种方法是您可以使用 getNorthDestination()、getEastDestination() 等方法创建父抽象类/接口“Place”。

然后你创建一个名为'ZooA'和'ZooB'的'Place'的子类/实现,并覆盖/实现getXXXDestination()方法以返回相应的位置

【讨论】:

  • 需要更多的工作来维护,比如添加方向 SouthEast、Up、Down,你最终不得不在很多地方添加方法。
  • 这就是我想要避免的,未来的变化可能会增加一个新的动物园,或者一个不破坏逻辑的新方向。 @gerrytan - 这是个好主意,它将 if 块限制为 if(Place place = Zoo A) return Place = new Zoo A ...然后只需调用方向上的开关 case North: place.goNorth();。对吗?
【解决方案3】:

从这里我看到至少三个类是 Place、Direction 和 Destination。

Place 将有一个 name 属性和一个 getName() 方法,名称被设置为 Zoo A、Zoo B、Home。

如果 Home 和 Zoo 具有不同的行为,您可以创建 Place 的子类。 在这种情况下,您可以这样做,因为 Home 没有方向,但 Zoo 有。

Direction 可以是一个包含北、东、西、南的枚举(它只是一种特殊类型的类)。

Destination 将具有两个属性,即 Place 和 Direction。它还有一个方法 getDestination()

public String getDestination(){
    if (this.direction == null){
        result = this.place.getName();
    } else {
        result = this.place.getName() + " " + this.direction.getName();
    }
    return result;
}

【讨论】:

    【解决方案4】:

    跟着 gerrytan 的回答走。对于每条信息,您都应该问自己:“我真的需要为此开设课程吗?”。很多时候,答案是否定的。简单的字符串/数值变量就足够了。现在,您希望将这些变量与 getter/setter 方法结合起来,因为这是 java 所强调的,而不是直接引用公共变量(例如 C++)。引用简单的方法比测试实例要容易得多。

    【讨论】:

      【解决方案5】:

      你可以用Zoo A和Zoo B做一个新的方法...... 你可以像 directionForZoo() 一样调用它,如果尝试使用 while 循环

      【讨论】:

        【解决方案6】:

        如果您不想过度设计它,以下解决方法将是摆脱 if/else 的简单解决方案。但这不是一种优雅的方法。

        您可以有一个地图,其中键是(地点+方向),值是相应的目的地。仅当 Place 和 Direction 值在您的程序中更像是静态的并且不太可能发生太大变化时,这才可以。

        例如:将您的地点和相应的目的地存储在地图中

        Map<String, String> destMap = new HashMap<String, String>();
        destMap.put("HOME","HOME");
        destMap.put("Zoo A+North","Zoo A North");
        destMap.put("Zoo A+East","Zoo A East");
        destMap.put("Zoo B+North","Zoo B North");
        

        根据地点和方向检索目的地:

        destMap.get(Place + "+" + Direction);
        

        【讨论】:

          【解决方案7】:

          您的示例的一个可能解决方案是创建一个(可能是抽象的)Location 类。这可以包含您的目的地的数据结构。这种数据结构的一种可能性是Map&lt;Direction, Location&gt;),其中Direction 是一个枚举,它可以用作映射到Location 对象的键,该对象是该方向的目的地。您可以将Location 子类化以创建HomeZoo 等类,或者您可以提供一个name 字段来区分不同的Locations。或者您可以将这两者结合起来。

          请注意,这是一个半生不熟的设计,可能会也可能不会满足您的需求。好的 OO 设计需要经验和详细了解您要解决的问题的确切要求。对前者有一些了解,但对后者知之甚少。

          【讨论】:

            【解决方案8】:

            我尝试了一些建议答案的变体。

            我最终使用了一个嵌套的 switch case 块。不是最理想的,也是我想避免的,但出于我的目的,它更易于维护(它不会改变或扩展)。

            我将@Ezequiel Muns 方法标记为正确,因为我的版本运行良好 - 它只是不是问题所需要的。

            感谢所有帮助。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-09-27
              相关资源
              最近更新 更多