2012-05-29 14 views
2

Schema.org是標記詞彙表(針對網頁),並根據屬性(無方法)定義了多個類型。我目前正試圖在Scala中將該模式的部分建模爲內部模型類,以便與面向文檔的數據庫(MongoDB)和Web框架一起使用。如何在Scala中模擬schema.org?

如可在LocalBusiness的定義可以看出,使用schema.org多重繼承也包括從「放置」類型屬性。所以我的問題是:你將如何在Scala中建模這樣的模式?

我想出了兩種解決方案爲止。第一個使用常規類來建模單個繼承樹,並使用特徵來混合這些附加屬性。

trait ThingA { 
    var name: String = "" 
    var url: String = "" 
} 

trait OrganizationA { 
    var email: String = "" 
} 

trait PlaceA { 
    var x: String = "" 
    var y: String = "" 
} 

trait LocalBusinessA { 
    var priceRange: String = "" 
} 

class OrganizationClassA extends ThingA with OrganizationA {} 

class LocalBusinessClassA extends OrganizationClassA with PlaceA with LocalBusinessA {} 

第二個版本嘗試使用案例類。但是,由於案例類繼承已被棄用,所以我無法輕鬆地對主要層次結構進行建模。

trait ThingB { 
    val name: String 
} 

trait OrganizationB { 
    val email: String 
} 

trait PlaceB { 
    val x: String 
    val y: String 
} 

trait LocalBusinessB { 
    val priceRange: String 
} 

case class OrganizationClassB(val name: String, val email: String) extends ThingB with OrganizationB 

case class LocalBusinessClassB(val name: String, val email: String, val x: String, val y: String, val priceRange: String) extends ThingB with OrganizationB with PlaceB with LocalBusinessB 

有沒有更好的方法來建模?我可以使用類似的組成

case class LocalBusinessClassC(val thing:ThingClass, val place: PlaceClass, ...) 

但隨後的過程中,不能當「地方」預計,當我嘗試呈現在谷歌地圖中使用的東西LocalBusiness,例如。

回答

0

什麼對你最有效最大程度上取決於你想如何將對象映射到底層數據存儲。

鑑於需要多重繼承,可能值得考慮的方法是僅使用特徵。這爲您提供了多重繼承,最少量的代碼重複或模板。

trait Thing { 
    val name: String    // required 
    val url: Option[String] = None // reasonable default 
} 

trait Organization extends Thing { 
    val email: Option[String] = None 
} 

trait Place extends Thing { 
    val x: String 
    val y: String 
} 

trait LocalBusiness extends Organization with Place { 
    val priceRange: String 
} 

注意Organization延伸Thing一樣,Place,就像在schema.org

要實例化它們,您可以創建指定所有屬性值的匿名內部類。

object UseIt extends App { 
    val home = new Place { 
    val name = "Home" 
    val x = "-86.586104" 
    val y = "34.730369" 
    } 

    val oz = new Place { 
    val name = "Oz" 
    val x = "151.206890" 
    val y = "-33.873651" 
    } 

    val paulis = new LocalBusiness { 
    val name = "Pauli's" 
    override val url = "http://www.paulisbarandgrill.com/" 
    val x = "-86.713660" 
    val y = "34.755092" 
    val priceRange = "$$$" 
    } 

} 

如果任何字段具有合理的默認值,則可以在特徵中指定默認值。

我將沒有值的字段留爲空字符串,但可能更有意義的是,使可選字段的類型爲 Option[String],以更好地指示它們的值未設置。 您喜歡使用Option,所以我使用Option

這種方法的缺點是,編譯器生成一個匿名內部類中的每個實例化的特徵之一的地方。這可能會給你一個.class文件的爆炸。更重要的是,這意味着同一性狀的不同實例會有不同的類型。

編輯:

在問候你將如何使用這從數據庫中,大大取決於你如何訪問您的數據庫加載對象。如果使用對象映射器,則需要按照映射器期望它們的結構化的方式構建模型對象。如果這種技巧適用於你的對象映射器,我會感到驚訝。

如果您正在編寫自己的數據訪問層,那麼您可以簡單地使用DAOrepository模式進行數據訪問,將邏輯放在那裏構建匿名內部類。

這只是構建這些對象的一種方法。這甚至不是最好的方式,但它證明了這一點。

trait Database { 
    // treats objects as simple key/value pairs 
    def findObject(id: String): Option[Map[String, String]] 
} 

class ThingRepo(db: Database) { 
    def findThing(id: String): Option[Thing] = { 
    // Note that in this way, malformed objects (i.e. missing name) simply 
    // return None. Logging or other responses for malformed objects is left 
    // as an exercise :-) 
    for { 
     fields <- db.findObject(id) // load object from database 
     name <- field.get("name") // extract required field 
    } yield { 
     new Thing { 
     val name = name 
     val url = field.get("url") 
     } 
    } 
    } 
} 

還有比這更給它一點(你如何識別物體,你他們如何存儲在數據庫中,你怎麼線了倉庫,你會如何處理polymorphic queries等)。但這應該是一個好的開始。

+0

非常感謝您的回答!是的,你是對的,使用選項建模所有模型更有意義。但是,我不太確定我該如何動態創建匿名內部類。如果我需要爲數據庫中的每個對象創建一個這樣的類(這些對象不過是鍵值映射),我認爲它不會很好地伸縮。 – IRQ

+0

我添加了一個如何使用[存儲庫模式](http://martinfowler.com/eaaCatalog/repository.html)將這些對象映射到數據庫的示例。 – leedm777

相關問題