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

35

Много, много лет назад я получил степень магистра в области объектно-ориентированной разработки программного обеспечения. Я охватил все: инициацию проекта, требования, анализ, дизайн, архитектуру, разработку и т. Д. И т. Д. Моя любимая книга по ИТ всех времен была «Разработка объектно-ориентированного программного обеспечения, основанный на опыте подход» (IBM-1996). Книга, созданная группой настоящих экспертов своего времени. В нем описывается подход, ориентированный на рабочий продукт, к объектно-ориентированному анализу, методам проектирования и разработки.

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

Дизайн и разработка потеряли веселье, и я собирался оставить позади всю ИТ-индустрию.

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

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

Как мы разрабатываем чистые решения Scala?

Конечно, обычные диаграммы последовательности, диаграммы взаимодействия, диаграммы объектов и т. Д. Могут быть заменены или улучшены для лучшего смешения императивных и функциональных объектно-ориентированных систем. Конечно, есть возможность для чего-то совершенно другого!

Я не ищу раздутой сложности - я ищу гибкую простоту. Так же, как Scala на самом деле прост и легок (хотя и отличается), но может быть расширен, чтобы соответствовать вашим требованиям, как пара штанов для йоги. Для этого прекрасного языка должна существовать система проектирования, которая будет простой и может быть расширена в соответствии с вашими требованиями и вашим доменом. Конечно, UML не может быть этим!

Так, как мы проектируем чистые системы Scala? Представьте себе на мгновение, что вы можете позволить себе создать полноценную систему с нуля, и знаете, что вы будете использовать только Scala - как будут выглядеть ваши модели? Какие типы диаграмм вы бы описали структуру и поведение? Как бы вы моделировали параметры, совпадения, миксины, одиночные объекты и т. Д., Не вдаваясь в сложность расширения существующих методов моделирования, а не в новый, легкий, инновационный набор инструментов.

Существует ли такой дизайн / процесс / решение или пришло время его изобрести?

Джек
источник
+1 за «может быть расширен в соответствии с вашими требованиями, как пара штанов для йоги».
Рассел
FWIW, я видел вопрос об UML и Scala, который может вас заинтересовать. Кроме того, если вы перейдете на сторону функций, то стоит рассмотреть теорию теории категорий. Грегори Мередит обычно ссылается на некоторые интересные презентации об этом, хотя я не уверен, что у него есть в его блоге - это все равно стоит проверить.
Даниэль С. Собрал,
Это многого стоит ;-) Это дает мне направление копать. Спасибо Даниэль
Джек
Стоит проверить, «модифицированный» UML для Scala, github.com/mnn/Dia2Scala/blob/master/other/Notation.md
Вейчинг Лин,

Ответы:

12

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

Я учусь на компьютерного инженера в университете, и в прошлом семестре я и группа работали над большим программным проектом, в котором основным языком был Scala. Мы сделали наш дизайн традиционным способом: варианты использования, модель предметной области, диаграммы последовательности, диаграммы классов UML; обычный груз. И, как вы уже знаете, они плохо подходят. Вот несколько причин.

Сначала рассмотрим диаграммы последовательности. Ощущение диаграммы последовательности имеет несколько стабильных, долговечны, полуавтономные актеры разговаривают друг с другом. В Scala объекты, как правило, создаются в течение короткого времени. Кроме того, классы case поощряют «тупые» объекты, содержащие только данные и не кодируют, поэтому концепция передачи сообщений неуместна. Но самая сложная задача для диаграмм последовательности заключается в том, что у них нет хорошего способа включения функций первого класса; в ОО-дизайне, использующем только обратный вызов здесь или там, его можно заставить работать, но если это ваш основной способ передачи управления, вы обнаружите, что диаграмма последовательности мало отражает ваш реальный код.

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

Вспомните еще раз, почему в Scala есть «тупые объекты». Если вы пишете класс case без методов:

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

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

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

Оуэн
источник
«То, что обещано программисту» - это звучит очень мило ;-)
Jack
12

UML появился из «Пузырьковых войн» конца 80-х и начала 90-х годов. Это всегда был компромисс для обозначения , а не метод проектирования. Многие из кланов, приписываемых UML, являются тайным результатом метода проектирования, который говорит: «Шаг 1: нарисуйте модель класса».

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

обязанности

CRC работает так же хорошо для Scala, как и для любого другого языка OO, то есть он работает очень хорошо! Моделирование распределения обязанностей антропоморфизирующими субъектами и понимание их сотрудничества все еще хорошо работает.

В больших масштабах вы все равно должны проектировать с объектами. Границы объекта являются границами инкапсуляции. Храните изменяемое состояние и детали реализации внутри этих границ объекта. Классы Case создают хорошие объекты интерфейса, как и ванильные коллекции и структуры данных.

Понимание реализаций

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

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

Представление и нотация

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

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

При мелкой детализации попробуйте грамотные методы программирования.

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


источник
9

Как мы разрабатываем чистые решения Scala?

Я думаю, что вы ошиблись с этим вопросом. Ваш общий дизайн для решений не должен быть привязан к определенному языку; скорее это должно соответствовать имеющейся проблеме. Отличительной особенностью Scala является то, что он достаточно гибок, чтобы не мешать вам, когда придет время для реализации вашего дизайна. Java, с другой стороны, навязывает некоторые дизайнерские решения (в первую очередь, менталитет «все в классе»), которые заставляют его чувствовать себя немного неуклюже.

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

Еще одна причина, по которой Scala отлично справляется со своими задачами, - это возможность создавать DSL, которые соответствуют вашим потребностям . Вы можете сделать свой собственный маленький язык, который решает определенную проблему «нормальным» способом, а затем просто внедрить его в Scala.

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

