14

Функциональное программирование способствует непреложным классам и ссылочной прозрачности.Функциональное программирование + управление доменом

Проект, основанный на домене, состоит из объекта значения (неизменяемого) и сущностей (изменчивых).

Должны ли мы создавать неизменяемые объекты вместо изменчивых?

Предположим, проект использует Scala в качестве основного языка, как мы можем писать объекты как классы case (неизменяемый), не рискуя устаревшим статусом, если мы имеем дело с параллелизмом?

Что такое хорошая практика? Сохранение объектов изменчиво (var полей и т. Д.) И избегание синтаксиса классов case?

+1

Вы должны изучить источник событий imo, это хороший способ достичь подхода, аналогичного DDD с OOP. –

+1

Взгляните на https://www.manning.com/books/functional-and-reactive-domain-modeling, где описано, как объединить DDD и FP. У Debasish также есть несколько хороших презентаций, например, проверьте: https://www.youtube.com/watch?v=U0Rk9Knq8Vk – Cal

ответ

12

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

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

У вас есть два действительных подход:

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

  • вы делаете весь класс неизменяемым, и вы окружаете его «менеджером», который может изменить учетную запись.

Поскольку первый довольно простой, я подробно расскажу о первом.

case class BankAccount(val balance:Double, val code:Int) 

class BankAccountRef(private var bankAccount:BankAccount){ 
    def withdraw(withdrawal) = { 
     bankAccount = bankAccount.copy(balance = bankAccount.balance - withdrawal) 
     bankAccount.balance 
    } 
} 

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

Оптимальный способ делать параллелизм: передача сообщений

Что делать, если с другой стороны, различные рабочие места не могут вызывать методы непосредственно на BankAccount или BankAccountRef, а просто предупредить их, что некоторые операции должны быть выполнено? Ну, тогда у вас есть Актер, любимый способ сделать параллелизм в Scala.

class BankAccountActor(private var bankAccount:BankAccount) extends Actor { 

    def receive { 
     case BalanceRequest => sender ! Balance(bankAccount.balance) 
     case Withdraw(amount) => { 
      this.bankAccount = bankAccount.copy(balance = bankAccount.balance - amount) 
     } 
     case Deposit(amount) => { 
      this.bankAccount = bankAccount.copy(balance = bankAccount.balance + amount) 

     } 

    } 
} 

Этот раствор подробно описан в Акко документации: http://doc.akka.io/docs/akka/2.1.0/scala/actors.html. Идея заключается в том, что вы общаетесь с Актером, отправляя сообщения в свой почтовый ящик, и эти сообщения обрабатываются в порядке получения. Таким образом, при использовании этой модели у вас никогда не будет недостатков параллелизма.

+0

Но разве нет риска иметь набор преобразований, которые в настоящее время хранятся в памяти? Представим себе, что разные потоки для нескольких заданий задания заданий поддерживают разные версии одного и того же объекта. Это приведет к несогласованности, не так ли? – Mik378

+0

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

+2

Use-case? Банковские счета. Фоновые задания могут представлять отчеты, ATM-обработчики, калькуляторы процентных ставок ... –

10

Это своего рода вопрос, который менее определен, чем вы думаете.

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

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

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

Две действительно приятные вещи, которые мне нравятся в неизменяемых объектах, помимо часто упоминаемого параллелизма, - это то, что они гораздо надежнее кэшировать, и они также отлично подходят для передачи распределенных сообщений (например, protobuf over amqp), поскольку намерение очень ясно.

Кроме того, в FP людей бороться изменяемые к неизменному моста, создавая «язык» или «диалог» ака DSL (Builders монады, Трубы, Стрелки, STM и т.д ...), который позволяет мутировать, а затем преобразовать обратно в неизменяемый домен. Услуги, упомянутые выше, используют DSL для внесения изменений. Это более естественно, чем вы думаете (например, SQL является примером «диалога»). ООП, с другой стороны, предпочитает иметь изменяемый домен и использовать существующую процессуальную часть языка.

+0

Так я и сделаю это. Сервисы для моделирования поведения домена и неизменяемых объектов данных (классы case как ADT). Держите службы как можно более чистыми (неизбежно есть IO, но вы можете осторожно подтолкнуть его к краю). Многие общие шаблоны в DDD существуют для решения проблем управления изменчивым состоянием в императивной системе OO со слоями. Мне кажется, что подход FP избавит от необходимости многого. –

+0

@Adam Gent Отличный ответ :) – Mik378

+0

@BrianSmith Да. Я думаю, что DDD, а также некоторые другие модели Fowler/Evans переоценены (этим парням нужно провести некоторое время в лагере FP). Современное бэкэнд-приложение лучше подходит для FP. [Я даже программист на Java и предпочитаю его] (https://github.com/agentgt/jirm). Также определение DDD является ошибочным. И, похоже, DDD презирает трансформации aka DTO, но, по моему опыту, вам всегда нужны объекты передачи (т. Е. Разные взгляды). FP охватывает преобразования. –

Смежные вопросы