scala – 结合镜片系列
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) 最终将转换为THREE employee.copy(…).copy(…).copy(…)invocations,而不仅仅是一个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} ) 或类似的东西…… 解决方法
它没有. 这个简单的代码: employee.lens(_.name) .modify(_.capitalize) 变得像怪物那样的东西*: monocle.syntax.ApplyLens(employee,new monocle.PLens[Employee,Employee,String,String] { def get(e: Employee): String = e.name; def set(s: String): Employee => Employee = _.copy(s); def modify(f: String => String): Employee => Employee = e => e.copy(f(e.name)) } }).modify(_.capitalize) 这与简单相去甚远 employee.copy(name = employee.name.capitalize) 并包括三个冗余对象(匿名镜头类,ApplyLens用于语法糖和lambda从修改返回).我们通过直接使用大写而不是使用headOption进行编写来跳过更多. 所以不,没有免费的晚餐.然而,大多数情况下,它足够好,没有人关心额外的镜头对象和中间结果. 多个操作 如果它们的类型对齐,你可以从多个镜头构建一个Traversal(聚合镜头)(这里是Employee to String) val capitalizeAllFields = Traversal.applyN( GenLens[Employee](_.name),GenLens[Employee](_.company.address.street.name),GenLens[Employee](_.company.address.city),GenLens[Employee](_.company.name) ).modify(_.capitalize) 这仍然会多次调用copy.为了提高效率,您可以使用Traversal.apply4等.品种,这将要求你手动编写该副本(我现在懒得做). 最后,如果要将各种转换应用于不同类型的字段,则应该使用修改和设置返回类型为Employee =>类型的函数的事实.雇员.对于你的例子,那将是: val operations = Seq( GenLens[Employee](_.company.address.street.name).modify(_.capitalize),GenLens[Employee](_.company.address.street.number).modify(_ + 42),GenLens[Employee](_.company.name).set("No Company Inc.") ) val modifyAll = Function.chain(operations) // does all above operations of course,with two extra copy calls modifyAll(employee) * – 这是Ammonite-REPL中desugar的简化输出.我跳过了modifyF,顺便说一句 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |