Слышал, что перекрёстные ссылки между классами это признак плохого кода. Почему?
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания http://rsdn.ru/Info/rules.xml
Здравствуйте, igor-booch, Вы писали:
IB>Слышал, что перекрёстные ссылки между классами это признак плохого кода. Почему?
Хм, вроде вопрос по типу, отчего не следует подносить руки близко к огню.
Вообще то "хорошие" объекты должны быть достаточно изолированы и бывают варианты, когда объекты должны знать друг о друге.
Но, имея два объекта с бепорядочными сслыками друг на друга можно считать это одним обектом, а если это не два а больше, то код превращается в там называемое спагетти, которое работает чисто по волшебству.
Одним изолированным объектом всегда проще управлять и его контролировать.
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания http://rsdn.ru/Info/rules.xml
Здравствуйте, igor-booch, Вы писали:
IB>Слышал, что перекрёстные ссылки между классами это признак плохого кода. Почему?
It depends, как всегда. Если объекты логически связаны друг с другом и обе ссылки контролирует только один из классов — никаких особых проблем я не вижу. В обратном случае — да, могут вылезти неочевидные косяки из-за зависимости между двумя в теории несвязанными объектами.
Здравствуйте, igor-booch, Вы писали:
IB>Слышал, что перекрёстные ссылки между классами это признак плохого кода. Почему?
Это может быть как плохо, так и нормально...как обычно.
Но в этом случае получается сильная связанность классов. Одну из связей (или обе) можно попробовать заменить на интерфейс.
Здравствуйте, Rustavelli, Вы писали:
R>Но в этом случае получается сильная связанность классов. Одну из связей (или обе) можно попробовать заменить на интерфейс.
Я бы не назвал такой подход правильным. Интерфейсы в первую очередь служат для описания ролей ("ведёт себя как", эдакий типизированный duck-typing). Классы — для описания сущностей ("является чем-то"). Когда мы прячем информацию о сильной связанности в интерфейс, мы намекаем, что сильная связность нужна только для некоторых сценариев использования. Здесь налицо нарушение SRP и в таких случаях лучше вообще отказаться от двусторонней зависимости.
Здравствуйте, skeptic, Вы писали:
S>Здравствуйте, igor-booch, Вы писали:
IB>>мой руководитель
S>И правильно делает ваш руководитель — связи между сущностями надо резать нещадно бритвой Оккама и выносить их в отдельную сущность. S>Начинать отсюда
Угу, только вспоминаем исходную цитату...
Не следует привлекать новые сущности без самой крайней на то необходимости
Так что 20 раз сначала стоит подумать, настолько ли ощутима связь, и стоит ли ее выносить в отдельную сущность
Хотя иногда, ну ей богу все же стоит
Я думаю дело вот в чем.
Если в любой связи между класса, какой-то класс выполняет роль поставщика услуг, а другой класс потребляет эти услуги,
то это делает классы простыми для понимания, отладки, сопровождения и т. д.
Это как в в удалённом взаимодействии,
клиент знает о сервисе, а сервис не знает о клиенте,
клиент спрашивает, сервис отвечает,
у клиента сначала работает рот, потом уши,
у сервиса сначала уши, потом рот.
Если нужно двунаправленное взаимодействие, то есть три пути:
1) изменить логику, так чтобы убрать перекрёстную ссылку, то есть сделать связь однонаправленной
2) если хочется вообще разорвать связь между классами, создать посредника, который будет знать об обоих классах.
3) поступить так же как поступают, если надо организовать двунаправленную коммуникацию между сервером и клиентской машиной: на сервере делается сервис и клиент и на клиентской машине делается сервис и клиент.
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания http://rsdn.ru/Info/rules.xml
Здравствуйте, igor-booch, Вы писали:
IB>Слышал, что перекрёстные ссылки между классами это признак плохого кода. Почему?
По той же причине, по которой в проектировании БД считается плохим наличие связей «многие ко многим» — такую архитектуру сложнее поддерживать, понимать и развивать. Перекрёстные ссылки между классами как правило свидетельствуют о том, что в класс запихнули более одной сущности. В реальной жизни объекты могут взаимоопределяться, например: сила трения об окружающую среду зависит от скорости падающего объекта, и наоборот — скорость падающего объекта зависит в том числе и от силы трения. Однако классы не обязательно должны реализовывать абстракцию отдельной «вещи», класс может описывать асбтрактное свойство или вообще только поведение. Чтобы не быть голословным, приведу пример:
Есть главный класс CMachine, делающий нечто сложное и есть класс CDetail, являющийся некоторой герметичной, но неотъемлимой частью класса CMachine:
class CDetail {...};
class CMachine
{
...
CDetail m_Detail;
}
По ходу реализации мы добавляем некоторый перечисляемый тип EState, как неотъемлимую часть CMachine (допустим CMachine — конечный автомат). Ну и следующая очень вероятная потребность — состояние EState потребуется для какого-нибудь метода CDetail. И если мы определим EState внутри CMachine, то теперь в заголовочный файл класса CDetail нужно будет подключать заголовочный файл класса CMachine.
Можно пойти другим путём — выделить состояние машины в отдельный файл. Правда теперь EState повисает в открытом космосе, что за «состояние», чъё «состояние»? Тут два подхода, либо отражать в названии (CCombustionEngine, CCombustionEngineDetail, ECombustionEngineState), либо (мой вариант) для всего конгломерата классов и типов определить namespace, и одноимённый ему filter в проекте. Внутри этого контекста понятно чъё состояние, извне оно будет выглядеть так: CombustionEngine::EState.
Теперь программист который должен проанализировать и/или расширить функциональность CDetail может ограничиться лишь просмотром возможных состояний CMachine и не анализировать его интерфейс.
В случае если изначально зависимому классу понадобился метод-член главного класса, то это тяжёлый случай взаимной зависимости. Очень вероятно в таком случае, что отношение между главным и «зависимым» классом определяется как «является» и необходимо использовать не агрегацию, а наследование и паттерн «шаблонный метод».
Можно решать проблему двусторонней связи в другом направлении, сделать класс CDetail локальным, но это приемлимо только при простоте обеих классов.
Здравствуйте, Sinix, Вы писали:
S>Интерфейсы .... эдакий типизированный duck-typing
Резазануло по ушам мозгам.
Наследование "говорит" о том, что объекты класса являются:
class Дерево {}
class Яблоня :Дерево {}
т.е. яблоня является деревом, кстати, это основной принцип постоения иерархии.
а вот duck-typing исповедует совершенно другую идеалогию: при некоторых условиях персона может быть уткой.
[go]
class Duck:
def quack(self):
print("Quaaaaaack!")
def feathers(self):
print("The duck has white and gray feathers.")
class Person:
def quack(self):
print("The person imitates a duck.")
def feathers(self):
print("The person takes a feather from the ground and shows it.")
def name(self):
print("John Smith")
Здравствуйте, igor-booch, Вы писали:
IB>Слышал, что перекрёстные ссылки между классами это признак плохого кода. Почему?
Потому, что это определяет жёсткую связь между классами.
Например, усложняет замену одного класса на другой, это осложняет раздельное тестирование классов.
Если есть такая необходимость, то лучше всего выносить нужные методы в интефейсы.
Всё сказанное выше — личное мнение, если не указано обратное.
Здравствуйте, Философ, Вы писали:
S>>Интерфейсы .... эдакий типизированный duck-typing
Ф>а вот duck-typing исповедует совершенно другую идеалогию: при некоторых условиях персона может быть уткой.
Ну да. Всё, что делают интерфейсы — добавляют возможность проверки на соблюдение контракта во время компиляции. Если заменю выделенное выше на "статитчески-проверяемый", будете ещё придираться?
Здравствуйте, Sinix, Вы писали:
S>Я бы не назвал такой подход правильным. Интерфейсы в первую очередь служат для описания ролей
Интерфейсы служат для изоляции контрактов в чистом виде. Чисто техническое решение, позволяющее избавится от проблем множественного наследования и кое каких еще. Способ использования — целиком и полностью зависит от конкретного дизайна.
S> ("ведёт себя как", эдакий типизированный duck-typing).
duck typing никак с типизированностью не конфликтует. К примеру, при наложении делегата на метод в дотнете имеем вполне себе классический, и при этом строго типизированный duck typing.
S>Классы — для описания сущностей ("является чем-то").
Ну да. Только стоит учесть, что сущностью в модели мы можем сделать что угодно, в том числе и роль.
S> Когда мы прячем информацию о сильной связанности в интерфейс, мы намекаем, что сильная связность нужна только для некоторых сценариев использования. Здесь налицо нарушение SRP и в таких случаях лучше вообще отказаться от двусторонней зависимости.
Сдается мне, он просто так специфично описал IoC.
... << RSDN@Home 1.2.0 alpha 5 rev. 1530 on Windows 7 6.1.7601.65536>>
Здравствуйте, Философ, Вы писали:
Ф>Наследование "говорит" о том, что объекты класса являются: Ф>class Дерево {} Ф>class Яблоня :Дерево {} Ф>т.е. яблоня является деревом, кстати, это основной принцип постоения иерархии.
Ф>а вот duck-typing исповедует совершенно другую идеалогию: при некоторых условиях персона может быть уткой.
Да ту же самую идеологию оно исповедует, разница только в способе конструирования иерархии — со стороны самих сущностей иерархии vs с внешней стороны. Разница примерно такая же, как между виртуальными методами и паттерном визитор.
... << RSDN@Home 1.2.0 alpha 5 rev. 1530 on Windows 7 6.1.7601.65536>>
Здравствуйте, AndrewVK, Вы писали:
AVK>duck typing никак с типизированностью не конфликтует. К примеру, при наложении делегата на метод в дотнете имеем вполне себе классический, и при этом строго типизированный duck typing.
Здравствуйте, Ikemefula, Вы писали:
I>А что значит "наложение делегата на метод" ?
Создание экземпляра делегата над конкретным методом. В отличие от джавы с ее listeners, в дотнете не нужно при этом точное совпадение типа, достаточно совпадения сигнатуры, да еще и с ко/контрвариантностью.
... << RSDN@Home 1.2.0 alpha 5 rev. 1530 on Windows 7 6.1.7601.65536>>