我有一个不可变的State
,我处理一个消息队列,其中每条消息都是该州某些字段的新值列表。
新值可能适用于某个字段的一部分 - 例如,设置或清除多个字段中的一个标志(位),或仅更改16位字段的低8位或高8位部分。
在处理完消息之后,我希望获得状态的不可变副本,并应用修改。
object StateField {
sealed abstract class StateField()
sealed abstract class Register extends StateField
sealed abstract class Flag extends StateField
case object AX extends Register
case object AH extends Register
case object AL extends Register
case object CF extends Flag
case object OF extends Flag
}
class StateFieldModification(field: StateField, value: Int)
class ModificationMessage(content: List[StateFieldModification])
case class State(AX: Int, Flags: Int) {
def readRegister(field: StateField.Register): Int = field match {
case StateField.AX => this.AX
case StateField.AH => this.AX & 0xFF
case StateField.AL => (this.AX << 8) & 0xFF
}
def readFlag(field: StateField.Flag): Boolean = field match {
case StateField.CF => (this.Flags & 0x0001) != 0
case StateField.OF => (this.Flags & 0x0800) != 0
}
def flagsWithBit(flagBit: Int, newBitValue: Boolean): Int = {
if (newBitValue) Flags | (1 << flagBit) else Flags & ~(1 << flagBit)
}
def withModification(modification: StateFieldModification): State = modification.field match {
case StateField.AX => this.copy(AX = modification.value)
case StateField.AH => this.copy(AX = (this.AX & 0x00FF) | (modification.value << 8))
case StateField.AL => this.copy(AX = (this.AX & 0xFF00) | modification.value)
case StateField.CF => this.copy(Flags = flagsWithBit(1, modification.value > 0))
case StateField.CF => this.copy(Flags = flagsWithBit(12, modification.value > 0))
}
def withModifications(message: ModificationMessage) = ???
}
Q#1 - 基于&#34;字段键&#34;制作字段getter的最佳方式是什么?
Q#2 - 根据&#34;字段键制作字段设置器的最佳方法是什么?#/ p>
Q#3 - 在给出可变性消息的情况下,制作新对象的最佳方法是什么?
请注意:
所有帮助表示赞赏。感谢!!!
答案 0 :(得分:2)
你似乎做错了。这将是一种采用像perl或ruby这样的动态语言的方法,但scala并不是那样的。你可以用scala模仿这样的东西,但是它很难让你不想这样做。
.getDetail
不是您经常在scala中看到的内容。如果您想获得某个人的姓名,通常只需要person.name
,person.getDetail(PersonDetail.Name)
。为什么?好吧,为什么不呢?当你能做到前者时,没有理由去做后者。
同样对于setter:person.copy(firstName =&#34; foo&#34;)的工作方式优于person.withModiciation(PersonDetail.Name, "foo")
第三种情况可能是最复杂的。如果你想应用一大堆修改怎么办? 嗯,我仍然认为,像
这样的东西val list = List(
PersonDetail.FirstName -> "foo",
PersonDetail.LastName -> "bar",
PersonDetail.OtherStuff -> "baz"
)
person.withModifications(list)
并不比scala的习惯
更好person.copy(firstName = "foo", lastName = "bar", otherStuff = "baz")
答案 1 :(得分:2)
您是否考虑过使用镜头库such as Monacle?镜头是功能抽象,允许您将函数应用于数据结构的一部分,例如Person案例类。 A Lens允许您放大部分结构,因此对于Person,可以
import monocle.Lens
val firstName = Lens[Person, String]( p => f => p.copy(firstName = f))
val newPerson = firstName.set("someNewName")(person)
这样,PersonDetail中的每个修改都可以对应一个合适的镜头。镜头也支持其他操作。对于更复杂的修改,镜头可以按照Monacle README中的说明进行组合。除了调用set函数外,它们还可以根据当前值修改数据,以对应FirstNameFirstLetter案例。
firstName.headOption.modify(_.toUpper)(person)
答案 2 :(得分:2)
镜片正式化了具有吸气和非变异的设定者的概念。数据结构的功能,但我们不需要形式主义来获得一些优势。我们将使用控制反转来为您的类创建修改器函数。这些修饰函数将编码如下的想法:
&#39;给我当前状态和新AX,我会给你一个新状态&#39;
&#39;给我当前状态和从当前AX计算新AX的功能,我会给你一个新状态&#39;
&#39;给我当前的状态和新的旗帜,我会给你一个新的状态&#39;
所以,代码:
case class State(ax: Int, flags: Int) {
private def setAx(newAx: Int): State = copy(ax = newAx)
private def modAx(f: Int => Int): State = setAx(f(ax))
private def setFlags(newFlags: Int): State = copy(flags = newFlags)
def withModification(modification: StateFieldModification): State =
modification.field match {
case StateField.AX => setAx(modification.value)
case StateField.AH =>
modAx { ax => (ax & 0x00FF) | (modification.value << 8) }
case StateField.AL =>
modAx { ax => (ax & 0xFF00) | modification.value }
case StateField.CF =>
setFlags(flagsWithBit(1, modification.value > 0))
case StateField.CF =>
setFlags(flagsWithBit(12, modification.value > 0))
}
def withModifications(message: ModificationMessage): State =
message.content.foldLeft(this) { (state, modification) =>
state withModification modification
}
}
P.S。,您可以简化一些类型,例如: StateField
并非真正需要成为多级层次结构 - 您可以将其拆分为单独的Register
和Flag
枚举;您可以打开或输入别名ModificationMessage
,因为它只是一个列表。