Связь. Лучшие практики

11

Следуя этой теме, я начал

Синглтон

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

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

Я действительно не хочу впадать в вредные привычки и плохо программировать, поэтому буду признателен за ваши ответы.

Даррен Янг
источник

Ответы:

11

Чтобы сделать ваш код свободно связанным, вот несколько простых вещей, которые нужно запомнить:

Часть 1:

Технически известный как «Разделение концерна». Каждый класс играет определенную роль, он должен обрабатывать бизнес-логику или логику приложения. Старайтесь держаться подальше от класса, который сочетает в себе обе обязанности. т.е. класс, который управляет данными (в широком смысле), является логикой приложения, а класс, который использует данные, является бизнес-логикой.

Лично я называю это (в моем собственном маленьком мире) как create it or use it. Класс должен создать объект или использовать объект, который никогда не должен делать оба.

Часть 2:

Как осуществить разделение интересов.
Для начала есть два простых метода:

Примечание: шаблоны проектирования не являются абсолютными.
Они должны быть адаптированы к ситуации, но имеют основную тему, которая похожа на все приложения. Так что не смотрите на примеры ниже и говорите, что я должен строго следовать этому; это всего лишь примеры (и слегка надуманные).

Инъекция зависимости :

Здесь вы передаете объект, который использует класс. Объект, который вы передаете, основан на интерфейсе, чтобы ваш класс знал, что с ним делать, но не должен знать фактическую реализацию.

class Tokenizer
{
    public:
        Tokenizer(std::istream& s)
            : stream(s)
        {}
        std::string nextToken() { std::string token; stream >> token;return token;}
    private:
        std::istream& stream;
};

Здесь мы вводим поток в токенизатор. Токенайзер не знает, к какому типу относится поток, пока он реализует интерфейс std :: istream.

Шаблон сервисного локатора :

Шаблон локатора службы представляет собой небольшое изменение в зависимости от внедрения. Вместо того, чтобы давать объект, который он может использовать, вы передаете ему объект, который знает, как найти (создать) объект, который вы хотите использовать.

class Application
{
     public:
         Application(Persister& p)
             : persistor(p)
         {}

         void save()
         {
             std::auto_ptr<SaveDialog> saveDialog = persistor.getSaveDialog();
             saveDialog.DoSaveAction();
         }

         void load()
         {
             std::auto_ptr<LoadDialog> loadDialog = persistor.getLoadDialog();
             loadDialog.DoLoadAction();
         }
    private:
        Persister& persistor;
};

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

Это полезно, когда potentiallyкаждый раз при создании экземпляра действия требуется уникальный объект.

Лично я считаю, что это особенно полезно при написании юнит-тестов.

Примечание шаблонов:

Шаблоны проектирования - огромный предмет сам по себе. Это ни в коем случае не исключительный список образцов, которые вы можете использовать, чтобы помочь со слабой связью; это просто общая отправная точка.

С опытом вы поймете, что вы уже используете эти шаблоны, просто вы не использовали их формальные имена. Стандартизируя их имена (и заставляя всех учить их), мы обнаруживаем, что обмениваться идеями легче и быстрее.

Мартин Йорк
источник
3
@Darren Young: Спасибо, что приняли это. Но вашему вопросу всего три часа. Я вернусь через день или около того, чтобы убедиться, что другие не предоставили лучшие ответы.
Мартин Йорк,
Спасибо за отличный ответ. Что касается разделения проблем ... У меня есть классы, которые требуют некоторые данные для его использования, а затем что-то делать с этими данными. Вот как в прошлом я проектировал занятия. Поэтому было бы лучше создать класс, который получает данные и адаптирует их к правильной форме, а затем другой класс для фактического использования данных?
Даррен Янг
@Darren Young: Как и при любом программировании, линия серая и нечеткая, нечеткая. Трудно дать точный ответ, не читая ваш код. Но когда я говорю о, managing the dataя имею в виду переменные (не фактические данные). Таким образом, такими вещами, как указатели, нужно управлять, чтобы они не просачивались. Но данные могут быть введены или способ извлечения данных может быть абстрагирован (так что ваш класс может быть повторно использован с различными методами извлечения данных). Извините, я не могу быть более точным.
Мартин Йорк,
1
@Darren Young: Как отметил @StuperUser в своем ответе. Не идите за борт (ака minutae of loose coupling(люблю это слово мелочи)). Секрет программирования заключается в том, чтобы узнать, когда использовать методы. Чрезмерное использование может привести к путанице кода.
Мартин Йорк,
@Martin - спасибо за совет. Я думаю, что именно здесь я сейчас борюсь ... Я постоянно беспокоюсь об архитектуре своего кода, а также пытаюсь выучить конкретный язык, который я использую C #. Я предполагаю, что это прибудет с опытом и моим желанием изучить этот материал. Я ценю ваши комментарии.
Даррен Янг
6

