Наследование и полиморфизм - простота использования против чистоты

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

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

Редактировать: чтобы быть более конкретным, я использую C ++, поэтому использование полиморфизма позволит различным объектам «действовать одинаково» в том смысле, что производные классы могут находиться в одном списке и работать с виртуальной функцией базы учебный класс. Использование интерфейса (или имитация их через наследование) выглядит как решение, которое я хотел бы использовать.

15.08.2008 01:00:47
10 ОТВЕТОВ
РЕШЕНИЕ

Это может быть достигнуто с использованием множественного наследования. В вашем конкретном случае (C ++) вы можете использовать чисто виртуальные классы в качестве интерфейсов. Это позволяет вам иметь множественное наследование, не создавая проблем области / неоднозначности. Пример:

class Damage {
    virtual void addDamage(int d) = 0;
    virtual int getDamage() = 0;
};

class Person : public virtual Damage {
    void addDamage(int d) {
        // ...
        damage += d * 2;
    }

    int getDamage() {
        return damage;
    }
};

class Car : public virtual Damage {
    void addDamage(int d) {
        // ...
        damage += d;
    }

    int getDamage() {
        return damage;
    }
};

Теперь и Персона, и Автомобиль - это «Повреждение», то есть они реализуют интерфейс Повреждения. Использование чисто виртуальных классов (так что они похожи на интерфейсы) является ключевым и должно использоваться часто. Он защищает будущие изменения от изменения всей системы. Читайте об Открыто-закрытом принципе для получения дополнительной информации.

3
15.08.2008 03:41:05

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

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

Если вы хотите пойти по шаблону (предполагая, что C ++ или аналогичный), вы можете сделать это с помощью mixins, но это может стать очень уродливым, если сделать это плохо.

0
15.08.2008 01:13:01

Обычно, когда мы говорим о «есть», у «есть» есть «мы имеем в виду наследование против композиции».

Хм ... счетчик урона будет просто атрибутом одного из ваших производных классов и не будет обсуждаться в терминах "человек - счетчик урона" по вашему вопросу.

Видеть это:

http://www.artima.com/designtechniques/compoinh.html

Что может помочь вам в пути.

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

0
23.05.2017 12:13:37

Этот вопрос действительно сбивает с толку: /

Ваш вопрос, выделенный жирным шрифтом, очень открытый и содержит ответ «это зависит», но ваш пример на самом деле не дает много информации о контексте, из которого вы спрашиваете. Эти строки смущают меня;

наборы данных, которые должны обрабатываться одинаково

Какой способ? Обрабатываются ли наборы функцией? Еще один класс? Через виртуальную функцию на данных?

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

Идеал «действовать одинаково» и полиморфизм абсолютно не связаны. Как полиморфизм позволяет легко достичь?

0
15.08.2008 01:17:13

@Kevin

Обычно, когда мы говорим о «есть», у «есть» есть «мы имеем в виду наследование против композиции».

Хм ... счетчик урона будет просто атрибутом одного из ваших производных классов и не будет обсуждаться в терминах "человек - счетчик урона" по вашему вопросу.

Наличие счетчика урона в качестве атрибута не позволяет ему разнообразить объекты со счетчиками урона в коллекцию. Например, у человека и автомобиля могут быть счетчики повреждений, но вы не можете иметь vector<Person|Car>или, vector<with::getDamage()>или что-либо подобное на большинстве языков. Если у вас есть общий базовый класс Object, вы можете переместить их таким образом, но тогда вы не сможете получить getDamage()общий доступ к методу.

Это была суть его вопроса, когда я его читал. «Должен ли я нарушать is-aи has-aради того, чтобы относиться к определенным объектам, как будто они одинаковы, даже если это не так?»

0
15.08.2008 01:22:11

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

В зависимости от языка у вас вполне может быть возможность множественного наследования, но обычно простые интерфейсы имеют смысл. Под «простым» я подразумеваю создание интерфейса, который не пытается быть слишком большим. Лучше иметь много простых интерфейсов и несколько монолитных. Конечно, всегда есть компромисс, и слишком много интерфейсов, вероятно, приведет к тому, что о них «забудут» о ...

0
15.08.2008 01:29:00

@Эндрю

Идеал «действовать одинаково» и полиморфизм абсолютно не связаны. Как полиморфизм позволяет легко достичь?

Все они имеют, например, одну общую функцию. Давайте назовем это addDamage(). Если вы хотите сделать что-то вроде этого:

foreach (obj in mylist)
    obj.addDamage(1)

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

class Person : DamageCounter {}
class Car : DamageCounter {}

foreach (DamageCounter d in mylist)
    d.addDamage(1)

Затем вы можете лечить Personи Carто же самое при определенных очень полезных обстоятельствах.

0
15.08.2008 01:29:19

Я думаю , вы должны быть реализации интерфейсов , чтобы иметь возможность реализовать ваши имеет отношение (я делаю это в C #):

public interface IDamageable
{
    void AddDamage(int i);
    int DamageCount {get;}
}

Вы можете реализовать это в ваших объектах:

public class Person : IDamageable

public class House : IDamageable

И вы были бы уверены, что свойство DamageCount и имеет метод, позволяющий вам добавлять урон, не подразумевая, что человек и дом связаны друг с другом в какой-то иерархии.

5
15.08.2008 01:54:52

Я согласен с Джоном, но при условии, что вам все еще нужен отдельный класс счетчика урона, вы можете сделать:

class IDamageable {
  virtual DamageCounter* damage_counter() = 0;
};
class DamageCounter {
  ...
};

Каждый повреждаемый класс затем должен предоставить свою собственную функцию-членmage_counter (). Недостатком этого является то, что он создает vtable для каждого класса повреждения. Вместо этого вы можете использовать:

class Damageable {
 public:
  DamageCounter damage_counter() { return damage_counter_; }
 private:
  DamageCounter damage_counter_;
};

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

1
16.09.2008 09:56:36

Полиморфизм не требует наследования . Полиморфизм - это то, что вы получаете, когда несколько объектов реализуют одну и ту же сигнатуру сообщения (метод).

0
18.01.2010 21:29:48