Повторно винаходити дизайн системи для Scala


35

Багато, багато місяців тому я займався майстрами в області об'єктно-орієнтованої програмної інженерії. Я висвітлював усе: ініціювання проекту, вимоги, аналіз, дизайн, архітектура, розробка тощо, тощо. Моєю улюбленою інформаційною книжкою всіх часів була розробка об’єктно-орієнтованого програмного забезпечення, підхід, заснований на досвіді (IBM-1996). Книга, створена групою справжніх експертів свого часу. Він описує підхід, орієнтований на робочий продукт, до об'єктно-орієнтованих методів аналізу, проектування та розробки.

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

Дизайн та розробка втратили це задоволення, і я збирався залишити всю ІТ-індустрію за собою.

Тоді я відкрив Скалу. О, як м'який дощ на запиленій дорозі. Все просто стало ясно, повітря знову стало солодким, і маленьке світло мого жорсткого диска заграє глибоко в ніч, весело тримаючи компанію у моєму світі відкриття.

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

Як ми розробляємо чисті рішення Scala?

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

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

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

Чи існує така конструкція / процес / рішення чи пора її вигадати?


+1 для "можна збільшити відповідно до ваших потреб, як пара штанів йоги".
Рассел

FWIW, я бачив питання про UML та Scala, яке може вас зацікавити. Крім того, якщо ви переходите до функції, то позначення теорії категорій, можливо, варто вивчити. Григорій Мередіт зазвичай посилається на кілька цікавих презентацій про це, хоча я не впевнений, що він має у своєму блозі - це все одно варто перевірити.
Даніель К. Собрал

Це коштує багато ;-) Це дає мені напрям копати. Дякую Даніелю
Джеку

Варто перевірити, "модифікований" UML для Scala, github.com/mnn/Dia2Scala/blob/master/other/Notation.md
WeiChing Lin,

Відповіді:


12

Я не можу дати відповіді на це питання, але дозвольте запропонувати кілька думок.

Я студент комп’ютерної інженерії в університеті, і в минулому семестрі я та група зробили великий програмний проект, в якому нашою основною мовою була Scala. Ми створили наш дизайн традиційним способом: використання випадків, доменної моделі, послідовних діаграм, діаграм класів UML; звичайне навантаження. І, як ви вже знаєте, вони погано підходять. Ось кілька причин.

Спочатку розглянемо діаграми послідовності. Відчуття діаграми послідовності має кілька стабільних, довговічні, напівавтономні актори розмовляють один з одним. У Scala об’єкти, як правило, створюються за короткий час. Крім того, класи випадків заохочують "німі" об'єкти, які містять лише дані та без коду, тому концепція передачі повідомлень не відповідає місцем. Але найважче завдання діаграм послідовностей полягає в тому, що вони не мають хорошого способу включення функцій першого класу; в OO-дизайні, використовуючи лише зворотний виклик тут або там, він може працювати так, але якщо це ваш основний засіб передачі управління, ви знайдете діаграму послідовностей, яка відображає мало вашого фактичного коду.

Діаграми класів UML більше піддаються Scala; так, міксини не проходять добре, але це щось, що можна «підробити», а не капітально ремонтувати. Але я думаю, що це, можливо, не вистачає суті.

Розглянемо ще раз, чому у Скали є «німі об’єкти». Якщо ви пишете клас справи без методів:

case class NewInvite(user: User, league: League)

вам не потрібні жодні методи, оскільки типи членів обіцяють щось, що ви можете зробити з ними. Насправді вони обіцяють все . І, у будь-якій області програми, змінні в межах сфери та їх типи обіцяють щось про те, куди може піти код у цій точці.

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


"Що обіцяно програмісту" - це прекрасне дзвінок до нього ;-)
Джек

12

UML вийшов із "Війни бульбашок" кінця 80-х - початку 90-х. Це завжди був компроміс для позначення , а не метод дизайну. Багато калунів, приписуваних UML, таємно є результатом методу проектування, який говорить "Крок 1: намалюй модель класу".

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

Обов’язки

CRC працює так само добре, як і для Scala, як і для будь-якої іншої мови ОО, що означає, що вона працює дуже добре! Моделювання розподілу обов'язків антропоморфізуючими суб'єктами та розуміння їх співпраці все ще працює добре.

У великому масштабі вам все одно слід проектувати об’єкти. Межі об'єкта - це межі інкапсуляції. Зберігайте деталі стану та змін, що змінюються, у межах цих об'єктів. Класи кейсів створюють гарні об'єкти інтерфейсу, так само як і ванільні колекції та структури даних.

Розуміння реалізацій

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

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

Представлення та позначення

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

