【问题标题】:Enrich case class via implicit extension通过隐式扩展丰富案例类
【发布时间】:2016-01-08 13:54:35
【问题描述】:

Person模型case class通过REST端点暴露给客户端。

scala> case class Person(firstname: String, lastname: String)
defined class Person

现在内部服务之一想要“丰富”这个Person case class 并向其中添加社会安全号码。但是,丰富只是从一项服务中填充社会安全号码并将其传递给另一项服务。

立即的想法是将ssn 属性添加到Person,如下所示 - 但这是不希望的,因为Person 暴露给端点消费者并且SSN 绝不能对任何调用暴露Person 数据的REST 端点的用户可见.

case class Person(firstname: String, lastname: String, ssn: String)

所以另一个想法是丰富Person案例类如下

scala> implicit class ExtendedPerson(person: Person){
 | val SSN : String = ""
 | }
defined class ExtendedPerson

那么Service1.findPerson API 将在范围内有implicit 并且将要准备Person 如下

val person = Person(firstName="Joe",lastname="Doe", ssn ="123-456-0000")

然后

Service2.moreWorkWithPerson(person,other arguments)

然后Service2.moreWorkWithPerson 将希望从person 参数中获取ssn=123-456-0000

在不修改 Person case class 的定义的情况下,在 Scala 中实现此目的的正确方法是什么?无形有帮助吗?任何指针将不胜感激?

【问题讨论】:

    标签: scala shapeless


    【解决方案1】:

    SSN 作为 ExtendedPerson 中的 val 没有多大意义,相反,它应该是一个使用人员名字和姓氏查询数据库的函数。鉴于您的数据库很慢并且您可能多次调用 Person.SSN,我建议不要使用此模式,因为它会在您每次调用 .SSN 时查询您的数据库。相反,我会为 Person 编写一个隐式转换器,以便更明确地进行转换。

    case class ExtendedPerson(basicInfo: Person,ssn: String)
    
    implicit class PersonHelper(person: Person) extends AnyVal{
     def extended = {
       val ssn = DB.querySSN(...)
       ExtendedPerson(person,ssn)
     }
    }
    
    
    val person: ExtendedPerson = Person("John","Doe").extended
    
    //person = ExtendedPerson(Person("John","Doe"),"123-456-0000"")
    

    注意:代码写得乱七八糟,但逻辑上应该是正确的

    【讨论】:

    • 是否有任何理由使用implicit class 而不是implicit def?另外,为什么不extends AnyVal
    • 我的回答会与此类似,所以我 +1。我建议同时使用 def withSSN(ssn: String)def withSSN() - 第一个让开发人员明确传递 SSN,第二个从数据库加载,如 shim_ 的答案
    【解决方案2】:

    看来你想多了。这有什么问题?

    case class Person(firstname: String, lastname: String, ssn: Option[String] = None);
    

    现在,在 REST 处理程序中执行 return Person("John", "Doe"),在其他服务中执行:return Person("John", "Doe", Some("111-11-1111"))

    【讨论】:

    • 由于这些原因,这是我试图避免的第一选择。为 SSN 等敏感数据添加默认无的选项 - 让开发人员有责任挑选和选择要向客户公开的成员 - 造成暴露 SSN 的风险。 `return Person("John", "Doe") 等也会很繁琐——因为现实生活中的案例类有很多参数,所以在返回时选择参数会很麻烦。
    • @user2066049 我不明白知道如何实例化正确的类(或导入正确的隐式)比知道调用正确的构造函数实例更少“开发人员的责任”。无论哪种方式,人们仍然需要采取措施获得实际的 SSN 值,这不太可能偶然发生。至于第二点,return Person("John", "Doe") 我的意思是您的 REST 控制器将继续执行与现在相同的操作。它是否繁琐与手头的问题正交。
    【解决方案3】:

    也许您应该只向客户端公开一个不同的类。

    case class Person(firstname: String, lastname: String, ssn: String) {
      def toClient = ClientPerson(firstname, lastname)
    }
    
    case class ClientPerson(firstname: String, lastname: String)
    
    object RESTService {
      def getPerson(id: String): ClientPerson = {
        val p: Person = DB.getPerson(id)
        p.toClient
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2021-07-31
      • 2020-03-24
      • 2017-04-04
      • 2010-12-14
      • 1970-01-01
      • 1970-01-01
      • 2018-08-01
      • 2017-09-06
      • 2019-03-04
      相关资源
      最近更新 更多