Функциональное программирование + управление доменом - программирование
Подтвердить что ты не робот

Функциональное программирование + управление доменом

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

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

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
   }
}

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

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

Что делать, если с другой стороны разные задания не могут вызывать методы непосредственно на BankAccount или BankAccountRef, а просто уведомлять их о необходимости выполнения некоторых операций? Ну, тогда у вас есть Актер, любимый способ сделать concurrency в 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)

        }

    }
}

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

Ответ 2

Это своего рода вопрос мнения, который меньше scala конкретный, чем вы думаете.

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

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

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

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

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