Я вважаю, що UML все ще може бути корисним для опису масштабної структури вашої системи. Однак це не допоможе на нижчих рівнях деталізації.

До дрібниць деталей спробуйте грамотні методи програмування.

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


9

Як ми розробляємо чисті рішення Scala?

Я думаю, ви ставите неправильний кут з цим питанням. Ваш загальний дизайн рішень не повинен бути пов'язаний з певною мовою; скоріше, це повинно відповідати існуючій проблемі. Чудова річ у Scala - це те, що вона досить гнучка, щоб не заважати, коли настає час втілити ваш дизайн. Java, з іншого боку, забезпечує дотримання деяких проектних рішень ( в першу чергу, все-це-клас менталітет) , який буде зробити його відчувати себе трохи незграбним.

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

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

Підсумовуючи, головна моя думка полягає в тому, що ви не повинні намагатися "розробляти чисті рішення Scala". Ви повинні розробляти рішення максимально природним і зрозумілим способом, і просто так трапляється, що Scala - це неймовірно гнучкий інструмент, який допоможе вам реалізувати ці рішення різними способами. Коли все, що ви знаєте, про молоток, кожна проблема починає виглядати як цвях, тому не забудьте вивчити різні доступні вам підходи. Не намагайтеся закрити процес проектування на кроки X, Y і Z, щоб не пропустити можливості від A до W.


4

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

Я можу лише перелічити декілька речей: семантичний підхід до дизайну, підмножина якого називається мовно-орієнтоване програмування (і широко використовується спільнотою Scala) та дизайн, керований доменом. Останнє є надто спрощеним та опромінованим поглядом на більш загальний семантичний підхід до дизайну, ви зможете насолоджуватися ним лише якщо вам подобається OOD.

Я також рекомендую дізнатися хитрість-два від інших функціональних програмних спільнот - наприклад, Haskell і Scheme, не всі їхні дизайнерські ноу-хау вже передані Scala.


3

Питання було таким: як ми розробляємо чисті рішення Scala?

Відповіді на кшталт,

  1. Ми використовуємо XYZ, тому що ....
  2. Ми використовуємо ABC, але так ...
  3. Ми почали використовувати XYZ, але потім визнали ABC кращою, тому що ...

вказував би на деяку зрілість або загальноприйняте існування такого набору інструментів чи рішення.

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

Чи безпечно сказати тоді, що ми знаємо відповідь на запасне питання:

"... настав час вигадати його?"

Може так ? (Якщо " так " не призначає засоби для рішення. Це може бути досягнуто або розширенням існуючого рішення, або створенням його з нуля. Це все ж визнає, що в існуючих варіантах дизайну є суттєві недоліки)

Ви можете проголосувати мою відповідь, якщо ви не згодні. Я прийму це як чоловік.


Революційний? Чому? Лісп десятиліттями був ідеальним поєднанням імперативних та функціональних підходів. Scala цікава тим, що має тип типу.
SK-логіка

3
Вибачте, якщо «революціонер» є сильним сильним словом. Я не маю на увазі, що Скала знову винайшов колесо - але впевнений, що пекло перетворило гуму. Насправді, Scala - це чудова суміш усіх благ багатьох мов програмування. Я впевнений, що він також сильно позичає у Lisp. На мене, і я не претендую на інших, мова Scala вважає себе революційною, тому що я думаю про код таким чином, що природним чином виходить. Мова дизайну / моделювання, що робить те саме, напевно буде безкоштовним. Це був мій єдиний пункт - не ображатись на Лісп та всі інші чудові мови.
Джек

2

