2013-07-09 2 views
7

С изменяемым объектом я могу написать что-то вродеScala «обновление» неизменный объект передовой опыт

var user = DAO.getUser(id) 
user.name = "John" 
user.email ="[email protected]" 
// logic on user 

Если пользователь неизменен, то мне нужно клонировать \ скопировать его на каждую операции изменения.

Я знаю несколько способов выполнения этой

  1. случай класса копию
  2. метод (как changeName), который создает новый объект с новым свойством

Что такое лучшая практика?

И еще один вопрос. Существует ли какой-либо существующий метод для получения «изменений» относительно исходного объекта (например, для создания оператора обновления)?

+0

Чтобы получить изменения, вы можете использовать Event Sourcing. –

ответ

8

Оба способа, о которых вы упомянули, относятся к функциональным и OO-парадигмам соответственно. Если вы предпочитаете функциональную декомпозицию с абстрактным типом данных, который в Scala представлен классами case, то выберите метод копирования. Использование мутаторов не является хорошей практикой в ​​моем варианте, потому что это вернет вас к жизни Java/C#/C++.

С другой стороны, делая ADT класс случай как

case class Person(name: String, age: String) 

более consise тогда:

class Person(_name: String, _age: String) { 
    var name = _name 
    var age = _a 

    def changeName(newName: String): Unit = { name = newName } 
    // ... and so on 
} 

(не самый лучший императивный код, может быть короче, но понятно).

Из причины есть еще один способ с мутаторами, просто возвращает новый объект при каждом вызове:

class Person(val name: String, 
      val age: String) {  
    def changeName(newName: String): Unit = new Person(newName, age) 
    // ... and so on 
} 

Но все-таки случай класс путь более consise.

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

Update

Благодаря Сеня, забыл упомянуть две вещи.

Линзы
На самом базовом уровне, линзы являются своим родом добытчиков и сеттеров для неизменных данных и выглядят следующим образом:

case class Lens[A,B](get: A => B, set: (A,B) => A) { 
    def apply(a: A) = get(a) 
    // ... 
} 

То есть. Объектив - это объект, который содержит две функции: get и set. get принимает A и возвращает B. set принимает A и B и возвращает новый A. Легко видеть, что тип B является значением, содержащимся в A. Когда мы передаем экземпляр, чтобы получить, мы возвращаем это значение. Когда мы передаем A и B для установки, мы обновляем значение B в A и возвращаем новый A, отражающий изменение. Для удобства get применяется с псевдонимом.Существует хороший intro для Scalaz объектив случая класса

Запись
Это один, ofcause, приходит из shapeless библиотеки и вызываемого Records. Реализация расширяемых записей, смоделированных как HLists ассоциаций. Ключи кодируются с использованием одноэлементных типов и полностью определяют типы их соответствующих значений (ex from github):

object author extends Field[String] 
object title extends Field[String] 
object price extends Field[Double] 
object inPrint extends Field[Boolean] 

val book = 
    (author -> "Benjamin Pierce") :: 
    (title -> "Types and Programming Languages") :: 
    (price -> 44.11) :: 
    HNil 

// Read price field 
val currentPrice = book.get(price) // Inferred type is Double 
currentPrice == 44.11 

// Update price field, relying on static type of currentPrice 
val updated = book + (price -> (currentPrice+2.0)) 

// Add a new field 
val extended = updated + (inPrint -> true) 
+3

Вы можете добавить третий способ ответа: [линзы] (https://blog.stackmob.com/2012/02/an-introduction-to-lenses-in-scalaz/). Этого недостаточно для нового ответа, но это стоит упомянуть. – senia

+0

@senia благодаря добавленным объективам и бесформенным записям – 4lex1v

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