OO设计:继承与类型(枚举)变量

时间:2016-12-06 18:02:56

标签: java oop inheritance design-patterns enums

使用继承时是否有一般的最佳做法:

public class Information {
  private String text;
}

public class Message extends Information {
}

public class Faq extends Information {
}

public class News extends Information {
}

以及何时使用enum作为成员变量来区分:

public enum InformationType {
  MESSAGE, FAQ, NEWS
}

public class Information {
  private String text;
  private InformationType type;
}

在我使用的代码中,我有第二个 - 类型字段,困扰我的是代码

public void displayInformation(final Information information) {
  if (information.getType == InformationType.MESSAGE) {
     ....
  } else if (information.getType == InformationType.FAQ) {
     ....
  } else if (information.getType == InformationType.NEWS) {
     ....
  } else {
     // some error handling
  }
}

这绝对是代码味道,我不知道如何将枚举作为成员变量而不是这些if-elses!?如果这是真的,那么你永远不应该把枚举作为对象的类型,但我看到了这个"模式"经常!

4 个答案:

答案 0 :(得分:3)

针对不同工作的不同工具。

<强> 1。使用枚举类来声明在编译时已知的一组固定常量。

enum课程方便,干净地描述的事物的好例子是:从一副扑克牌和太阳系中的行星中得到适合和排名。这两个例子都有一个共同点,那就是对象是常量的(或者相当稳定;没有法律来反对进化枚举类),并且可以在程序执行开始时实例化。

枚举类与普通的Java类非常相似。您可以通过在枚举类中定义实例字段和构造函数来为每个常量添加特定于常量的数据,并且可以通过编写常量特定的方法来定义特定于常量的行为。

在Java中编写丰富的枚举类型很容易,它们可以非常强大。如果使用得当,它们将使您的代码更加简化和易于维护。当您处理一组固定的常量对象时,枚举类通常是最佳选择。

具有常量特定方法的枚举通常会使您的程序代码更易于阅读并且易于维护。例如,而不是:

public void displayInformation(final Information information) {
    if (information.getType == InformationType.MESSAGE) {
        ....
    } else if (information.getType == InformationType.FAQ) {
        ....
    } else if (information.getType == InformationType.NEWS) {
        ....
    } else {
        // some error handling
}

您可以在枚举类InformationType中定义一个抽象方法,例如displayInfo(),然后编写常量特定的方法,如下所示:

public enum InformationType {
    MESSAGE {
        @Override 
        public String displayInfo() {
            ....
        }
    },
    FAQ {
        @Override 
        public String displayInfo() {
            ....
        }
    },
    :
    :
    LAST_TYPE {
        @Override
        public String displayInfo() {
            ....
        }
    };

    abstract public String displayInfo();
}

在丰富的枚举类型中以这种方式使用常量特定的方法是在代码中使用switch-case块的一个非常好的替代方法,这可能会很快变得非常难看。一旦实现了这种丰富的枚举类型,就可以用更清晰,更易读的方式替换原始方法:

public void displayInformation(final Information information) {
    information.getType().displayInfo();
}

<强> 2。当您希望使用与超类具有“是”关系的任意数量的对象时,请使用子类。

子类是扩展超类的普通Java类。使用像这样的层次结构的正确时间是,类的实例都与超类具有“是”关系。原型示例是超类Bicycle和子类MountainBike。所有山地自行车都是自行车。

如果子类和超类之间没有明确定义的“是一种”关系,那么你最好不要使用继承(更喜欢组合而不是继承)。

第3。避免使用枚举常量描述层次结构的陷阱。

例如,您有一个类Shape,其实例字段包含类型为ShapeType的枚举常量(并且您将枚举类编写为具有常量CIRCLE,RECTANGLE,TRIANGE)。

当这样的标记类很简单时,有时使用实例字段作为标记是正确的做法,但要避免使用标记作为类层次结构的伪装伪装的缺陷。当然,表示Shape的最佳和最干净的方式是带有子类CircleRectangleTriangle的抽象超类(所有这些 - 都是 - 形状)。

答案 1 :(得分:0)

最终,编程中的大部分挑战都是将每个概念和决策放在一个地方,以最大限度地降低项目的变更成本。

在这种情况下,如果只有一个地方根据InformationType有不同的行为,那么可以使用if / elses列表,因为它不再是制作几个不同的子类的代码(事实上,它的代码略少一些)。

但是,如果您需要在代码中的多个位置进行区分,那么最好将其抽象为InformationType特定的代码块(比如让子类实现公共接口),否则您将复制代码。

答案 2 :(得分:0)

“战略模式”通常可以解决这样的问题。

“策略消除条件语句。策略模式为条件语句提供了一种替代选择所需行为的替代方法。当不同的行为被集中到一个类中时,很难避免使用条件语句来选择正确的行为。将行为封装在单独的行为中策略类消除了这些条件语句。“ http://java.boot.by/scea5-guide/ch07s03.html

答案 3 :(得分:-1)

在我看来,枚举是正确的做法。

您可以使用Map来代替多个if-else行。这将演示文稿与数据分开:

private final Map<InformationType, String> infoFormats;

{
    Map<InformationType, String> map = new EnumMap<>(InformationType.class);
    map.put(InformationType.MESSAGE, "Message: %s");
    map.put(InformationType.FAQ,     "FAQ item: %s");
    map.put(InformationType.NEWS,    "News: %s");

    assert map.keySet().containsAll(EnumSet.allOf(InformationType.class)) :
        "Programmer error:  Not all InformationType values accounted for!";

    infoFormats = Collections.unmodifiableMap(map);
}

// ...

public void displayInformation(final Information information) {
    String format = infoFormats.get(information.getType());
    String rawText = information.getText();
    String text = String.format(format, rawText);
    show(text);
}

如果显示文本的差异比仅仅格式更重要,则Map可以是函数图:

private final Map<InformationType, UnaryOperator<String>> infoFormats;

{
    Map<InformationType, String> map = new EnumMap<>(InformationType.class);
    map.put(InformationType.MESSAGE, s -> String.format("Message (%,d chars): %s", s.length(), s));
    map.put(InformationType.FAQ,     s -> "FAQ item: " + s);
    map.put(InformationType.NEWS,    s -> String.format("%tF %<tT - %s", Instant.now(), s));

    assert map.keySet().containsAll(EnumSet.allOf(InformationType.class)) :
        "Programmer error:  Not all InformationType values accounted for!";

    infoFormats = Collections.unmodifiableMap(map);
}

// ...

public void displayInformation(final Information information) {
    String rawText = information.getText();
    String text = infoFormats.get(information.getType()).apply(rawText);
    show(text);
}

如果有必要,您可以使用Map<InformationType, Function<Information, String>>进行更多自定义。