Я б не розрізняв мови, я би робив це так само в Java. Однак я зі школи OO, а не функціонал (алгебраїчний Хаскелл один чи лісп). Дизайн додатків Haskell насправді відрізняється від Scala або Clojure. Також дизайн додатків Scala.js відрізняється завдяки потужним DOM - важко боротися з важливою частиною вашого додатка, яка є обов'язковою. З часом я звик це робити OO, намагаючись максимально усунути стан і незмінність. Якби я був шанувальником типу або скалазу, мої програми, мабуть, виглядали б зовсім інакше.

  1. Вибираючи технологічний стек та основні дизайнерські рішення, такі як відповідальність клієнт / сервер, розподілений характер - чи нам справді потрібна стійкість даних? Що найкраще підходить для нашої програми? (звичайно, ще не бібліотеки чи рамки)

  2. Починаючи з лише одного App.scalaфайлу, де я визначаю ключові типи (ознаки та класи випадків) та ролі - багато споглядаю та роздумуючи, будучи АФК. З нею так просто грати, поки він знаходиться в одному файлі. Навіть прототип може виникнути, якщо це досить проста програма. Я присвячую багато часу цій фазі, тому що немає нічого важливішого, ніж мати максимально правильний і переважно прозорий і розумний прототип. Все це допомагає зробити наше подальше споглядання більш точним і це збільшує ймовірність успіху.

  3. Тепер, коли ми довели правильність нашого рішення, маємо чітке бачення і всі потенційні проблеми розкриваються, саме час подумати, які саме сторонні бібліотеки чи методології запровадити. Чи потрібно нам грати в рамки чи лише кілька ліб? Чи ми зробимо цих видатних акторів, чи справді нам потрібна стійкість Акки, .., .. чи, може, ні? Чи використовуємо ми Rx для цих потоків? Що ми використовуємо для користувальницького інтерфейсу, щоб не втратити розуму після того, як він має 10 інтерактивних функцій?

  4. розділіть App.scalaфайл на кілька, тому що нові ознаки та класи з’явилися та стали більшими. Їх інтерфейс повинен розповідати про їх ролі. Тепер також час подумати про використання типів класів та спеціального поліморфізму, де це можливо. Так що хто б не дзвонив на ваш інтерфейс, він повинен бути гнучким для них. Сенс у тому, щоб реалізувати лише загальну функціональність, залишаючи деталі абоненту. Іноді те, що ви намагаєтеся реалізувати, може бути подібним до Тьюрінга, тому ви повинні створити спосіб для абонентів самостійно реалізовувати конкретні дані, а не накопичувати запити на функції на GitHub. Цей матеріал важко додати пізніше. Це потрібно продумати на самому початку. А також проектування DSL.

  5. продумуючи дизайн через те, що додаток буде зростати, і він повинен бути готовим протистояти новим функціям, оптимізаціям, наполегливості, користувальницькому інтерфейсу, видаленню тощо - речі, які ще не були реалізовані, або над ними якось глузували чи абстрагувались. Пам'ятайте, що хоча це все в одному файлі, змінити речі досить легко. Людський мозок починає втрачати його після того, як він поширюється на 10+ файлів. І це як пухлина, яка росте, саме так починається перенапруження. Я помітив, що мої програми закінчуються кількома довгими частковими функціями, які утворюють якусь основу цього. Мені легко працювати. Напевно, я заразився акторами Акки.

  6. Тепер, коли існують перші фактичні функції, а не лише основні функції, прийшов час почати писати тести. Чому так пізно? Тому що за цей час ви, ймовірно, пройшли основні переписування, і ви витратили час на тестування. Не слід писати тести, якщо ви справді не впевнені, що не буде великих перероблень. Я вважаю за краще інтеграційні тести, які легко витримують безперервний рефакторинг, і не витрачаю більше часу на тестування, що насправді розробляє речі. Мені траплялося кілька разів, що я витрачав занадто багато часу на тестування. Виправлення потенційних помилок, безумовно, окупається більше, ніж поганих тестів. Погані тести або тести, написані з першого ж моменту, можуть спричинити біль. Зауважте, що я, звичайно, не шанувальник TDD - ІМХО, це фігня.

  7. Повторне проектування, чітке та чітке оформлення програми, чіткі ролі компонентів. У міру того, як додаток зростає, це не залишається таким способом, якщо ви не геніальний програміст, і якщо ви читаєте цю відповідь, ви, мабуть, не так :-) Ні я. Як правило, я не існую, оскільки ви не знали, як цього уникнути що - це може спричинити проблеми, спробуйте їх усунути. Також не забудьте усунути запахи коду, які вас турбували протягом певного часу. У цей момент ваш керівник проекту (якщо він у вас є), ймовірно, починає трясти головою. Скажи йому, що це окупиться.

  8. Готово, чи добре розроблено додаток? Це залежить: чи мають у складі чітко визначені ролі, які насправді мають сенс навіть поза вашою головою? Не могли б ви взяти деякі з них та відкрити їх без особливих зусиль? Чи є чітке розділення стурбованості між ними? Чи можете ви додати нову функцію, не переживаючи, що вона вріжеться в іншому місці (знак, який ви точно знаєте, що ви робите)? Взагалі кажучи, його слід читати як книгу з прямолінійним прагматичним вирішенням проблем. Що є IMHO - головна ознака гарного дизайну.

Загалом, це лише важка робота і час, який ви, мабуть, не отримаєте від своїх менеджерів. Все, що я описав тут, займає багато часу, сил, кави, чаю і особливо мислення AFK. Чим більше ви дасте, тим краще дизайн. Немає загального рецепту проектування додатків, здорового глузду, прагматизму, KISS, працьовитості, досвіду означає хороший дизайн.

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