В одном из проектов наша команда использует списки объектов для выполнения массовых операций над наборами данных, которые должны обрабатываться одинаково. В частности, разные объекты в идеале должны вести себя одинаково, что очень легко достигается с помощью полиморфизма. Проблема, с которой я сталкиваюсь, заключается в том, что наследование подразумевает отношение является, а не имеет. Например, несколько объектов имеют счетчик урона, но для того, чтобы это было удобно использовать в списке объектов, можно было бы использовать полиморфизм - только это подразумевало бы отношение есть, которое не было бы истинным. (Человек не счетчик урона).
Единственное решение, которое мне приходит в голову, - это заставить члена класса возвращать правильный тип объекта при неявном кастинге, вместо того чтобы полагаться на наследование. Может быть, лучше отказаться от идеала is a / has a в обмен на простоту программирования?
Edit: Если быть более точным, то я использую C++, поэтому использование полиморфизма позволило бы различным объектам "действовать одинаково" в том смысле, что производные классы могли бы находиться в одном списке и управляться виртуальной функцией базового класса. Использование интерфейсов (или их имитация через наследование) представляется решением, которое я готов использовать.
Я думаю, что Вы должны осуществлять интерфейсы, чтобы быть в состоянии провести в жизнь Ваш , имеет отношения (делаю это в C#):
public interface IDamageable
{
void AddDamage(int i);
int DamageCount {get;}
}
Вы могли осуществить это в своих объектах:
public class Person : IDamageable
public class House : IDamageable
И you' d быть уверенным, что у собственности DamageCount и есть метод, чтобы позволить Вам добавлять повреждение, не подразумевая, что человек и дом связаны друг с другом в своего рода иерархии.
Это может быть достигнуто, используя несколько наследование. В Вашем конкретном случае (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;
}
};
Теперь и Человек и Автомобиль ' a\U 0026\U 0023\39; Повреждение, значение, они осуществляют интерфейс Damage. Использование чистых виртуальных классов (так, чтобы они были похожи на интерфейсы) ключевое и должно часто использоваться. Это изолирует будущие изменения от изменения всей системы. Читайте на Открыто закрытом Принципе для получения дополнительной информации.
Я соглашаюсь с Джоном, но у принятия Вас все еще есть потребность в отдельном классе прилавка повреждения, Вы можете сделать:
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_;
};
Но многие люди Не Спокойны с несколькими наследование, когда у нескольких родителей есть членские переменные.
@Kevin
Обычно, когда мы говорим о ' a' против ' имеет a' we' ре, говорящее о Наследовании против Состава.
Гм... прилавок повреждения просто был бы признаком одного из Ваших производных классов и wouldn' t действительно быть обсужденным с точки зрения ' человек - повреждение counter' относительно Вашего вопроса.
Наличие повреждения возражает как признак doesn' t позволяют ему разнообразным объектам с прилавками повреждения в коллекцию. Например, у человека и автомобиля могли бы оба быть прилавки повреждения, но Вы can' t имеют 'vector< Person|Car>'; или 'vector< с:: getDamage () >'; или что-либо подобное на большинстве языков. Если у Вас есть общий базовый класс Объекта, то Вы можете пихнуть их таким образом, но тогда Вас can' t получают доступ 'getDamage ()' метод в общем.
Это было сущностью его вопроса, когда я прочитал его. " Если я нарушаю, '-', и 'имеет -' ради рассмотрения определенных объектов, как будто они - то же, даже при том, что они - not' t? "
" Выполнение его right" будет обладать преимуществами в конечном счете, если только потому, что кто-то обслуживающий систему позже найдет легче постигать, если это было сделано правильно для начала.
В зависимости от языка у Вас может быть выбор нескольких наследование, но обычно простые интерфейсы имеют большую часть смысла. " simple" я имею в виду, делают интерфейс этим isn' t пытающийся быть слишком много. Лучше иметь много простых интерфейсов и нескольких монолитных. Конечно, всегда есть компромисс, и слишком много интерфейсов, вероятно, привели бы к, являющимся " forgotten" о...
@Andrew
идеал " действуя same" и полиморфизм абсолютно не связан. Как полиморфизм облегчает достигать?
Они все имеют, например, одна функция вместе. Let' s называют его 'addDamage ()'. Если Вы хотите сделать что-то вроде этого:
foreach (obj in mylist)
obj.addDamage(1)
Тогда Вам нужен или динамический язык, или Вам нужны они, чтобы простираться от общего родительского класса (или интерфейс). например:
class Person : DamageCounter {}
class Car : DamageCounter {}
foreach (DamageCounter d in mylist)
d.addDamage(1)
Затем Вы можете рассматривать 'Человека' и 'Автомобиль' то же при определенных очень полезных обстоятельствах.
Иногда стоит отказаться от идеального в пользу реалистичного. Если "сделать все правильно" без реальной пользы приведет к большим проблемам, то я сделаю все неправильно. При этом я часто думаю, что стоит потратить время на то, чтобы сделать все правильно, потому что ненужное множественное наследование увеличивает сложность и может способствовать тому, что система будет менее удобной для сопровождения. Вы сами должны решить, что лучше для ваших обстоятельств.
Одним из вариантов может быть реализация этими объектами интерфейса Damageable
, а не наследование от DamageCounter
. Таким образом, человек имеет счетчик повреждений, но является повреждаемым. (Мне часто кажется, что интерфейсы имеют гораздо больше смысла в качестве прилагательных, чем существительных). Тогда можно было бы иметь согласованный интерфейс повреждений для объектов Damageable
и не показывать, что счетчик повреждений является базовой реализацией (если это не нужно).
Если вы хотите пойти по пути шаблонов (предполагается, что это C++ или аналогичный язык), вы можете сделать это с помощью миксинов, но это может очень быстро стать уродливым, если сделано плохо.
Полиморфизм does не требует inheritance. Полиморфизм - то, что Вы получаете, когда несколько объектов осуществляют ту же подпись сообщения (метод).
Обычно, когда мы говорим о 'is a' и 'has a', мы говорим о наследовании и композиции.
Счетчик повреждений будет просто атрибутом одного из ваших производных классов и не будет обсуждаться в терминах 'A person is a damage counter' по отношению к вашему вопросу.
См. это:
http://www.artima.com/designtechniques/compoinh.html
Это может помочь вам на этом пути.
@Derek: Судя по формулировке, я предполагал, что есть базовый класс, перечитав вопрос, я вроде как теперь понял, что он имел в виду.
Этот вопрос действительно запутан :/
Ваш вопрос, выделенный жирным шрифтом, очень открытый и имеет ответ "это зависит", но ваш пример не дает много информации о контексте, в котором вы спрашиваете. Эти строки сбивают меня с толку;
наборы данных, которые должны обрабатываться одинаковым образом
Каким образом? Наборы обрабатываются функцией? Другим классом? Через виртуальную функцию на данных?
В частности, разные объекты в идеале должны вести себя одинаково, что легко достигается с помощью полиморфизма
Идеал "одинакового поведения" и полиморфизм абсолютно не связаны. Как полиморфизм позволяет легко достичь этого?