Зчеплення. Кращі практики


11

Слідом за цією темою я почав

Однотонний візерунок

Це змусило мене задуматися про те, як поєднані мої заняття та як найкраще досягти вільної зв'язку. Будь ласка, майте на увазі, що я новий програміст (4 місяці моєї першої роботи), і це справді перший розгляд, який я приділяв цьому, і дуже прагну зрозуміти цю концепцію.

Отже, що саме являє собою вільне з’єднання проти важкого зчеплення? У своєму поточному (і першому проекті) я працюю над проектом ac # winforms, де розділ GUI створює об'єкти та підписує їх події, коли вони спрацьовують, GUI створює інший об’єкт (у цьому прикладі datagridview (клас що я створив, що обертається навколо стандартного перегляду даних і додає додаткову функціональність) і приєднує його до графічного інтерфейсу. Це погана зв'язок чи гарна?

Я дійсно не хочу вникати в шкідливі звички і починати погано кодувати, отже, я буду вдячний за ваші відповіді.

Відповіді:


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 у своїй відповіді. Не переходьте за борт (AKA minutae of loose coupling(люблю це слово minutae)). Секрет програмування полягає в тому, щоб навчитися використовувати методи. Перевикористання може призвести до замішування коду.
Мартін Йорк

@Martin - дякую за пораду. Я думаю, що там я зараз борюся ..... Я схильний постійно турбуватися про архітектуру свого коду, а також намагаюся вивчити мову, якою я користуюся C #. Я здогадуюсь, що це матиме досвід і моє бажання вивчити цей матеріал. Я ціную ваші коментарі.
Даррен Янг

6

Я розробник ASP.NET, тому не знаю багато про з'єднання WinForms, але знаю небагато з веб-додатків N-Tier, припускаючи, що трирівнева архітектура додатків користувальницького інтерфейсу, домену, рівня доступу до даних (DAL).

Вільна муфта стосується абстракцій.

Як @MKO заявляє, що якщо ви можете замінити збірку на іншу (наприклад, новий проект інтерфейсу користувача, який використовує проект вашого домену, новий DAL, який зберігається в електронній таблиці, а не в базі даних), то відбувається нещільне з'єднання. Якщо ваш Домен та DAL залежать від проектів далі по ланцюгу, то зв'язок може бути слабкішим.

Одним із аспектів того, що щось зв'язано вільно, є те, чи можна замінити об'єкт іншим, який реалізує той самий інтерфейс. Це залежить не від фактичного об'єкта, а від абстрактного опису того, що він робить (його інтерфейсу).
Вільний зв'язок, інтерфейси та інжектори залежностей (DI) та інверсія управління (IoC) корисні для аспекту ізоляції проектування для перевірки.

Напр. Об'єкт у проекті інтерфейсу викликає об’єкт сховища в доменному проекті.
Ви можете створити підроблений об'єкт, який реалізує той самий інтерфейс, що і сховище, який використовує тестовий код, а потім записати спеціальну поведінку для тестів ( заглушки для запобігання виробничого коду, який зберігає / видаляє / отримує виклик, і знущається, що виконує функцію заглушки і відстежує про стан підробленого об’єкта для цілей тестування).
Значить, єдиний виклик коду, який викликається, тепер лише у вашому об'єкті користувальницького інтерфейсу, ваш тест буде відповідати лише тому методу, і будь-які тести пошкоджуватимуть дефект цього методу.

Також у меню "Аналіз" у VS (залежно від наявної у вас версії) є інструменти для обчислення кодових метрик для вашого проекту, одним із яких є "З'єднання класів", більше інформації про це буде знайдено в документації MSDN.

Не зациклюйтеся на TOO в хвилини розпущеного з’єднання, хоча, якщо немає жодного шансу, що щось буде повторно використане (наприклад, проект домену з більш ніж одним інтерфейсом), а термін експлуатації продукту невеликий, тоді нещільна муфта стає меншою пріоритету (він все ще буде врахований), але все одно відповідальність архітекторів / технічних клієнтів буде переглядати ваш код.


3

