Как изменить код так, чтобы он соответствовал закону Деметры
public class BigPerformance
{
public decimal Value { get; set; }
}
public class Performance
{
public BigPerformance BigPerf { get; set; }
}
public class Category
{
public Performance Perf { get; set; }
}
Если я позвоню:
Category cat = new Category();
cat.Perf.BigPerf.Value = 1.0;
Я предполагаю, что это нарушает Закон Деметры / Принцип Наименьшего Знания?
Если так, как я могу исправить это, если у меня есть большое количество свойств внутреннего класса?
5 ответов
Одна из моих любимых цитат от Мартина Фаулера:
Я бы предпочел, чтобы его называли "Случайно полезное предложение Деметры".
http://haacked.com/archive/2009/07/14/law-of-demeter-dot-counting.aspx
Если вы говорите о Законе Деметры как в "не называйте соседей соседями", вы можете делегировать его другим методам, которые делают то, что вы хотите.
Из вашего примера, я думаю, вы хотите сбросить значение производительности или что-то в этом роде. Вы можете изменить пример кода так, чтобы он был по своей сути вместо этого:
Category cat = new Category();
cat.resetPerf();
Код был бы чем-то похожим на это:
public class BigPerformance
{
//constructors 'n stuff
public static decimal DEFAULT;
public decimal Value {get; private set;}
public void reset() {
Value = BigPerformance.DEFAULT;
}
}
public class Performance
{
//constructors 'n stuff
private BigPerformance BigPerf {get; set};
public reset() {
BigPerf.reset();
}
}
public class Category
{
// constructors 'n stuff
public Performance Perf {get; private set;}
public resetPerformance() {
Perf.reset();
}
}
Таким образом, Category
Класс не должен знать, как сбросить значение в случае, если значение по умолчанию является чем-то другим или его тип будет изменен в будущем.
Лично, если риск для изменений низок, я бы вместо этого пошел на ответ Юхарра.
Category cat = new Category();
cat.Perf.BigPerf.Value = 1.0;
является
Category cat = new Category();
cat.GetPerf().GetBigPerf().SetValue(1.0);
So it is breaking the rules if the wikipedia definition is correct:
..[M]ethod M of an object O may only invoke the methods of the following kinds of objects:
- О себе
- Параметры М
- любые объекты, созданные / созданные в M
- Прямые составляющие объекты О
- глобальная переменная, доступная O, в области M
In particular, an object should avoid invoking methods of a member object returned by another method
If you are worried about the 3 being tightly coupled, then remove the public accessors and add a method on Category to set the value. Then refactor Performance and BigPerformance to be private members.
Если вы всегда помните о своих классах и используете IoC, вы заметите, что вам больше не нужно беспокоиться о LoD.
Рассмотрим этот вариант
Как я собираюсь проверить
Category
? Я не хочу, чтобы это автоматически создавалоPerformance
это использует медленную файловую систему. Давайте пройдемIPerformance
ЧерезCategory
и заменить фактическую реализацию на пустышкуPerformance
пример.Как я собираюсь проверить
Performance
? Я не хочу, чтобы это автоматически создавалоBigPerformance
сделать подключение к базе данных. Давайте пройдемIBigPerformance
ЧерезPerformance
и заменить фактическую реализацию на пустышкуBigPerformance
пример.
...
Вы, очевидно, заметили закономерность
Ваш код будет в строке
BigPerformance BigPerf = new BigPerformance();
BigPerf.Value := 1.0;
Performance Perf = new Performance(BigPerformance);
Category cat = new Category(Performance);
(This would be retrieved from a factory.)
Похоже (а в краткосрочной перспективе это так), похоже, гораздо больше работы, но преимущества в конечном итоге окупятся, если вы сможете тестировать свои классы изолированно.
Загляните в блог Миско Хевери, чтобы узнать больше об этой и других темах.
Это не нарушает закон Деметры, потому что вы используете публичный контракт классов.