想要重构我刚刚遇到的这个面向对象的代码。该代码的作者说,MySave类仅作为keys
和inserts
地图的占位符创建。
MySave类的客户端始终为每个请求创建新的MySave 实例,并使用add()方法填充键并插入地图。
然后另一个函数将使用MySave()的keys
和inserts
val从相应的Maps中检索这些值,然后将它们发送到持久层等。
即使在这个OO设计中已经存在缺陷(例如,尽管当前的使用是为每个新请求创建MySave对象,但是以下设计不会阻止仅创建单个类实例并且阻止共享状态的线程安全问题,以及它通过公共vals
等泄漏共享状态。而不是修复OO设计我想将其转换为功能代码。
在转换为FP范例时,您的方法是什么?我刚刚开始学习Scalaz state monad,但这听起来像是这个OO代码的一个很好的FP替代品,还是比这更微不足道的东西可以工作?
class MySave {
val keys = mutable.Map[Entity Int]()
val inserts = mutable.Map[Entity, String]()
def add(d: Entity, value: String): \/[Throwable,Unit] = {
//this is a side effecting method which either updates `keys` or `inserts` map
// by simply reassigning `keys` or `inserts` vars e.g. keys += d ->value
}
}
答案 0 :(得分:2)
此代码目前有点难以理解,让我看看是否可以梳理相关位。
免责声明:我不是Scala程序员,虽然我已经使用了一点。如果我的语法关闭,请原谅。
您说的是当前请求的方式如下:
val save = MySave()
save.add(d, x)
save.add(d2, y)
...
SendToPersistentLayerEtCetera(save)
此使用模式表明(正确使用)MySave
与一系列Dictionary, String
对同构。所以您的界面可能如下所示:
class MySave {
val keys = Map[Entity, Int] # immutable maps
val inserts = Map[Entity, Keys]
}
def save(inputs : List[Pair[Entity, String]]) : MySave
您可以像以前一样将MySave
传递给SendToPersistentLayerEtCetera
,但现在MySave
不能被误用。 save
是在功能上还是在命令上实现与设计无关。如果你想在功能上实现save
,它看起来像是一个向左折叠(折叠将Pair[Entity,String]
插入MySave
的函数,返回新的MySave
)。
如果您希望有很多save
次呼叫,以致创建List
的费用过高,我会建议使用一个懒惰的流,它可以左右折叠存储器中。
答案 1 :(得分:1)
将此代码移动到更实用的功能,而不是
val save = new MySave()
save.add(d, x)
save.add(d2, y)
您可以执行以下操作
case class MySave(keys: Map[Dictionary, Int], inserts: Map[Dictionary, String])
def add(mySave: MySave, d: Dictionary, value: String): MySave = {
val save1 = add(MySave(), d, x)
val save2 = add(save1, d2, y)
这是好的和不可改变的,但是你必须绕过州,你真正想要的是状态monad。
case class MySave(keys: Map[Dictionary, Int], inserts: Map[Dictionary, String])
def add(d: Dictionary, value: String): State[MySave, Unit] = ...
这可以让您执行以下操作...
val prog = for {
_ <- add(d, x)
_ <- add(d2, y)
} yield ()
prog.exec(MySave())
现在你不再需要传递MySave实例了。
有关实施状态的帮助,或者找出我的建议,我建议先试用,然后在SO上发布问题,或在freenode上尝试#scalaz。
答案 2 :(得分:0)
我仍然不了解您的代码的一些细节,但我认为可以给出一些明确的指示。你可以做的第一件事就是让整个MySave
类不变为这样:
case class MySave( val keys: Map[Dictionary Int], inserts: Map[Dictionary, String])
你可以做的第二件事是修改add
函数,使它返回MySave
对象的新状态(也许你已经注意到这也可以用状态monad完成但是那个方法对我来说不是那么清楚)。新功能可能是这样的:
def add(dd: Dictionary, value: String): \/[Throwable,MySave] = {
nmField match {
case Some(id) =>
val validId = checkDictType("numeric", value)
for {
k <- validId
} yield { MySave(keys + (dd -> k) , inserts) }
case None =>
for {
r <- validateDictTypeAndValue(dd, value)
} yield { MySave( keys, inserts + (dd -> value) ) }
}
}
我猜checkDictType
和validateDictTypeAndValue
返回Either
值,对吗?
我仍然不确定是否有某种方法可以使它更具功能性#34;例如使用你建议的State monad。在这种情况下,您可能希望使用一些Monad Transformer来实现&#34;互操作&#34;与Either
。