Полиморфизм стоит увеличения сцепления?
Я пишу упрощенную игру, чтобы научиться получать больше опыта в C++, и у меня есть идея, где я чувствую, что полиморфизм почти работает, но не работает. В этой игре Party
движется довольно линейно через Map
, но может случайно встретить Fork
в дороге. Вилка является (в основном) std::vector<location*>
Первоначально я собирался кодировать что-то вроде следующего в Party
функция-член:
if(!CurrLocation->fork_.empty())
// Loop through forks and show options to the player, go where s/he wants
else
(CurrLocation++)
Но мне было интересно, если какой-то вариант из следующих может быть лучше:
CurrLocation = CurrLocation->getNext();
С Fork, фактически полученным из Location, и перегружая некоторую новую функцию getNext()
, Но в последнем случае location
(структура низкого уровня) должна была бы быть тем, чтобы представить сообщение пользователю вместо того, чтобы "передавать это обратно", что я не считаю элегантным, поскольку оно объединяет location
в UserInterface::*
,
Ваши мнения?
4 ответа
Все проблемы могут быть решены путем добавления уровня косвенности. Я бы использовал предложенный вами вариант и отделил Location от Party, позволив getNext принять объект, который разрешает выбор направления. Вот пример (не проверено):
class Location;
class IDirectionChooser
{
public:
virtual bool ShouldIGoThisWay(Location & way) = 0;
};
class Location
{
public:
virtual Location * GetNext(IDirectionChooser & chooser)
{
return nextLocation;
}
virtual Describe();
private:
Location * nextLocation;
};
class Fork : public Location
{
public:
virtual Location * GetNext(IDirectionChooser & chooser)
{
for (int i = 0; i < locations.size(); i++)
if (chooser.ShouldIGoThisWay(*locations[i]))
return locations[i];
}
virtual Describe();
private:
vector<Location *> locations;
};
class Party : public IDirectionChooser
{
public:
void Move()
{
currentLocation = currentLocation->GetNext(GetDirectionChooser());
}
virtual IDirectionChooser & GetDirectionChooser() { return *this; }
virtual bool ShouldIGoThisWay(Location & way)
{
way.Describe();
cout << "Do you want to go that way? y/n" << endl;
char ans;
cin >> ans;
return ans == 'y';
}
};
Вы должны использовать полиморфизм, если он имеет смысл и упрощает ваш дизайн. Вы не должны использовать его только потому, что он существует и имеет причудливое имя. Если это делает ваш дизайн проще, то оно того стоит.
Правильность и простота должны быть конечной целью каждого дизайнерского решения.
Я думаю, что вы сами определили проблемы и, возможно, сможете решить их с помощью своих знаний об остальной части системы или нескольких подробностей, которые мы рассмотрим здесь.
Как уже упоминалось:
- Полиморфизм следует использовать, чтобы упростить конструкцию - что он и сделал бы в этом случае, так что это хорошо видно.
- У вас есть проблема с муфтой - опять хорошо заметна, муфта может привести к проблемам в дальнейшем. Однако это говорит мне о том, что способ применения полиморфизма может быть не лучшим.
- Программирование на интерфейсах должно позволить вам скрыть внутренние детали того, как система собрана, и таким образом уменьшить связь.
Полиморфизм не дает большей связи, я думаю, что это отдельные вопросы.
Фактически, если вы программируете интерфейсы и следуете общей инверсии шаблонов управления, то это приведет к меньшему или нулевому соединению.
В вашем примере я не вижу, как местоположение связано с UserInterface?
Если это так, может ли связь быть удалена через другой уровень абстракции между UserInterface и Location, такой как LocationViewAdapter?