Сведения о вопросе

NOTtoday

23:57, 23rd August, 2020

Теги

c++   inheritance   oop   polymorphism    

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

Просмотров: 514   Ответов: 10

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

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

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



  Сведения об ответе

P_S_S

11:46, 9th August, 2020

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

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

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

public class Person : IDamageable

public class House : IDamageable

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


  Сведения об ответе

PROGA

06:06, 12th August, 2020

Это может быть достигнуто с помощью множественного наследования. В вашем конкретном случае (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;
    }
};

Теперь и Человек, и автомобиль 'is-a' повреждения, то есть они реализуют интерфейс повреждения. Использование чисто виртуальных классов (чтобы они были похожи на интерфейсы) является ключевым и должно использоваться часто. Он изолирует будущие изменения от изменения всей системы. Для получения дополнительной информации ознакомьтесь с принципом "открыто-закрыто".


  Сведения об ответе

lool

11:56, 25th August, 2020

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

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

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

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

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


  Сведения об ответе

ASSembler

17:53, 26th August, 2020

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


  Сведения об ответе

PROGA

07:08, 29th August, 2020

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

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

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


  Сведения об ответе

SEEYOU

21:06, 1st October, 2020

Обычно, когда мы говорим о 'is a' против 'has a', мы говорим о наследовании против композиции.

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

Видеть это:

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

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

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


  Сведения об ответе

JUST___

00:37, 9th August, 2020

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

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

наборы данных, которые все должны быть обработаны аналогичным образом

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

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

Идеал "acting the same" и полиморфизм абсолютно не связаны между собой. Как полиморфизм делает его легко достижимым?


  Сведения об ответе

#hash

05:09, 29th August, 2020

@Kevin

Обычно, когда мы говорим о 'is a' против 'has a', мы говорим о наследовании против композиции.

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

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

Вот в чем была суть его вопроса, когда я читал его. -Должен ли я нарушать правила № 30 и № 31 ради того, чтобы относиться к некоторым объектам так, как будто они являются одним и тем же, даже если это не так?"


  Сведения об ответе

VERSUION

11:41, 11th August, 2020

"Doing it right" будет иметь преимущества в долгосрочной перспективе, хотя бы потому, что кто-то, поддерживающий систему позже, будет легче понять, правильно ли это было сделано с самого начала.

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


  Сведения об ответе

9090

16:06, 10th August, 2020

@Andrew

Идеал "acting the same" и полиморфизм абсолютно не связаны между собой. Как полиморфизм делает его легко достижимым?

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

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

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

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

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

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


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться