Monocle是一个很好的库(并不是唯一的一个),它实现了镜头模式。如果我们需要在庞大的嵌套对象中更改一个字段,那么这非常有用。就像这个例子http://julien-truffaut.github.io/Monocle/所示。
case class Street(number: Int, name: String)
case class Address(city: String, street: Street)
case class Company(name: String, address: Address)
case class Employee(name: String, company: Company)
以下是样板代码
employee.copy(
company = employee.company.copy(
address = employee.company.address.copy(
street = employee.company.address.street.copy(
name = employee.company.address.street.name.capitalize // luckily capitalize exists
)
)
)
)
可以轻松地替换为
import monocle.macros.syntax.lens._
employee
.lens(_.company.address.street.name)
.composeOptional(headOption)
.modify(_.toUpper)
这很不错。据我所知,宏魔法会将所有内容转换为与上面完全相同的代码。
但是,如果我想要组合多个操作呢?如果我想要同时使用一次调用更改街道名称、地址城市和公司名称怎么办?就像下面这样:
employee.copy(
company = employee.company.copy(
address = employee.company.address.copy(
street = employee.company.address.street.copy(
name = employee.company.address.street.name.capitalize // luckily capitalize exists
),
city = employee.company.address.city.capitalize
),
name = employee.company.name.capitalize
)
)
如果我在这里仅仅重用镜头,我将有以下代码:
employee
.lens(_.company.address.street.name).composeOptional(headOption).modify(_.toUpper)
.lens(_.company.address.city).composeOptional(headOption).modify(_.toUpper)
.lens(_.company.name).composeOptional(headOption).modify(_.toUpper)
这将最终被翻译为三个
employee.copy(...).copy(...).copy(...)
调用,而不是只有一个employee.copy(...)
。如何改进呢?此外,应用一系列操作将非常有益。像一对序列
Seq[(Lens[Employee, String], String => String)]
,其中第一个元素是指向正确字段的透镜,第二个元素是修改它的函数。这将有助于从外部构建此类操作序列。对于上述示例:val operations = Seq(
GenLens[Employee](_.company.address.street.name) -> {s: String => s.capitalize},
GenLens[Employee](_.company.address.city) -> {s: String => s.capitalize},
GenLens[Employee](_.company.name) -> {s: String => s.capitalize}
)
或类似的东西...