2013-03-23 52 views
7

我目前正试图围绕斯卡拉思考,并打算将它用于我的下一个必须处理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 
    } 
} 

有很多模块,它们可能以不同的组合形成不同的IOD。斯卡拉反过来拥有所有特性,案例类,动态类等丰富的建模能力。你将如何在Scala中建模这样的领域?我很新的语言,但我一直在想使用一成不变的情况下类来定义模块,然后在不同的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 
} 

等等等等,对于镜头它可能会成为一个问题,编码所有的样板 - 将有M x N x L镜头的订单覆盖整个域的M IOD,N模块和L属性。另外一些领域的可选性使得任务极其复杂,至少在scalaz-seven

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

回答

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)) 
} 

您使用像这样:

scala> Patient("John",0).name(_ + " Smith") 
res1: Patient = Patient(John Smith,0) 

这有点与镜头可以实现的,但你必须非常小心地避免使用更多的样板(在使用和定义上)。它没有镜头的灵活性,您可以随意拆卸功能(例如更新),但它可以通过简化来弥补。

我做了我的大部分深更新这样:

myImage.patient(_.study(_.date(_ => Some(System.currentTimeMillis)))) 

是所有你需要与当前时间(这里假装DateTime实际上是一个Long)的一项研究,以更新整个树,如果你真的需要提取再生这个paticular树(这是比较有镜头自然的)的这种能力,你可以

val setStudyTime = (t: Long) => myImage.patient(_.study(_.date(_ => Some(t)))) 

甚至

def studyTimeSetter(si: SimpleImageIOD) = 
    (t: Long) => si.patient(_.study(_.date(_ => Some(t)))) 

如果您需要在临时通知时轻松获取此功能。

如果您需要镜头为您提供的所有东西,那么使用这种方法以专门的方式重新构建它会更有帮助,但如果您只需要一小部分,并且大多只需要减少样板设置即可做得不错。

+0

你是什么意思的“嵌套镜头”? – 2013-03-23 23:33:34

+0

@ak。 - 好吧,“写作”真的。如果你有'Long => Patient'和'Patient => SimpleImageIOD'这样的东西,你可以编写它们来得到一个'Long => SimpleImageIOD'。你不需要建立所有的可能性;你可以在你使用它们的地方做到这一点。 – 2013-03-23 23:49:37

+0

在斯卡拉镜片方面,你可能意指'和那么'而不是'撰写'。我认为将'compose'应用于两个镜头'Long => Patient'和'String => Patient'会产生一个镜头,它能够做到(Long,String)=> Patient'。但重点不在镜架上套上镜头,对吗? – 2013-03-24 15:17:16