2009-07-20 3 views
5

Law of Demeter не предотвращает передачу объектов в конструкторы классов. Тем не менее, он запрещает получать тот же самый объект позже и вызывать метод на нем, чтобы получить скалярное значение. Вместо этого должен быть создан прокси-метод, который вместо этого возвращает скалярное значение. Мой вопрос в том, почему приемлемо передавать объект в конструктор класса, но неприемлемо вернуть один и тот же объект позже и вытащить из него значение?Закон Деметры и Конструкторы классов

+0

Можете ли вы предоставить ссылку на «Закон диаметра»? – gahooa

ответ

13

Поскольку Закон Деметры говорит, что вы не должны проектировать внешний интерфейс объекта, чтобы он выглядел так, как будто он состоит из некоторых других объектов с известными интерфейсами, которые клиенты могут просто захватить и получить доступ.

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

Например, если вы передаете URL-адрес в свой конструктор объекта HTTPRequest, это не означает, что HTTPRequest должен иметь метод getURL, который возвращает объект URL, по которому ожидается, что вызывающий абонент вызовет getProtocol, getQueryString, и т. д. Если кто-то, у кого есть объект HTTPRequest, может захотеть узнать протокол запроса, он должен (согласно закону) узнать, вызвав getProtocol на объекте, который у них есть, а не на каком-то другом объекте, с которым они узнают, HTTPRequest хранит внутренне.

Идея состоит в том, чтобы уменьшить сцепление - без Закона Деметры пользователь должен знать интерфейс HTTPRequest и URL для получения протокола. С Законом им нужен только интерфейс HTTPRequest. И HTTPRequest.getProtocol() явно может возвращать «http» без необходимости использования какого-либо объекта URL для обсуждения.

Тот факт, что иногда пользователь объекта запроса является тем, кто его создал, и поэтому использует интерфейс URL, чтобы передать этот параметр, ни здесь, ни там. Не все пользователи объектов HTTPRequest сами создали их. Таким образом, клиенты, которые имеют право в соответствии с Законом на доступ к URL-адресу, потому что они сами создали его, могут сделать это таким образом, а не хватать его от запроса. Клиенты, которые не создали URL-адрес, не могут.

Лично я считаю, что Закон Деметры, как обычно указано в простой форме, взломан. Могут ли они серьезно сказать, что если у моего объекта есть строковое поле «Имя», и я хочу знать, содержит ли имя какие-либо символы, отличные от ASCII, тогда я должен либо определить метод NameContainsNonASCIICharacters на моем объекте, а не смотреть на саму строку, иначе добавить функцию visitName в класс с функцией обратного вызова, чтобы обойти это ограничение, убедившись, что строка является параметром для функции, которую я написал? Это не меняет сцепление вообще, оно просто заменяет методы геттера методами посетителей. Должен ли каждый класс, возвращающий целое число, иметь полный набор арифметических операций, если я хочу манипулировать возвращаемым значением? getPriceMultipliedBy (int n)? Наверняка нет.

Для чего это полезно, так это то, что когда вы его нарушаете, вы можете спросить себя, почему вы его нарушаете, и можете ли вы разработать лучший интерфейс, не нарушая его. Часто вы можете, но на самом деле это зависит от того, какие объекты вы говорите. Некоторые интерфейсы могут безопасно сочетаться с обширными фрагментами кода - такими, как целые, строковые и даже URL-адреса, которые представляют собой широко используемые концепции.

+0

Смотрите, Uri-оболочка - это своего рода вездесущий класс, который я бы освободил от этого правила, точно так же, как строка. Как средство достижения цели, это хорошее эмпирическое правило, но это не самоцель. Цель состоит в том, чтобы скрыть детали внутренней реализации, а не играть coy. –

+0

Да, у меня не было бы никакого аргумента, если бы это было названо «Руководством Деметры», но нет смысла быть робким, когда пытаешься стадных кошек советую программистов. Я не сомневаюсь, что в качестве академического упражнения, применяя его как закон, интересно и плодотворно и дает много идей для улучшения дизайна интерфейса. ИМО вы рисуете линию, как только вы регулярно встраиваете один интерфейс в другой. Иногда у X фактически есть только Y, и все это знают :-) –

+0

Хорошо сказано. Моя забота о типичных программистах - это то, что некоторые из них склонны рассматривать хорошие идеи как магические. Стереотипным примером является недавний выпускник, который читает книгу GoF по шаблонам проектирования, а затем уходит, чтобы изменить ВСЕ в Singleton. –

4

Идея состоит в том, что вы говорите только с ближайшими друзьями. Таким образом, вы не сделаете этого ...

var a = new A(); 
var x = a.B.doSomething(); 

Вместо того, чтобы вы это сделать ...

var a = new A(); 
var x = a.doSomething(); // where a.doSomething might call b.doSomething(); 

Это имеет свои преимущества, поскольку вещи становятся проще для звонящих (Car.Start() по сравнению с Car.Engine.Start()), но вы получите много маленьких методов обертывания. Вы также можете использовать Mediator pattern для смягчения этого типа «нарушения».

+0

Я никогда не слышал о Деметере раньше, поэтому ... имеет смысл, что удобные функции, которые делегируют B, потребуются, но «B getB() const« запрещено? Если да, то почему? –

+0

Вот и вся идея закона ... его можно было пересказать «Ты говоришь только с твоими ближайшими друзьями». Какая польза? Эта идея существует уже давно. В наши дни мы, конечно, пишем очень слабо связанные приложения. –

4

Ответ JP довольно хорош, так что это просто дополнение, а не несогласие или другая замена.

Как я понимаю, эта эвристика заключается в том, что вызов А не должен прерываться из-за изменения класса В. Поэтому, если вы связываете свои вызовы с помощью a.b.foo(), интерфейс A становится зависимым от B, нарушая правило. Вместо этого вы должны вызвать a.BFoo(), который вызывает b.foo() для вас.

Это хорошее эмпирическое правило, но это может привести к неудобному коду, который на самом деле не затрагивает зависимость так, как ее закрепляют. Теперь A должен предлагать BFoo навсегда, даже если B больше не предлагает Foo. Не так много улучшений, и было бы, пожалуй, лучше, по крайней мере, в некоторых случаях, если бы изменения в B нарушили вызывающего абонента, который хочет Foo, а не самого B.

Я бы также добавил, что, строго говоря, это правило постоянно нарушается для определенной группы вездесущих разделов, таких как строка. Возможно, приемлемо решить, какие классы также повсеместно распространены в определенном слое приложения и свободно игнорировать «Правило» Деметры для них.

+0

Kinda должен +1 вы здесь, так как вы сказали то же самое, что и я, только короче и быстрее :-) –

+0

Amd Я вернул услугу, так как вы включили хороший пример. –