Вы можете эффективно использовать неизменяемые объекты в 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. Идея заключается в том, что вы общаетесь с Актером, отправляя сообщения в свой почтовый ящик, и эти сообщения обрабатываются в порядке получения. Таким образом, при использовании этой модели у вас никогда не будет недостатков параллелизма.
Вы должны изучить источник событий imo, это хороший способ достичь подхода, аналогичного DDD с OOP. –
Взгляните на https://www.manning.com/books/functional-and-reactive-domain-modeling, где описано, как объединить DDD и FP. У Debasish также есть несколько хороших презентаций, например, проверьте: https://www.youtube.com/watch?v=U0Rk9Knq8Vk – Cal