Должен ли я не создавать подклассы по типу объекта, если есть много типов? - PullRequest
1 голос
/ 29 сентября 2008

Я работаю с журналом событий, где существует около 60 различных «типов» событий. Каждое событие имеет около 10 свойств, а затем есть подкатегории событий, которые имеют различные дополнительные свойства.

То, как я работаю с этими событиями, зависит от их типа или от того, какие категориальные интерфейсы они реализуют.

Но, похоже, это приводит к раздуванию кода. У меня много избыточности в методах подкласса, потому что они реализуют одни и те же интерфейсы.

Уместнее ли использовать один класс событий со свойством «type» и логику записи, которая проверяет тип и поддерживает некоторую организацию категорий типов (например, список типов событий, которые относятся к категории a, второй список, который категория б и т. д.)? Или дизайн подкласса более уместен в этом случае?

Первый подход:

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) {
  ...
}

Это хорошо работает при первом подходе, поскольку вместо instanceof я могу просто вызывать метод для события и реализовывать «один и тот же код» в каждом из соответствующих подклассов.

Но второй подход гораздо более лаконичен. Тогда я пишу такие вещи, как:

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

И вся моя «логика обработки» может быть организована в один класс, и ни один из них не будет избыточно распределен среди подклассов. Так это тот случай, когда ОО выглядит более подходящим, но не лучше?

Ответы [ 6 ]

1 голос
/ 06 октября 2008

Поскольку я на самом деле не получил ответы, которые искал, я даю свою собственную лучшую догадку, основанную на моем нежелательном опыте обучения.

Сами события на самом деле не имеют поведения, а обработчики событий имеют поведение. События просто представляют модель данных.

Я переписал код так, чтобы он рассматривал события как объектные массивы свойств, чтобы я мог использовать новые переменные аргументы Java и функции автобокса.

С этим изменением мне удалось удалить около 100 гигантских классов кода и выполнить большую часть той же логики в примерно 10 строках кода в одном классе.

Извлеченные уроки: не всегда целесообразно применять ОО-парадигмы к модели данных. Не концентрируйтесь на предоставлении идеальной модели данных через ОО при работе с большой переменной областью. ОО дизайн иногда приносит больше пользы контроллеру, чем модели. Не зацикливайтесь также на оптимизации заранее, поскольку обычно потеря производительности на 10% является приемлемой и может быть восстановлена ​​другими способами.

По сути, я перерабатывал проблему. Оказывается, это тот случай, когда правильное проектирование ОО излишне и превращает однодневный проект в трехмесячный проект. Конечно, я должен научиться чему-то нелегкому!

1 голос
/ 29 сентября 2008

Наследование может быть ограничено, если вы решите расширить абстрактный базовый класс интерфейс конкретной категории, потому что вам может потребоваться реализовать и другую категорию.

Итак, вот предлагаемый подход: Предполагая, что вам нужна одна и та же реализация для определенного метода интерфейса категории (независимо от события), вы можете написать класс реализации для каждого интерфейса категории.

Итак, вы бы получили:

public class Category1Impl implements Category1 {
    ...
}

public class Category2Impl implements Category2 {
    ...
}

Затем для каждого из ваших классов Event просто укажите интерфейсы Category, которые он реализует, и сохраните частный экземпляр класса реализации Category (так что вы используете композицию, а не наследование). Для каждого из методов интерфейса Category просто перенаправьте вызов метода в класс реализации Category.

1 голос
/ 29 сентября 2008

Я бы выбрал решение типа объекта для каждого события, но вместо этого я бы сгруппировал часто используемые комбинации интерфейсов в (возможно, абстрактные) классы, предоставляя их скелетные реализации . Это значительно уменьшает раздувание кода, создаваемое множеством интерфейсов, но, с другой стороны, увеличивает количество классов. Но при правильном и разумном использовании это приводит к более чистому коду.

0 голосов
/ 29 сентября 2008

Зависит от того, имеет ли каждый тип события по своей природе поведение, которое само событие может выполнить.

Ваши объекты Event нуждаются в методах, которые ведут себя по-разному в зависимости от типа? Если это так, используйте наследование.

Если нет, используйте enum для классификации типа события.

0 голосов
/ 29 сентября 2008

Если существует значительное смешение и сопоставление поведения, я бы рассмотрел использование композиции других объектов, а затем попросил бы конструктор объекта определенного типа события создать эти объекты или использовать конструктор для создания объекта.

возможно что-то вроде этого?

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

Возможно, есть некоторые улучшения, которые могут быть сделаны, но это может помочь вам начать.

0 голосов
/ 29 сентября 2008

Наличие большого количества файлов .java не обязательно плохо. Если вы можете сознательно извлечь небольшое количество (2-4 или около того) Интерфейсов, представляющих контракты классов, а затем упаковать все реализации, представленный вами API может быть очень чистым, даже с 60 реализациями.

Я также мог бы предложить использовать некоторые делегированные или абстрактные классы для использования общих функций. Все делегаты и / или абстрактные помощники должны быть частными для пакета или частными для класса и недоступными вне предоставляемого вами API.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...