Мне не кажется, что это изменение имеет какое-либо отношение к Закону Деметры. Закон, по сути, заключается в том, чтобы закодировать структуру вашего графа объектов в ваш код, вызывая методы через всю цепочку других объектов. Например, предположим, что в приложении для автострахования у клиента есть полис, а у полиса есть транспортные средства, у транспортных средств есть назначенные им водители, а у водителей есть даты рождения и, следовательно, возраст. Вы можете представить себе следующий код:
public boolean hasUnderageDrivers(Customer customer) {
for (Vehicle vehicle : customer.getPolicy().getVehicles()) {
for (Driver driver : vehicle.getDrivers()) {
if (driver.getAge() < 18) {
return true;
}
}
}
return false;
}
Это нарушило бы Закон Деметры, потому что этот код теперь знает о внутреннем, чего ему знать не нужно. Он знает, что водители назначаются транспортным средствам, а не просто назначаются страховому полису в целом. Если в будущем страховая компания решит, что водители будут просто числиться в полисе, а не закрепляться за конкретным транспортным средством, тогда этот код придется изменить.
Проблема в том, что он вызывает метод своего параметра, getPolicy()
, а затем еще один, getVehicles()
, а затем еще один, getDrivers()
, а затем еще один, getAge()
. Закон Деметры гласит, что метод класса должен вызывать методы только для:
- Сам
- Его поля
- Его параметры
- Объекты, которые он создает
(Последнее может быть проблемой для модульного тестирования, когда вы можете захотеть, чтобы объекты вводились или создавались фабриками, а не создавались непосредственно локально, но это не имеет отношения к закону Деметры.)
Чтобы решить проблему с hasUnderageDrivers()
, мы можем передать объект Policy
и у нас может быть метод для Policy
, который знает, как определить, есть ли в политике несовершеннолетние драйверы:
public boolean hasUnderageDrivers(Policy policy) {
return policy.hasUnderageDrivers();
}
Вызов на один уровень ниже, customer.getPolicy().hasUnderageDrivers()
, вероятно, допустим. Закон Деметры — это эмпирическое правило, а не жесткое правило. Вам также, вероятно, не нужно сильно беспокоиться о вещах, которые вряд ли изменятся; Driver
, вероятно, всегда будет иметь дату рождения и метод getAge()
.
Но возвращаясь к вашему случаю, что произойдет, если мы заменим все эти геттеры публичными полями? Это совсем не помогает с Законом Деметры. У вас все еще может быть та же проблема, что и в первом примере. Учитывать:
public boolean hasUnderageDrivers(Customer customer) {
for (Vehicle vehicle : customer.policy.vehicles) {
for (Driver driver : vehicle.drivers) {
if (driver.age < 18) {
return true;
}
}
}
return false;
}
(Я даже преобразовал driver.getAge()
в driver.age
, хотя, вероятно, это будет расчет, основанный на дате рождения, а не простом поле.)
Обратите внимание, что точно такая же проблема со встраиванием знаний о том, как составляется граф объектов (у клиента есть политика, в которой есть транспортные средства с водителями), возникает, когда мы пишем код с общедоступными полями вместо геттеров. Проблема связана с тем, как собираются части, а не с тем, вызываются ли геттеры.
Между прочим, обычная причина предпочесть геттеры общедоступным полям (конечным?) заключается в том, что вам может понадобиться добавить в них некоторую логику позже. Возраст заменяется расчетом, основанным на дате рождения и сегодняшней дате, или установщик должен иметь некоторую проверку (например, выдает, если вы передаете null
), связанную с ним. Я никогда раньше не слышал, чтобы Закон Деметры упоминался в таком контексте.
person
David Conrad
schedule
24.09.2014
x.getStuff()
иx.stuff
неразличимы (и где полеstuff
может даже не существовать во время вызова...) - person David Tonhofer   schedule 21.02.2016