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;
Я принимаю это, это нарушает закон Demeter / Принцип Наименьшего количества Знания?
Если так, как я исправляю это, если у меня есть большое количество внутренних свойств класса?
Одна из моих любимых цитат Мартина Фаулера:
Я бы предпочел, чтобы это называлось Иногда полезным предложением Деметры
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;
is
Category cat = new Category();
cat.GetPerf().GetBigPerf().SetValue(1.0);
Таким образом, это нарушает правила, если определение в Википедии правильное:
.. [M] этод M объекта O может только {{1} } вызывают методы следующих типов объектов:
- O самого
- M's parameters
- любых объектов, созданных / созданных в пределах M
- O прямых компонентных объектов
- глобальная переменная, доступный для O, в области M
В частности, объект должен избегать вызова методов объекта-члена, возвращаемого другим методом
. Если вас беспокоит, что эти 3 тесно связаны, удалите общедоступные методы доступа и добавьте метод для категории, чтобы установить значение. Затем выполните рефакторинг Performance и BigPerformance, сделав их закрытыми членами.
Если вы всегда помните о тестируемости ваших классов и используете IoC, вы заметите, что вам не нужно так сильно беспокоиться о LoD.
Посмотрите на это с другой стороны
Как я собираюсь протестировать
Категория
? Я не хочу, чтобы она автоматически создавалаПроизводительность
при использовании медленной файловой системы . Давайте передадимIPerformance
вCategory
и заменим фактическую реализацию фиктивным экземпляромPerformance
. .Как мне протестировать
Performance
? Я не хочу, чтобы он автоматически создавалBigPerformance
, создающий { {1}} подключение к базе данных. Давайте передадим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.)
Похоже (и, вероятно, в сокращенном варианте) требуется гораздо больше работы, но Преимущества окупятся в долгосрочной перспективе, если вы сможете тестировать свои классы изолированно.
Взгляните на блог Миско Хевери , чтобы получить информацию по этому и другим вопросам.
Это не нарушение Закона Деметры, потому что вы используете публичный контракт классов.