2013-08-21 67 views
2

我很難找到設計一些簡單的類來表示Scala中的HTTP消息的優雅方法。如何在使用繼承時設計不可變模型類

說我有這樣的事情:

abstract class HttpMessage(headers: List[String]) { 
    def addHeader(header: String) = ??? 
} 

class HttpRequest(path: String, headers: List[String]) 
    extends HttpMessage(headers) 

new HttpRequest("/", List("foo")).addHeader("bar") 

我怎樣才能讓addHeader方法返回其自身的副本與新的頭添加? (並且保持path的當前值)

謝謝, Rob。

回答

4

這很煩人,但實現您所需模式的解決方案並不是微不足道的。

第一點要注意的是,如果你想保留你的子類型,你需要添加一個類型參數。沒有這一點,你是不是能夠在HttpMessage指定一個未知的返回類型

abstract class HttpMessage(headers: List[String]) { 
    type X <: HttpMessage 
    def addHeader(header: String):X 
} 

然後你就可以實現你的具體子類的方法,你必須指定X的值:

class HttpRequest(path: String, headers: List[String]) 
    extends HttpMessage(headers){ 
    type X = HttpRequest 
    def addHeader(header: String):HttpRequest = new HttpRequest(path, headers :+header) 
} 

一個更好的,更具擴展性的解決方案是使用隱式的目的。

trait HeaderAdder[T<:HttpMessage]{ 
     def addHeader(httpMessage:T, header:String):T 
} 

,現在你可以在HttpMessage類像下面的定義方法:

abstract class HttpMessage(headers: List[String]) { 
     type X <: HttpMessage 
     def addHeader(header: String)(implicit headerAdder:HeaderAdder[X]):X = headerAdder.add(this,header) } 
} 

這個最新方法是基於類型類的概念和規模不是繼承好得多。這個想法是,您不必強制您的層次結構中的每個T都具有有效的HeaderAdder [T],並且如果您嘗試在範圍內沒有隱式可用的類上調用該方法,則會收到編譯時錯誤。

這很好,因爲它可以防止在層次結構中的某些類變爲「髒」或重構它以避免它變成時,必須實現addHeader = sys.error(「This is not supported」) 「髒」。

管理隱最好的辦法是把它們放在一個特點如下所示:

trait HeaderAdders { 
    implicit val httpRequestHeaderAdder:HeaderAdder[HttpRequest] = new HeaderAdder[HttpRequest] { ... } 
    implicit val httpRequestHeaderAdder:HeaderAdder[HttpWhat] = new HeaderAdder[HttpWhat] { ... } 
} 

,然後您還提供了一個對象,如果用戶如果有不能混用(如通過物體的反射性質研究框架,你不想額外的屬性添加到您當前的實例)(http://www.artima.com/scalazine/articles/selfless_trait_pattern.html

object HeaderAdders extends HeaderAdders 

因此,例如,你可以寫的東西,如

// mixing example 
class MyTest extends HeaderAdders // who cares about having two extra value in the object 

// import example 
import HeaderAdders._ 

class MyDomainClass // implicits are in scope, but not mixed inside MyDomainClass, so reflection from Hiberante will still work correctly 

順便說一句,這個設計問題是相同的斯卡拉集合,唯一的區別是你的HttpMessage是TraversableLike。看看這個問題Calling map on a parallel collection via a reference to an ancestor type

+0

請問你能否完成這個例子或描述如何採取最後一步?你可以在Object HttpRequest中定義一個隱含的val HeaderAdder,是否正確?非常感謝 – LaloInDublin

+0

嗨Edmondo1984,謝謝你的回覆!很高興知道我不只是錯過了一些明顯的東西:)雖然我沒有考慮過你提出的第二種選擇,我一定會嘗試一下。 – headexplodes