Закон Деметры не имеет смысла в моем случае - PullRequest
1 голос
/ 15 июня 2009

Глядя на этот ответ Я понимаю, что вы не должны копировать частные указатели, используя дружбу в C ++, как я делал в своей программе:

class bar;

class foo
{
private:
   some_smart_pointer<int> state;

public:
   foo(int value) : state(new int(value)) {}
   friend class baz;
};

class bar
{
private:
   some_weak_pointer<int> state;
public:
   friend class baz;
};

class baz
{
public:
   foo Foo;
   bar Bar;

   baz() { Bar.state = Foo.state; }
};

Это нормально, но передача постоянного умного указателя нарушает принцип инкапсуляции.
Это выставляет внутренности системы. Передача bar в foo также создает связь. Также в моей системе имеет смысл объединять объекты в то, что в этом вопросе называется свободным интерфейсом:

MainObject.PropertyCollection.Property

Как сказано в этом ответе SO, он не нарушает закон Деметры или нет?
Я здесь очень запутался.
Я перепроектирую свою библиотеку в последний раз и хочу, чтобы она была правильной.
Я подумал, что вместо передачи своего внутреннего интеллектуального указателя я должен передать класс-обертку, который также будет использоваться в качестве свойства, и установить его только для чтения, а затем копирование безопасно, верно?

РЕДАКТИРОВАТЬ согласно комментариям: Ну, я внедряю D20 3.5 SRD (http://www.d20srd.org/).
Например, есть шесть способностей.
В моем дизайне D20Ababilities объединены внутри D20Character.
D20Abilities содержит 6 экземпляров D20Ability: Сила, Ловкость, Телосложение. Мудрость, Интеллект и Харизма.
Все экземпляры D20Ability содержат закрытое состояние (которое называется модификатором), которое является общим для всей системы. Например, каждый навык нуждается в модификаторе своей способности.

1 Ответ

2 голосов
/ 15 июня 2009

В отношении вашего описания и закона Деметры. Используя пример из Википедии, говорят, что он предпочитает:

void exercise(const Dog& d) { return d.walk(); }

вместо:

void exercise(const Dog& d) { return d.legs.move(); }

Потому что собака должна знать, как ходить, а не двигать ногами. Применив это к вашей программе D & D, мы получили бы что-то вроде:

bool hit_orc_with_long_sword(const Character& c, int DCValue) {
   return c.D20Abilities.Strength() + d20.roll() > DCValue;
}

Предпочитают: skill_check (c, MeleeAttack (LongSword (), Orc ());

bool skill_check(const Character& c, const Action& a) {
    return c.attempt(a);
}

//.... in class character:

bool Character::attempt(const Action& a)
{
    return a.check_against(D20Abilities);
}

//.... in class Action:

bool Action::check_against(const D20Abilities& a) {
   return GetRelevantStat(a) + d20.roll() > DCValue;
}

Таким образом, вместо доступа через группы свойств, Персонажи знают, как выполнять Действия, а Действия знают, какова вероятность их успеха с определенной статистикой. Это разъединяет интерфейс. Код потребителя (например, описанный выше hit_orc_with_long_sword) не должен «сообщать» персонажу, как проверить его силу, он обрабатывается взаимодействием классов, каждый из которых обрабатывает четко определенный домен.

Однако приведенный пример кода выглядит так, как будто имеет несколько проблем. Когда вы используете друга для выполнения чего-либо, кроме реализации определенных операторов, я обнаружил, что это указывает на проблему проектирования. Вы не должны разбрасывать внутреннее состояние от одного объекта к другому; объекты должны знать, как выполнять набор действий из своего внутреннего состояния и аргументов, передаваемых им в качестве функций.

...