Дэн Бертон
источник
4

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

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

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

SK-логика
источник
3

Вопрос заключался в следующем: как мы проектируем чистые решения Scala?

Ответы, как,

  1. Мы используем XYZ, потому что ....
  2. Мы используем ABC, но так ....
  3. Мы начали использовать XYZ, но потом обнаружили, что ABC лучше, потому что ...

указали бы на некоторую зрелость или общепризнанное существование такого набора инструментов или решения.

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

Можно ли тогда сказать, что мы знаем ответ на запасной вопрос:

"... пора ли изобретать один?"

Может ли это быть да ? (Там, где « да » не предписывает средства для решения. Это может быть достигнуто либо путем расширения существующего решения, либо путем его создания с нуля. Тем не менее, оно допускает существенные недостатки в существующих вариантах проектирования)

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

Джек
источник
Революционный? Зачем? Лисп был идеальным сочетанием императивного и функционального подходов на протяжении десятилетий. Scala интересна своей системой типов.
SK-logic
3
Извиняюсь, если слово «революционный» - сильное и сильное слово. Я не намекаю на то, что Скала заново изобрел колесо - но он точно переделал резину. На самом деле, Scala - прекрасное сочетание всех достоинств многих языков программирования. Я уверен, что это в значительной степени заимствует и у Лисп. Для меня, и я не претендую на других, язык Scala кажется революционным в том смысле, что я думаю о коде так, что это выглядит чертовски естественно. Язык дизайна / моделирования, который делает то же самое, несомненно, будет бесплатным. Это было мое единственное замечание - не обижайся на Лисп и все другие великие языки.
Джек,
2

Я бы не делал различий между языками, я делал бы то же самое в Java. Однако я из ОО-школы, а не из функционала (алгебраический Хаскелл или Лиспа). Дизайн приложения на Haskell действительно отличается от Scala или Clojure. Кроме того, дизайн приложения Scala.js отличается из-за DOM с состоянием - трудно бороться с частью вашего приложения, которая является обязательной. Со временем я привык делать это ОО, стараясь максимально устранить состояние и изменчивость. Если бы я был фанатом typelevel или scalaz, мои приложения могли бы выглядеть совсем иначе.

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

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

  3. Теперь, когда мы доказали правильность нашего решения, у нас есть четкое видение и выявлены все потенциальные проблемы, пришло время подумать, какие сторонние библиотеки или методологии использовать. Нужна ли нам игровая среда или несколько библиотек? Мы собираемся сделать этих актеров с государственным состоянием, нам действительно нужна устойчивость Акки, .., .. или, возможно, нет? Используем ли мы Rx для этих потоков? Что мы используем для пользовательского интерфейса, чтобы не сойти с ума после того, как у него есть 10 интерактивных функций?

  4. разбить App.scalaфайл на несколько, потому что появились новые черты и классы, которые стали больше. Их интерфейс должен рассказать об их ролях. Теперь пришло время подумать об использовании классов типов и специального полиморфизма, где это возможно. Так что тот, кто вызывает ваш интерфейс, должен быть гибким для них. Дело в том, чтобы реализовать только общие функциональные возможности, оставив детали для звонящего. Иногда то, что вы пытаетесь реализовать, может быть похоже на Turing-complete, поэтому вы должны предоставить возможность вызывающим сторонам самостоятельно реализовывать специфику, а не накапливать запросы функций в GitHub. Этот материал трудно добавить позже. Это нужно продумать в самом начале. А также проектирование DSL.

  5. продумывая дизайн, потому что приложение будет расти, и оно должно быть готово к тому, чтобы противостоять новым функциям, оптимизациям, постоянству, пользовательскому интерфейсу, удаленному взаимодействию и т. д. - вещам, которые еще не были реализованы или каким-то образом подвергались насмешкам или абстрагированию. Помните, что хотя все это в одном файле, все довольно легко изменить. Человеческий мозг начинает терять его после того, как он распространяется по 10+ файлам. И это похоже на опухоль, которая растет, вот так начинается перегрузка. Я заметил, что мои приложения заканчиваются несколькими длинными частичными функциями, которые образуют своего рода его основу. Мне легко работать. Думаю, я заразился актерами Акки.

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

  7. Рефакторинг, создание и поддержание читаемости дизайна приложения, четкие роли компонентов. По мере того как приложение растет, оно не остается таким, если вы не гениальный программист, и если вы читаете этот ответ, вы, вероятно, нет :-) И я тоже. Обычно существуют некоторые состояния, потому что вы не знали, как избежать это может вызвать проблемы, попробуйте их устранить. Также не забудьте устранить запахи кода, которые беспокоили вас в течение некоторого времени. В этот момент ваш менеджер проекта (если он у вас есть), вероятно, начинает качать головой. Скажите ему, что это окупится.

  8. Готово, приложение хорошо разработано? Это зависит от того, есть ли у компонентов четко определенные роли, которые действительно имеют смысл даже вне вашей головы? Не могли бы вы взять некоторые из них и открыть их без особых усилий? Есть ли четкое разделение проблем между ними? Можете ли вы добавить новую функцию, не беспокоясь о том, что она потерпит крах в другом месте (признак того, что вы точно знаете, что делаете)? Вообще говоря, это должно быть читаемым как книга с прямыми прагматическими решениями проблем. Что является ИМХО основным признаком хорошего дизайна.

В общем, это просто тяжелая работа и время, которое вы, вероятно, не получите от своих менеджеров. Все, что я здесь описал, занимает много времени, усилий, кофе, чая и особенно размышлений АФК. Чем больше вы отдаете, тем лучше дизайн. Не существует общего рецепта для разработки приложений, здравый смысл, прагматизм, KISS, тяжелая работа, опыт означает хороший дизайн.

Лысак
источник