Сцепление, сплоченность и закон Деметры

Закон Деметры указывает, что вы должны говорить только с объектами, о которых вы знаете напрямую. То есть, не выполняйте цепочку методов, чтобы разговаривать с другими объектами. Когда вы это делаете, вы устанавливаете неправильные связи с промежуточными объектами, ненадлежащим образом coupling ваш код для другого кода.

Это плохо.

Решение будет состоять в том, что класс, о котором вы знаете, существенно раскрывает простые обертки, которые делегируют ответственность за объект, с которым он имеет отношение.

Это хорошо.

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

Это плохо.

Действительно ли это приводит к снижению сплоченности? Это меньшее из двух зол?

Является ли это одной из тех серых областей разработки, где вы можете обсуждать, где находится эта линия, или существуют сильные, принципиальные способы принятия решения о том, где рисовать линию и какие критерии вы можете использовать для принятия этого решения?

Ответ 1

Грэди Буч в "Объектно-ориентированном анализе и дизайне":

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

Предоставьте Собаку с Клиентом в приведенном выше примере, и это может быть немного яснее. Таким образом, цель состоит в том, чтобы просто нацелиться на функциональную сплоченность и как можно больше отойти от совпадения. В зависимости от ваших абстракций это может быть просто или может потребовать некоторого рефакторинга.

Комбинация примечаний применяется так же, как к "модулю", чем к одному классу, т.е. группа классов, работающих вместе. Таким образом, в этом случае классы Customer and Order по-прежнему имеют приличную сплоченность, потому что у них такое сильное отношение, клиенты создают заказы, заказы принадлежат клиентам.

Маркус Фаулер говорит, что ему было бы удобнее называть это "Предложение демона" (см. статью Mocks не являются заглушками):

"Тестеры-матки пытаются больше поговорить об избегании" обрывов поездов "- цепочки методов стиля getThis(). getThat(). getTheOther(). Избегание цепочек методов также известно как закон Деметры. запах, противоположная проблема предметов среднего возраста, раздутая с помощью методов пересылки, также является запахом. (Я всегда чувствовал, что мне было бы более комфортно с Законом Деметры, если бы это называлось Предложение Demeter.)"

То, что я прекрасно понимаю, откуда я берусь: это вполне приемлемо и часто необходимо иметь более низкий уровень сплоченности, чем может потребоваться строгое соблюдение "закона". Избегайте совпадения сплоченности и нацеленности на функциональное сцепление, но не подвешивайте его на настройке, где это необходимо для более естественного сочетания с абстракцией вашего дизайна.

Ответ 2

Если вы нарушаете Закон Деметры, имея

int price = customer.getOrder().getPrice();

решение заключается не в создании getOrderPrice() и преобразовании кода в

int price = customer.getOrderPrice();

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

Ответ 3

Я думаю, вы, возможно, неправильно поняли, что такое сплоченность. Класс, который реализуется в терминах нескольких других классов, не обязательно имеет низкую сплоченность, если он представляет собой четкую концепцию и имеет четкую цель. Например, у вас может быть class Person, который реализуется в терминах классов Date (для даты рождения), Address и Education (список школ, куда отправился человек). Вы можете предоставить обертки в Person для получения года рождения, в последнюю школу, в которую отправился человек, или в состояние, в котором он живет, чтобы не подвергать сомнению тот факт, что Person реализуется с точки зрения этих других классов. Это уменьшило бы сцепление, но это сделало бы Person не менее сплоченным.

Ответ 4

Его серая область. Эти принципы предназначены для того, чтобы помочь вам в вашей работе, если вы обнаружите, что вы работаете на них (т.е. они мешают вам и/или вы его усложняете, усложняют ваш код), тогда вы слишком усердны, и вам нужно отступить.

Сделайте это для вас, не работайте для него.

Ответ 5

Я не знаю, действительно ли это снижает сцепление.

Агрегация/состав - это все, что касается класса, использующего другие классы для соответствия контракту, который он предоставляет через свои общедоступные методы. Класс не должен дублировать интерфейс связанных объектов. Это фактически скрывает любое knowowledge об этих агрегированных классах от вызывающего метода.

Чтобы подчиняться закону Деметры в случае нескольких уровней зависимости класса, вам просто нужно применять агрегацию/состав и хорошую инкапсуляцию на каждом уровне.

Другими словами, каждый класс имеет одну или несколько зависимостей от других классов, однако это только зависимости от ссылочного класса, а не от любых объектов, возвращаемых из свойств/методов.

Ответ 6

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