Это плохой ООП дизайн для симуляции с участием интерфейсов?

13

Я разрабатываю свою собственную маленькую ООП-программу для симуляции вампиров, волков, людей и грузовиков и пытаюсь реализовать собственное ограниченное понимание интерфейсов.

( Я все еще абстрагируюсь здесь и не имею никакой реализации кода, так что это скорее вопрос ООП-дизайна ... Я думаю!)

Прав ли я в поиске «общего поведения» между этими классами и реализации их как интерфейсов ?

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

public class Vampire : Villain, IBite, IMove, IAttack

Аналогично для грузовиков ...

public class Truck : Vehicle, IMove

И для людей ...

public class Man : Human, IMove, IDead

Мое мышление прямо здесь? (Ценю твою помощь)

user3396486
источник
14
Животные, овощи и минералы редко бывают хорошими примерами для реализации приложений. Реальные реализации, как правило, более абстрактны, например IEnumerable, IEquatableи т. Д.
Роберт Харви,
6
У вас есть одно упоминание о том, что ваши объекты собираются делать в вашем программном обеспечении («кусаться»). Программное обеспечение, как правило, предназначено для того, чтобы что- то делать , основывать объектную модель только на характеристиках, никуда не ведя
Тофро
@tofro Мое намерение состояло в том, чтобы IBite содержал несколько методов, которые бы реализовывали поведение в отношении (1) снижения уровня «жизни / энергии» другого (2) появления или вызова графики «крови» и (3) обновления статики симуляции данные (такие как NoOfBites). Я думаю, что могу оценить, что интерфейс лучше всего использовать для реализации различных методов поведения.
user3396486 30.09.16
2
Разве классы Human, Vampire и Vehicle не реализуют интерфейс IMove уже? Почему вы должны заставить подклассы реализовывать это слишком явно?
Пьер Арло
Все ли эти интерфейсы действительно необходимы? В Python, к счастью, вам ничего не нужно, это было действительно освежающее изменение (моим первым языком был Object Pascal). Также виртуальные методы могут быть лучшим решением в некоторых случаях.
Аяся

Ответы:

33

В общем, вы хотите иметь интерфейсы для общих характеристик вашего кластера.

Я частично согласен с @Robert Harvey в комментариях, который сказал, что обычно интерфейсы представляют более абстрактные особенности классов. Тем не менее, начиная с более конкретных примеров, я считаю хороший способ начать думать абстрактно.

Хотя ваш пример технически верен (т. Е. Да, вампиры и волки кусаются, так что вы можете иметь интерфейс для этого), существует вопрос актуальности. Каждый объект имеет тысячи характеристик (например, животные могут иметь мех, плавать, лазить по деревьям и т. Д.). Сделаете ли вы интерфейс для всех них? Очень менее вероятно.

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

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

Paul92
источник
1
Я, конечно, надеюсь, что каждый объект не имеет тысячи атрибутов.
садовник
4
Атрибуты не как в атрибутах oop, но атрибуты / характеристики грамматики (такие как перечислимые, сопоставимые и т. Д.): D. Плохой выбор слов.
Paul92
3
Стоит отметить, что полезными являются те интерфейсы, которые вы будете использовать. Например, IBiteэто не особенно полезно, но вы можете захотеть, IAttackчтобы вы могли работать над всеми вещами, которые делают атаки, или IUpdateчтобы вы могли запускать обновления для всего, или IPhysicsEnabledчтобы вы могли применять к ним физику и т. Д.
anaximander
1
Этот ответ поднимает некоторые очень хорошие моменты. Заключительный абзац суммирует это довольно хорошо; по крайней мере, так хорошо, как вы можете с уровнем детализации.
Легкость гонок с Моникой
1
группировка методов общего назначения больше подходит для абстрактных классов. Интерфейс предназначен для разработки контрактов, которые должны уважать тех, кто его реализует, а не группировать одну и ту же реализацию для некоторых объектов.
Вальфрат,
28

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

Если у меня есть Combatкласс с fight()методом, то этот метод, вероятно, должен вызывать оба move()и один и attack()тот же объект. Это настоятельно предполагает необходимость ICombatantинтерфейса, который fight()может вызывать move()и attack()через. Это чище, чем fight()взять IAttackобъект и навести его на предмет IMoveтого, может ли он также двигаться.

Это не значит, что вы не можете иметь IMove IAttackинтерфейсы. Я просто надеюсь, что вы не сделаете их без необходимости в клиенте. И наоборот, если ни одному клиенту не нужно заставлять объект двигаться и атаковать, тоICombatant этом нет необходимости.

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

candied_orange
источник
1
Блин это хорошо. Игры кажутся действительно хорошим способом использования и объяснения ООП.
Джефф
6
@JeffO до тех пор, пока вы на самом деле не внедрите достаточно большую игру и не поймете, что ООП - беспорядочный беспорядок, и вам будет лучше с компонентными системами или ориентированными на данные проектами.
Darkhogg
«Интерфейсы принадлежат клиентам, которые их используют»
Tibos
4
Соответствующий ericlippert.com/2015/04/27/wizards-and-warriors-part-one
Джаред Смит
1
+1 за разницу между библиотеками и приложениями, я часто (слишком много?: /) Читаю тонны вещей, которые просто подходят для одного, а не для другого.
Вальфрат
3

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

Например, предположим, что только несколько видов существ могут иметь Woozles, и каждый хочет, чтобы такие существа имели NumerOfWoozlesсвойство. Если бы такое свойство было в интерфейсе, который был реализован только существами, которые могут иметь Woozles, то код, который хотел бы найти общее количество Woozles, хранящегося в коллекции существ смешанных типов, должен был бы сказать что-то вроде:

int total = 0;
foreach (object it in creatures)
{
   IWoozleCountable w = trycast(it, IWoozleCountable);
   if (w != null) total += w.WoozleCount;
}

Однако, если бы WoozleCount был членом Creature / ICreature, хотя несколько подтипов переопределяли бы стандартную реализацию WoozleCount Creature, которая всегда возвращает ноль, код можно упростить до:

int total = 0;
foreach (ICreature it in creatures)
   total += it.WoozleCount;

Хотя некоторые люди могут не согласиться с идеей, чтобы каждое Существо реализовало свойство WoozleCount, которое действительно полезно только для нескольких подтипов, свойство будет иметь смысл для всех типов, независимо от того, будет ли оно полезным для элементов, о которых известно, что они принадлежат к этим типам, и я бы расценил интерфейс «кухонной раковины» как нечто меньшее, чем запах кода, чем оператор trycast.

Supercat
источник