代码之家  ›  专栏  ›  技术社区  ›  Josh

如果有许多类型,我应该不按对象类型进行子类划分吗?

  •  1
  • Josh  · 技术社区  · 16 年前

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

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

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

    是否更适合使用具有“类型”属性的单个事件类,并编写检查类型和维护某些类型组织的逻辑(例如,属于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 回复  |  直到 16 年前
        1
  •  0
  •   Scott Stanchfield    16 年前

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

    事件对象是否需要不同类型的方法?如果是,使用继承。

    如果不是,则使用枚举对事件类型进行分类。

        2
  •  1
  •   petr k.    16 年前

    我将使用每事件类型的对象解决方案,但我将把常用的接口组合分组到(可能是抽象的)类下,提供它们的 骨架实现 . 这大大减少了由多个接口生成的代码膨胀,但另一方面,增加了类的数量。但是,如果使用得当和合理,就会导致代码更干净。

        3
  •  1
  •   pythonquick    16 年前

    如果决定扩展 特定的类别接口,因为您可能还需要实现另一个类别。

    因此,这里有一个建议的方法: 假设您需要为一个特定的类别接口方法使用相同的实现(不管事件如何),您可以为每个类别接口编写一个实现类。

    所以你应该:

    public class Category1Impl implements Category1 {
        ...
    }
    
    public class Category2Impl implements Category2 {
        ...
    }
    

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

        4
  •  1
  •   Josh    16 年前

    因为我没有真正得到我想要的答案,所以我根据我不太理想的学习经验给出了自己最好的猜测。

    事件本身实际上没有行为,而是事件的处理程序有行为。事件只是表示数据模型。

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

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

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

    基本上,我对这个问题过于工程化了。事实证明,在这种情况下,正确的OO设计是多余的,将一个一夜的项目变成了一个3个月的项目。当然,我必须用艰苦的方式去学习!

        5
  •  0
  •   James A. Rosen    16 年前

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

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

        6
  •  0
  •   Brandon    16 年前

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

    也许是这样?

    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() );
       }
    }
    

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