[英]Scala Cake Pattern and Existential Types
我正在使用Cake Pattern編寫一個簡單的Scala應用程序,但是我遇到了特定用例的麻煩。 通常,我定義一個具有某種存在性類型(MyType)且由特征(MyTypeLike)限制的組件。 這允許“注入”一個顯式類型的特定實現(類MyType擴展MyTypeLike)。 但是,在某些情況下,我將需要定義帶有幾個子類型的存在類型,如下面的示例所示。 這是我遇到麻煩的時候。
trait ApiComponent {
type Api <: ApiLike
trait ApiLike
type RestApi <: RestApiLike
trait RestApiLike extends ApiLike {
/* omitted for brevity */
}
type SoapApi <: SoapApiLike
trait SoapApiLike extends ApiLike {
/* omitted for brevity */
}
type WebsocketApi <: WebsocketApiLike
trait WebsocketApiLike extends ApiLike {
/* omitted for brevity */
}
def apiForName: PartialFunction[String, Api]
}
trait ApiComponentImpl extends ApiComponent {
class TwitterApi extends RestApiLike {
/* omitted for brevity */
}
class SalesforceApi extends SoapApi {
/* omitted for brevity */
}
override def apiForName: PartialFunction[String, Api] = {
case "twitter" => new TwitterApi // Compiler error: TwitterApi is not a subtype of Api
case "salesforce" => new SalesforceApi // Compiler error: SalesforceApi is not a subtype of Api
}
}
trait SomeOtherComponent {
self: ApiComponent =>
def doSomethingWithTwitter = apiForName lift "twitter" map { twitterApi =>
/* Do something with Twitter API */
}
}
很容易看出為什么這行不通。 RestApi不是Api的子類型,但是RestApiLike是ApiLike的子類型,因此TwitterApi只是RestApiLike和ApiLike的子類型。 哎呀! 當然,一個簡單的解決方案是將所有內容更改為* Like。 但是,這種方式在腦海中籠罩了存在類型的整個概念。 另外,通過改變哪里有*就像沒有地方一樣,我可以任意地使編譯器在不同地方抱怨,但是我不能使編譯器高興:-(
如何解決呢? 我很高興快速解決問題,但我也想獲得一些更詳細的設計反饋和其他建議。
@wingedsubmariner是正確的,您可以在實現中添加type Api = ApiLike
。 但這太嚴格了。 我將解釋其他方法以及如何解決。
Api <: ApiLike
即Api >: Nothing <: ApiLike
,因此Api
可能就是Nothing
。 因此,僅當e
類型為Nothing
(即e
失敗)時,類型檢查器才會在需要返回Api
時接受表達式e
。 換句話說,您不能返回Api
類型的值。 Api
類型的值,您需要給出更具體的下限。 說Api = ApiLike
是一種方法,但是您可以將Api
固定為更具體的類型,但仍繼承自ApiLike
:這允許Api
在諸如ApiComponentImpl
實現中具有更多方法(從客戶端隱藏)。 請參閱下面的Sol.1 / 2/3。 ApiComponent
所有實現中, RestApi
和RestApiLike
所有實現RestApiLike
需要從Api
RestApiLike
。 這不會顯示在您擁有的代碼中,但是
RestApi <: Api
-您可以通過使用它們的交點,與創建給出兩個上限with
。 RestApiLike
(或* Like)實例也從Api
繼承。 由於Api
是抽象類型,因此您不能說特征extends Api
。 相反,您可以使用自類型注釋來指定以下內容:這為實現者添加了約束。 例如:
trait ApiComponent {
type Api <: ApiLike // with Api //EDIT, Api <: Api makes no sense.
trait ApiLike
type RestApi <: RestApiLike with Api
trait RestApiLike extends ApiLike {
this: Api =>
/* omitted for brevity */
}
type SoapApi <: SoapApiLike
trait SoapApiLike extends ApiLike {
this: Api =>
/* omitted for brevity */
}
type WebsocketApi <: WebsocketApiLike
trait WebsocketApiLike extends ApiLike {
this: Api =>
/* omitted for brevity */
}
def apiForName: PartialFunction[String, Api]
}
trait ApiComponentImpl extends ApiComponent {
//Sol. 1:
//type Api = ApiLike
//Sol. 2:
//trait Api extends ApiLike {
//decls
//}
//Sol. 3, equivalent to 2:
trait Api2 extends ApiLike {
//decls
}
type Api = Api2
class TwitterApi extends RestApiLike with Api {
/* omitted for brevity */
}
class SalesforceApi extends SoapApiLike with Api {
/* omitted for brevity */
}
override def apiForName: PartialFunction[String, Api] = {
case "twitter" => new TwitterApi // Compiler error: TwitterApi is not a subtype of Api
case "salesforce" => new SalesforceApi // Compiler error: SalesforceApi is not a subtype of Api
}
}
trait SomeOtherComponent {
self: ApiComponent =>
def doSomethingWithTwitter = apiForName lift "twitter" map { twitterApi =>
/* Do something with Twitter API */
}
}
編輯:
對於顯示的代碼,您是否需要蛋糕模式的全部功能? 並不是的。 在此答案中,我將簡化此設計,並討論您可能需要顯示的代碼。 我想您的實際代碼可能需要一些中間形式。
我們如何簡化此代碼:
Api
中的Api
,而其他類則看起來像此API的簡單實現,具有更多客戶端無法訪問的方法。 在這種情況下,您不需要ApiComponent
所有這些接口。 type Api <: ApiLike; trait ApiLike {...}
type Api <: ApiLike; trait ApiLike {...}
而不是簡單地具有trait Api {...}
,而是允許ApiComponentImpl
將Api
細化為更特定的接口,以便ApiComponentImpl
的Api
客戶端可以使用更特定的接口。 您真的需要嗎? ApiComponent
的單個實現。 只要這不會改變,您可以通過ApiComponent
和ApiComponentImpl
進一步簡化此代碼,但是我想您確實想保持這種分離,因為您想隱藏ApiComponentImpl
一部分並允許其他實現。 因此,這是代碼的(過度)簡化版本:
trait ApiComponent { trait Api { ... } def apiForName: PartialFunction[String, Api] } trait ApiComponentImpl { //Many implementations of Api //... def apiForName: PartialFunction[String, Api] = //pick which. }
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.