Можно ли иметь «пустой» класс, который расширяет другой класс? - PullRequest
9 голосов
/ 27 сентября 2008

Допустим, у меня есть один класс Foo, в котором есть куча логики, и другой класс Bar, который по сути одинаков. Однако, поскольку Foo и Bar являются различными (но связанными) сущностями, мне нужно, чтобы различие было очевидно из моего кода (т.е. я могу сказать, является ли экземпляр Foo или Bar)

Пока я бился об этом, не задумываясь, я получил следующее:

public class Foo {
  /* constructors, fields, method, logic and what-not */
}

public class Bar extends Foo {
  /* nothing here but constructors */ 
}

Это нормально? Что лучше сделать Bar составным классом? например:

public class Bar {
  private Foo foo;

  /* constructors and a bunch of wrapper methods that call
     into foo */
}

Или даже, пока мы на этом, что-то гораздо более низкотехнологичное:

public class Foo {
  /* constructors, fields, method, logic and what-not */

  private boolean isABar; // Could be an enum
}

Что вы думаете? Как вы справляетесь с этими «маркерными классами»?


В качестве примера того, как мой код может по-разному относиться к Foo и Bar, мой код должен иметь возможность выполнять такие вещи, как List<Foo> и List<Bar>. A Foo не может войти в List<Bar> и наоборот.

Ответы [ 10 ]

14 голосов
/ 27 сентября 2008

По моему мнению, лучше всего, если Foo и Bar подкласс от общего класса предков (возможно, AbstractFoo), который имеет все функциональные возможности. Какая разница в поведении должна существовать между Foo и Bar? Кодируйте эту разницу как абстрактный метод в AbstractFoo, а не с помощью оператора if в вашем коде.

Пример: Вместо этого:

if (foo instanceof Bar) {
    // Do Bar-specific things
}

Сделайте это вместо:

class Bar extends AbstractFoo {
    public void specialOp() {
        // Do Bar-specific things
    }
}

// ...
foo.specialOp();

Преимущество этого подхода в том, что если вам нужен третий класс, это очень похоже на Foo, но имеет небольшую разницу, вам не нужно просматривать весь код и добавлять редактировать все if заявления. : -)

3 голосов
/ 27 сентября 2008

Все зависит от значения классов Foo и Bar. Что они представляют и какова их цель. Пожалуйста, уточните.

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

2 голосов
/ 27 сентября 2008

Foo и Bar наследуются от FooBarImplementation

Я бы сделал класс FooBarImplementation, который бы реализовывал общие функции Foo и Bar. Фу и Бар извлекут из этого пользу. Но в вашем коде никогда никогда не используйте тип FooBarImplementation. Мои дни Java немного позади, но я думаю, что должен быть какой-то способ скрыть FooBarImplementation от пользовательского кода (сделать его защищенным или видимым только в зависимости от организации вашего проекта. Таким образом, никакой пользовательский код не будет смешивать Foo для бара (и наоборот)?

class FooBarImplementation
{
   public void doSomething() { /* etc. */ }
   /* etc. */
}

class Foo inherits FooBarImplementation { /* etc. */ }
class Bar inherits FooBarImplementation { /* etc. */ }

Foo и Bar, составленные с помощью FooBarImplementation

Другой возможностью было бы заставить Foo и Bar перенаправлять каждый из их методов во внутренний класс (опять же, FooBarImplementation). Таким образом, пользовательский код не может быть Foo и Bar.

class FooBarImplementation
{
   public void doSomething() { /* etc. */ }
   /* etc. */
}

class Foo
{
   private FooBarImplementation fooBarImplementation = new FooBarImplementation() ;

   public void doSomething() { this.fooBarImplementation.doSomething() ; }
   /* etc. */
}


class Bar
{
   private FooBarImplementation fooBarImplementation = new FooBarImplementation() ;

   public void doSomething() { this.fooBarImplementation.doSomething() ; }
   /* etc. */
}

НЕ заставлять Foo наследовать от Bar (или наоборот)

Шоудл Фу унаследовал от Барна, Фу был бы Баром, если говорить о языке. Не делайте этого, вы потеряете разницу между объектами, а это то, чего вы не хотите.

Не использовать логическое поле и поле типа whataver

Это худшая идея, с которой вы можете столкнуться. Бьярн Страуструп предупредил против такого рода паттернов для C ++, и C ++ - это не только ООП. Так что я думаю, что этот шаблон еще более "анти" для Java ...: -)

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

В основном вам нужно применить шаблон стратегии .

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

Если есть вероятность того, что Foo и Bar могут когда-нибудь расходиться в реализации, тогда на ваш вопрос ответят - используйте наследование любым способом, который кажется наилучшим.

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

И если этот класс создан, а не присваивать ему логическое свойство, например isFoo, было бы намного лучше взглянуть на то, почему вам нужно отличать Foo от Bar. Что в Foos заставляет вас обращаться с ними иначе, чем с Bars? Выясните это и создайте свойство, которое определяет различную информацию. Foos больше? Затем создайте свойство для размера (даже если это перечисление со значениями "Foo-size" и "Bar-size").

Это примерно столько, сколько можно сказать без конкретных примеров того, какими могут быть Фу и Бар.

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

Если между Foo и Bar нет поведенческой разницы, то имя класса "Foo" недостаточно абстрактно. Определите общую абстракцию между Foo и Bar и соответственно переименуйте класс. Затем предоставьте поле члена в классе для идентификации экземпляров как «Foo», «Bar» и т. Д. Используйте enum, если вы хотите ограничить возможные значения «Foo» и «Bar».

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

Как уже говорили другие, это зависит, но если у вас есть общие функциональные возможности между Foo и Bar и разница в функциональных возможностях может быть выражена в виде параметров, то я голосую за подклассы. Это было в основном то, что мы делали в конце практического курса по программированию.

Мы должны реализовать игру судоку, и в итоге у нас было «AbstractPlayfield», которое могло применять произвольный набор правил к произвольному игровому полю. Это поле AbstractPlay было разделено на отдельные варианты, которые мы должны были реализовать. Эти подклассы устанавливают параметры (в основном правила и форму доски) для абстрактного игрового поля, и все работает как шарм. Мы даже получили больше наследования в этих подклассах, потому что несколько вариантов содержали правила «Числа должны быть уникальными в строке» и «Числа должны быть уникальными в столбце».
Используя это, мы смогли завершить работу, которая была оценена в течение примерно 2 месяцев примерно за 3 дня :) (И они раздражали нас «Тестируйте эти крошечные классы установки атрибутов, потому что у вас могут быть ошибки там! их! Нам все равно, что вся важная логика проверена! ".)

С другой стороны, если класс Bar не имеет специальных функций, отличных от Bar, я не вижу смысла добавлять его - по крайней мере, из данных, которые вы мне предоставляете. Это может иметь смысл, если вы хотите выполнить некоторые операции, основанные на типах и диспетчеризацию по типу, но я не могу прочитать это из Foo и Bar. В этом случае я бы не стал создавать Бар из-за ЯГНИ.

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

ваши данные были недостаточно четкими, но, исходя из того, что, по вашему мнению, вам нужно, я озадачен, почему вы просто не выберете 4-й вариант:

Class MainStuff;
Class TypeA;
Class TypeB;

Нет, либо заставьте TypeA и B наследоваться от MainStuff, либо не сделайте MainStuff членом данных TypeA и TypeB. Это зависит от значения этих 3 классов.

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

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

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

Определенно используйте логическое свойство. Это самое простое решение, если только вы не предвидите, что класс Bar должен будет изменить свой интерфейс позже (например, переопределить его методы).

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