Зв'язування стосується ступеня прямого знання, яке має один клас іншого . Це не означає трактувати як інкапсуляція проти некапсуляції. Це не посилання на знання одного класу про атрибути чи реалізацію іншого класу, а скоріше знання самого іншого класу. Сильне з'єднання відбувається, коли залежний клас містить вказівник безпосередньо на конкретний клас, який забезпечує необхідну поведінку. Залежність не може бути замінена або її "підпис" змінено, не вимагаючи зміни до залежного класу. Вільне сполучення відбувається, коли залежний клас містить вказівник лише на інтерфейс, який потім може бути реалізований одним або багатьма конкретними класами.Залежність класу залежно від "контракту", визначеного інтерфейсом; визначений перелік методів та / або властивостей, які повинні надавати класи реалізації. Таким чином, будь-який клас, який реалізує інтерфейс, може задовольнити залежність класу, не змінюючи клас. Це дозволяє розширити можливості в розробці програмного забезпечення; новий клас, що реалізує інтерфейс, може бути записаний для заміни поточної залежності в деяких чи всіх ситуаціях, не вимагаючи зміни на залежний клас; нові та старі класи можна міняти вільно. Міцна муфта цього не дозволяє. Посилання на посилання


3

Погляньте на 5 принципів SOLID . Дотримуючись SRP, ISP і DIP будуть суттєво знижувати зв'язок, DIP є найбільш потужним. Це основоположний принцип, що лежить в основі вже згаданого DI .

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

І останнє, цей розділ на IoC вам може бути досить корисним, як вхідний пункт до загальних методів.

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


1

За даними Вікіпедії:

В обчислювальних системах та проектуванні систем слабко пов'язана система - це система, де кожен з її компонентів має або не використовує мало визначень інших окремих компонентів або взагалі їх не має.

Проблема з тісним з'єднанням ускладнює внесення змін. (Здається, багато письменників припускають, що це в першу чергу спричиняє проблеми під час технічного обслуговування, але, на мій досвід, це актуально і під час початкової розробки.) Що, як правило, відбувається в щільно зв'язаних системах, це те, що зміна одного модуля в системі вимагає додаткових змін в модулях, до яких він пов'язаний. Найчастіше для цього потрібні більші зміни в інших модулях тощо.

Навпаки, у слабко пов'язаній системі зміни є відносно ізольованими. Тому вони дешевші і їх можна зробити з більшою впевненістю.

У вашому конкретному прикладі матеріали поводження з подіями дійсно забезпечують деякий поділ між GUI та базовими даними. Однак це звучить так, ніби є інші ділянки розділення, які ви могли б вивчити. Без деталізації вашої конкретної ситуації важко бути конкретним. Однак ви можете почати з трирівневої архітектури, яка відокремлює:

  • Логіка зберігання та пошуку даних
  • Бізнес-логіка
  • Логіка, необхідна для запуску інтерфейсу користувача

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

Якщо ви серйозно ставитесь до розробки систем, що не підлягають з’єднанню, ознайомтеся з принципами SOLID та конструкціями.

Однак важливо усвідомити, що ці закономірності та принципи є саме такими - закономірностями та принципами. Вони не правила. Це означає, що їх потрібно застосовувати прагматично та розумно

Що стосується шаблонів, важливо розуміти, що не існує єдиного "правильного" впровадження жодної з цих моделей. Також вони не є шаблонами для вирізання файлів cookie для розробки вашої власної реалізації. Вони там, щоб сказати вам, яка форма може мати гарне рішення, і надати вам спільну мову для спілкування дизайнерських рішень з іншими розробниками.

Всього найкращого.


1

Використовуйте ін'єкційне залежність, стратегії та події. Взагалі: читайте про дизайнерські зразки, вони все стосуються слабкої зв'язку та зменшення залежностей. Я б сказав, що події настільки ж слабко пов'язані, як у вас, тоді як введення залежностей та стратегічних малюнків потребують певних інтерфейсів.

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


4
Презерватив якийсь абстрактний термін ІТ чи я просто не отримую каламбур?
Даррен Янг

3
Це ще одна назва контейнера для ін'єкцій залежностей.
Mchl

2
Також відмінний спосіб запобігти поширенню вірусів. Ми говоримо про одне й те саме?
сова

1
Важка муфта часто робиться на
бекенді

1
Налаштування заголовка навряд чи зловживає форматуванням
Хомд

0

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

init, відкрити, закрити

проти

setTheFoo, setBar, initX, getConnection, закрити

Перший очевидний і схожий на приємний API. Другий може спричинити помилки, якщо викликати в неправильному порядку.

Я не надто переживаю, що потрібно змінювати та перекомпілювати абонентів. Я підтримую багато коду, деякі з них нові, а деякі 15 років. Я зазвичай хочу помилок компілятора, коли я вношу зміни. Іноді я навмисно зламаю API з цієї причини. Це дає мені можливість розглянути наслідки для кожного абонента. Я не є великим прихильником введення залежності, тому що я хочу вміти візуально простежувати свій код без чорних коробок і хочу, щоб компілятор вдавав якомога більше помилок.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.