Не следует ли мне создавать подклассы по типу объекта, если их много?

StackOverflow https://stackoverflow.com/questions/146931

Вопрос

Я работаю с журналом событий, в котором содержится около 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()) {
 ...
}

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

Это было полезно?

Решение

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

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

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

Другие советы

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

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

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

Итак, у вас будет:

public class Category1Impl implements Category1 {
    ...
}

public class Category2Impl implements Category2 {
    ...
}

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

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

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

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

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top