Шукаєте поради щодо дизайну OO


12

Я розробляю додаток, який буде використовуватися для відкривання та закриття клапанів у промислових умовах, і думав про щось таке, як це: -

public static void ValveController
{
    public static void OpenValve(string valveName)
    {
        // Implementation to open the valve
    }

    public static void CloseValve(string valveName)
    {
        // Implementation to close the valve
    }
}

(Реалізація записує кілька байтів даних до послідовного порту для управління клапаном - "адреса", отримана від назви клапана, і "1", або "0", щоб відкрити або закрити клапан).

Інший розробник запитав, чи слід замість цього створити окремий клас для кожного фізичного клапана, яких десятки. Я погоджуюся, що було б приємніше писати код як, PlasmaValve.Open()а не ValveController.OpenValve("plasma"), але чи це надмірність?

Крім того, мені було цікаво, як найкраще вирішити дизайн з парою гіпотетичних майбутніх вимог:

  1. Нас просять підтримувати клапан нового типу, який вимагає різних значень для його відкривання та закриття (не 0 і 1).
  2. Нас просять підтримувати клапан, який можна встановити в будь-яке положення від 0-100, а не просто «відкрити» або «закрити».

Як правило, я б використовував спадщину для подібних речей, але останнім часом я почав обертатися "складом над спадщиною" і цікавлюсь, чи існує розсуд, який має використовувати композиція?


2
Я створив би загальний клас клапана, який має ідентифікатор для конкретного клапана (не рядок, можливо, перерахунок) та будь-яку інформацію, необхідну для управління потоком всередині методів OpenValve / CloseValve. Крім того, ви можете зробити клас valv абстрактним і зробити окремі реалізації для кожного з них, коли клапан відкривання / закриття просто викликає логіку всередині даного класу клапанів для випадку, якщо різні клапани мають різні механізми відкривання / закривання. Загальний механізм буде визначений у базовому класі.
Джиммі Хоффа

2
Не хвилюйтеся з приводу майбутніх гіпотетичних вимог. ЯГНІ.
pdr

3
@pdr YAGNI - двостороння лезо, я погоджуюся, що варто дотримуватися в цілому, але, якщо взяти до крайності, можна сказати, що робити що-небудь, щоб допомогти майбутній ремонтопридатності чи читанність порушує YAGNI, тому що я вважаю сферу YAGNI занадто неоднозначною для багато. Однак, багато людей знають, де користуватися YAGNI і куди її кинути, оскільки облік майбутнього врятує вам серйозні болі. Я просто думаю, що слід бути обережним, пропонуючи людям слідувати за ЯГНІ, коли не знаєш, куди вони приземляться у цьому спектрі.
Джиммі Хоффа

2
Людина, "склад над успадкуванням" завищений. Я б зробив абстрактний клас / інтерфейс Valve, а потім підкласив їх у PlasmaValve. І тоді я би переконався, що мій ValveController працюватиме з Valve (ими), не піклуючись про те, який саме підклас вони є.
MrFox

2
@suslik: Абсолютно. Я також бачив чудовий код, який називають спагетті людьми, які не розуміють принципів SOLID. Ми могли б продовжувати це з цим назавжди. Моя думка полягає в тому, що я бачив більше проблем, спричинених відхиленням встановлених принципів (народжених багаторічним досвідом) поза рукою, ніж я бачив, викликаних надмірною прихильністю. Але я згоден, що обидві крайності небезпечні.
пдр

Відповіді:


12

Якщо кожен екземпляр об'єкта клапана би виконував той самий код, що і цей ValveController, то, схоже, кілька випадків одного класу було б правильним шляхом. У цьому випадку просто налаштуйте, який клапан керує (і як) в конструкторі об'єкта клапана.

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


1
+1 для згадування тверджень на основі типу перемикачів як запаху коду. Я часто бачу подібні заяви про перемикання, коли розробник стверджує, що він саме слідкував за KISS. Ідеальний приклад того, як принципи дизайну можуть бути зірвані хе
Джиммі Хоффа

2
Кілька екземплярів можуть також полегшити з'єднання клапанів в послідовності, що дозволяє моделювати фактичний трубопровід установки як спрямований графік у вашому коді. Потім ви можете додати ділову логіку до класів, якщо вам потрібно зробити щось на кшталт відкриття одного клапана, коли інший закривається, щоб уникнути накопичення тиску, або закрити всі клапани нижче за течією, щоб у вас не було ефекту "молот для води" коли клапан знову відкриється.
TMN

1

Моє основне захоплення - це використання рядків для параметра, що ідентифікує клапан.

Принаймні створіть Valveклас, який має getAddressформу, в якій лежать основні потреби в реалізації, і передайте їх до ValveControllerта переконайтеся, що ви не можете створити неіснуючі клапани. Таким чином, вам не доведеться обробляти неправильні рядки в кожному з відкритих і закритих методів.

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

Якщо вам подобається тестування, ви також повинні зробити ValveControllerсингл, щоб ви могли знущатися над ним (або створити тренувальну машину для операторів).


Я ніколи не бачив, щоб хтось рекомендував синглет заради тестування - зазвичай це йде іншим шляхом.
Казарк

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