2008-08-15 16 views
5

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

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

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

ответ

3

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

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

0

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

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

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

0

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

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

Смотреть это:

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

, которые могут помочь вам по пути.

@Derek: Из формулировки, я предполагал, там был база Clase, перечитав вопрос я вроде теперь, что он клонит.

0

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

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

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

Каким образом? Являются ли наборы обработаны функцией? Другой класс? Через виртуальную функцию по данным?

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

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

0

@Kevin

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

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

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

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

0

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

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

0

@ Андрей

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

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

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

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

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

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

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

5

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

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

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

public class Person : IDamageable 

public class House : IDamageable 

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

1

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

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_; 
}; 

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

0

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