Scala - 域对象建模

ak.*_*ak. 7 dns modeling functional-programming scala object

我目前正试图围绕Scala进行思考,并打算将它用于我的下一个必须处理DICOM的项目.DICOM具有相当广泛的规范,涵盖了数千页的标准.我对DICOM的理解非常有限,但简而言之,DICOM对象 - IOD(信息对象定义)由模块组成,而模块是类型名称 - 值属性对的集合.一些模块和属性的可选性进一步复杂化.例如:

SimpleImageIOD: {
    PatientModule: {
        name: String
        dateOfBirth: DateTime
    }
    StudyModule: {
        name: String
        date: DateTime (optional)
    }
    SeriesModule: {
        name: String
    }
    ImageModule: {
        height: Integer
        width: Integer
        pixelSize: Double (optional)
    }
    EquipmentModule: { (optional)
        type: String
    }
}
Run Code Online (Sandbox Code Playgroud)

有大量的模块,它们可能以各种组合形成不同的IOD.Scala反过来拥有大量的建模能力,包括所有特征,案例类,动态类等.你会如何在Scala中建模这样的域?我对语言很新,但我一直在考虑使用不可变的case类来定义模块,然后将它们聚合在各种IOD中,并使用镜头进行更新:

case class Patient(name: String, dateOfBirth: DateTime)
case class Study(name: String, date: Option[DateTime])
case class Series(name: String)
case class Image(height: Integer, width: Integer, pixelSize: Option[Double])
case class Equipment(type: String)

case class SimpleImageIOD(patient: Patient, study: Study, series: Series, 
                          image: Image, equipment: Option[Equipment])

object Patient {
    val nameL: Lens[Patient, String] = ...
    val dateOfBirthL: Lens[Patient, DateTime] = ...
}

object SimpleImageIOD {
    val patientL: Lens[SimpleImageIOD, Patient] = ...
    val patientNamel = patientL andThen Patient.nameL
}
Run Code Online (Sandbox Code Playgroud)

等等.对于镜头来说,编码所有样板可能会成为一个问题 - M x N x L镜头的顺序将覆盖整个域的MIOD,N模块和L属性.此外,某些领域的选择性至少使得任务变得非常复杂scalaz-seven.

还有哪些可行的方法可以与Scala的精神保持一致?

Rex*_*err 5

你可以嵌入镜片,所以我认为你不会有M x N x L镜片.我认为这是一种非常合理的方法.

另一种方法是定义您自己的单项更新方法,如下所示:

case class Patient(name: String, dateOfBirth: Long) {
  def name(f: String => String): Patient = copy(name = f(name))
  def dateOfBirth(f: Long => Long): Patient = copy(dateOfBirth = f(dateOfBirth))
}
Run Code Online (Sandbox Code Playgroud)

你喜欢这样使用:

scala> Patient("John",0).name(_ + " Smith")
res1: Patient = Patient(John Smith,0)
Run Code Online (Sandbox Code Playgroud)

这是透镜可以实现的,但你必须非常小心,避免使用更多的样板(在使用和定义上).它没有镜头的灵活性,你可以任意拉开功能(例如更新),但它通过简化来弥补它.

我通过这种方式进行了大部分深度更新:

myImage.patient(_.study(_.date(_ => Some(System.currentTimeMillis))))
Run Code Online (Sandbox Code Playgroud)

你只需要用当前时间的研究来更新整个树(假装这里DateTime实际上是a Long),如果你真的需要提取这种特殊树的再生能力(这对于镜片来说更自然),你可以

val setStudyTime = (t: Long) => myImage.patient(_.study(_.date(_ => Some(t))))
Run Code Online (Sandbox Code Playgroud)

甚至

def studyTimeSetter(si: SimpleImageIOD) =
  (t: Long) => si.patient(_.study(_.date(_ => Some(t))))
Run Code Online (Sandbox Code Playgroud)

如果您需要立即轻松获取此功能.

如果你需要镜头为你提供的所有东西,那么使用这种方法以特别的方式重建它是更多的工作,但如果你只需要一小部分而且大部分只是需要减少样板来进行设置,这样做相当不错工作.