如果有很多类型,我不应该按对象类型进行子类化吗?

时间:2008-09-28 22:46:53

标签: java design-patterns inheritance

我正在处理事件日志,其中有大约60种不同的“类型”事件。每个事件共享大约10个属性,然后存在共享各种额外属性的事件的子类别。

我如何处理这些事件取决于它们的类型或它们实现的分类接口。

但它似乎导致代码臃肿。我在子类方法中有很多冗余,因为它们实现了一些相同的接口。

使用具有“type”属性的单个事件类并编写检查类型并维护某些类型组织的逻辑(例如,类别a的事件类型列表,第二个列表是b类等)?或者在这种情况下子类设计更合适?

第一种方法:

public interface Category1 {}
public interface Category2 {}

public abstract class Event {
 private base properties...;
}

public class EventType1 extends Event implements Category1, Category2 {
 private extra properties ...;
}

public class EventType2 extends Event implements Category3, Category4 {
 private extra properties ...;
}

第二种方法:

public enum EventType {TYPE1, TYPE2, TYPE3, ...}
public class Event {
 private union of all possible properties;
 private EventType type;
}

我个人认为,似乎单个事件对象是合适的,因为,如果我正确地思考它,就不需要使用继承来表示模型,因为它实际上只是行为和我的根据类型改变的条件。

我需要有类似的代码:

if(event instanceof Category1) {
  ...
}

这在第一种方法中运行良好,因为我可以只调用事件上的方法,并在每个适当的子类中实现“相同的代码”。

但第二种方法更加简洁。然后我写下这样的东西:

if(CATEGORY1_TYPES.contains(event.getEventType()) {
 ...
}

并且我的所有“处理逻辑”都可以组织成一个单独的类,并且它们都不会在子类中冗余地分布。那么,虽然OO看起来更合适,但最好不要这样做吗?

6 个答案:

答案 0 :(得分:1)

我会使用每个事件类型解决方案的对象,但我会将常用的​​接口组合分组(可能是抽象的)类,提供骨架实现。这极大地减少了由于具有许多接口而产生的代码膨胀,但另一方面,增加了类的数量。但是,如果使用得当且合理,可以使代码更清晰。

答案 1 :(得分:1)

如果您决定扩展a的抽象基类,则继承可能会受到限制 特定的类别界面,因为您可能还需要实现另一个类别。

所以,这是一个建议的方法: 假设您需要为特定的Category接口方法实现相同的实现(无论事件如何),您可以为每个Category接口编写一个实现类。

所以你会:

public class Category1Impl implements Category1 {
    ...
}

public class Category2Impl implements Category2 {
    ...
}

然后,对于每个Event类,只需指定它实现的Category接口,并保留Category实现类的私有成员实例(因此使用合成而不是继承)。对于每个Category接口方法,只需将方法调用转发给Category实现类。

答案 2 :(得分:1)

由于我没有真正得到我正在寻找的答案,因此根据我不太理想的学习经验,我提供了自己最好的猜测。

事件本身实际上没有行为,它是具有行为的事件的处理者。事件只代表数据模型。

我重写了代码,只是将事件视为属性的对象数组,以便我可以使用Java的新变量参数和自动装箱功能。

通过这一更改,我能够删除大约100个巨大的代码类,并在单个类中的大约10行代码中完成大部分相同的逻辑。

经验教训:将OO范例应用于数据模型并不总是明智的。在处理大型可变域时,不要专注于通过OO提供完美的数据模型。 OO设计有时比模型更有益于控制器。不要先关注优化,因为通常10%的性能损失是可以接受的,并且可以通过其他方式重新获得。

基本上,我过度设计了这个问题。事实证明,这是一个适当的OO设计过度的情况,并将一个晚上的项目变成一个3个月的项目。当然,我必须以艰难的方式学习东西!

答案 3 :(得分:0)

仅仅拥有大量的.java文件并不一定是坏事。如果您可以有意义地提取代表类的合同的少量(2-4左右)接口,然后打包所有实现,那么您提供的API可以非常干净,即使有60个实现。

我可能还建议使用一些委托或抽象类来引入通用功能。代理和/或抽象助手都应该是包私有或类私有的,在您公开的API之外不可用。

答案 4 :(得分:0)

如果有相当多的混合和行为匹配,我会考虑使用其他对象的组合,然后让特定事件类型对象的构造函数创建这些对象,或者使用构建器来创建对象。

也许是这样的?

class EventType {

  protected EventPropertyHandler handler;

  public EventType(EventPropertyHandler h) {
     handler = h;
  }

  void handleEvent(map<String,String> properties) {
    handler.handle(properties);
  }
}

abstract class EventPropertyHandler {
   abstract void handle(map<String, String> properties);
}
class SomeHandler extends EventPropertyHandler {
   void handle(map<String, String> properties) {
      String value = properties.get("somekey");
      // do something with value..
   }
}

class EventBuilder {
   public static EventType buildSomeEventType() {
      // 
      EventType e = new EventType( new SomeHandler() );
   }
}

可能会有一些改进,但这可能会让你开始。

答案 5 :(得分:0)

这取决于每种类型的事件本身是否具有事件本身可以执行的不同行为。

您的Event对象是否需要每种类型的行为不同的方法?如果是这样,请使用继承。

如果没有,请使用枚举对事件类型进行分类。