Я являюсь разработчиком ASP.NET, поэтому не знаю много о связывании WinForms, но немного знаю о веб-приложениях N-уровня, предполагая 3-уровневую архитектуру приложений UI, Domain, Data Access Layer (DAL).

Слабая связь - это абстракции.

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

Один аспект чего-то слабо связанного - можно ли заменить объект другим, который реализует тот же интерфейс. Это не зависит от реального объекта, но абстрактное описание того, что он делает (его интерфейс).
Слабая связь, интерфейсы и инжекторы зависимостей (DI) и Inversion of Control (IoC) полезны для изоляции при тестировании.

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

Кроме того, в меню «Анализ» в VS (в зависимости от имеющейся у вас версии) есть инструменты для расчета метрик кода для вашего проекта, одним из которых является Class Coupling, более подробная информация об этом содержится в документации MSDN.

Не получить СЛИШКОМ увязли в minutae рыхлой связи , хотя, если есть НЕТ вероятность того, что вещи , чтобы повторно использовать (например , домен проекта с более чем одним UI) и жизнь продукта мала, то слабая связь становится меньше приоритета (это все равно будет принято во внимание), но все равно это будет обязанность архитекторов / технических руководителей, которые будут проверять ваш код.

StuperUser
источник
3

Сцепление относится к степени прямого знания, которое один класс имеет другой . Это не должно быть истолковано как инкапсуляция или не инкапсуляция. Это не ссылка на знание одним классом атрибутов или реализации другого класса, а скорее знание самого этого другого класса. Сильная связь возникает, когда зависимый класс содержит указатель непосредственно на конкретный класс, который обеспечивает требуемое поведение. Зависимость не может быть заменена или ее «сигнатура» изменена без необходимости изменения зависимого класса. Слабая связь возникает, когда зависимый класс содержит указатель только на интерфейс, который затем может быть реализован одним или несколькими конкретными классами.Зависимость класса зависит от «контракта», указанного интерфейсом; определенный список методов и / или свойств, которые должны обеспечивать реализации классов. Таким образом, любой класс, который реализует интерфейс, может удовлетворить зависимость зависимого класса без необходимости изменения класса. Это позволяет расширить возможности разработки программного обеспечения; новый класс, реализующий интерфейс, может быть написан для замены текущей зависимости в некоторых или во всех ситуациях без необходимости изменения зависимого класса; новые и старые классы могут свободно обмениваться. Сильная связь не позволяет этого. Ссылка

Амир Резаи
источник
3

Взгляните на 5 твердых принципов. Придерживаясь SRP, интернет-провайдер и DIP значительно снизят связь, DIP, безусловно, является самым мощным. Это фундаментальный принцип ниже уже упомянутого DI .

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

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

На самом деле, я нашел вопрос по stackoverflow , где я демонстрирую применение SOLID для конкретной проблемы. Может быть интересно читать

back2dos
источник
1

Согласно Википедии:

В вычислительной технике и системном проектировании слабосвязанная система - это система, в которой каждый из ее компонентов имеет или практически не использует определения других отдельных компонентов.

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

Напротив, в слабо связанной системе изменения относительно изолированы. Поэтому они дешевле и могут быть сделаны с большей уверенностью.

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

  • Логика хранения и поиска данных
  • Бизнес логика
  • Логика требуется для запуска интерфейса

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

Если вы серьезно относитесь к проектированию слабосвязанных систем, ознакомьтесь с принципами SOLID и шаблонами проектирования.

Однако важно понимать, что эти шаблоны и принципы - это просто шаблоны и принципы. Они не правила. Это означает, что их нужно применять прагматично и разумно

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

Всего наилучшего.

Kramii
источник
1

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

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

Homde
источник
4
Является ли презерватив каким-то абстрактным IT-термином или я просто не понимаю каламбур?
Даррен Янг
3
Это другое название для контейнера ввода зависимостей.
MCHL
2
Также отличный способ предотвратить распространение вирусов. Мы говорим об одном и том же?
Сова
1
Тяжелая связь часто делается на
бэкэнде
1
Установка заголовка вряд ли злоупотребляет форматированием
Homde
0

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

init, открыть, закрыть

против

setTheFoo, setBar, initX, getConnection, закрыть

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

Я не слишком беспокоюсь о необходимости изменять и перекомпилировать вызывающие. Я поддерживаю много кода, некоторые из которых новые и около 15 лет. Я обычно хочу ошибки компилятора при внесении изменений. Иногда я намеренно нарушаю API по этой причине. Это дает мне возможность рассмотреть последствия для каждого абонента. Я не большой поклонник внедрения зависимостей, потому что я хочу иметь возможность визуально отслеживать свой код без черных ящиков и хочу, чтобы компилятор ловил как можно больше ошибок.

Шон МакЭлигот
источник