Основная проблема, с которой я сталкиваюсь довольно часто, но когда-либо находил ясное решение, это та, в которой вы хотите кодировать поведение для взаимодействия между различными объектами общего базового класса или интерфейса. Чтобы сделать это немного конкретнее, я приведу пример;
Боб работал над стратегической игрой, которая поддерживает «классные географические эффекты». Они округляются до простых ограничений, таких как, если войска ходят в воде, они замедляются на 25%. Если они идут по траве, они замедляются на 5%, а если они идут по асфальту, они замедляются на 0%.
Теперь руководство заявило Бобу, что им нужны новые виды войск. Там будут джипы, лодки, а также суда на воздушной подушке. Кроме того, они хотели, чтобы джипы получали урон, если их водили в воду, а суда на воздушной подушке игнорировали все три типа местности. Ходят слухи, что они могут добавить еще один тип местности с еще большим количеством функций, чем замедление юнитов и получение урона.
Ниже приведен пример очень грубого псевдокода:
public interface ITerrain
{
void AffectUnit(IUnit unit);
}
public class Water : ITerrain
{
public void AffectUnit(IUnit unit)
{
if (unit is HoverCraft)
{
// Don't affect it anyhow
}
if (unit is FootSoldier)
{
unit.SpeedMultiplier = 0.75f;
}
if (unit is Jeep)
{
unit.SpeedMultiplier = 0.70f;
unit.Health -= 5.0f;
}
if (unit is Boat)
{
// Don't affect it anyhow
}
/*
* List grows larger each day...
*/
}
}
public class Grass : ITerrain
{
public void AffectUnit(IUnit unit)
{
if (unit is HoverCraft)
{
// Don't affect it anyhow
}
if (unit is FootSoldier)
{
unit.SpeedMultiplier = 0.95f;
}
if (unit is Jeep)
{
unit.SpeedMultiplier = 0.85f;
}
if (unit is Boat)
{
unit.SpeedMultiplier = 0.0f;
unit.Health = 0.0f;
Boat boat = unit as Boat;
boat.DamagePropeller();
// Perhaps throw in an explosion aswell?
}
/*
* List grows larger each day...
*/
}
}
Как вы можете видеть, все было бы лучше, если бы у Боба был твердый проектный документ с самого начала. По мере роста количества юнитов и типов местности растет и сложность кода. Бобу не только нужно выяснить, какие элементы могут быть добавлены в интерфейс модуля, но он также должен повторить много кода. Весьма вероятно, что новые типы местности требуют дополнительной информации из того, что можно получить из базового интерфейса IUnit.
Каждый раз, когда мы добавляем в игру еще одну единицу, каждая местность должна обновляться, чтобы обрабатывать новую единицу. Понятно, что это требует много повторений, не говоря уже об уродливой проверке во время выполнения, которая определяет тип рассматриваемого юнита. Я отключил вызовы для определенных подтипов в этом примере, но такие вызовы не нужны. Примером может быть то, что когда лодка падает на сушу, ее винт должен быть поврежден. Не все агрегаты имеют пропеллеры.
Я не уверен, как называется такая проблема, но это зависимость многих от многих, которую мне трудно отделить. Мне не нравятся сотни перегрузок для каждого подкласса IUnit на ITerrain, так как я хотел бы получить чистую связь.
Любой свет по этой проблеме очень популярен. Возможно, я думаю о выходе из орбиты все вместе?