2017-05-18 4 views
1

私は非常に畳み込まれていると感じるコードを書いています。畳み込みコードを暗黙的に簡略化

私は、特性である引数を受け入れるAPIを持っています。この形質は、多くのタイプによって実施することができる。さらに、これらのクラスのそれぞれは、特殊なプロセッサによって処理される必要があります。

例えば、以下の2つの実際のタイプのMobileContextとWebContextを持つContextという名前のTraitを作成しました。

MobileContextとWebContextが別にログに記録され、ContextWriter [MobileContext]とContextWriter [WebContext]という形式の特殊な実装があるとします。

このメソッドは汎用である必要がありますが、実際の型の特性に応じて適切なContextWriterへの呼び出しをディスパッチできる必要があります。

ここに私のコードです。

trait Context 
case class WebContext(name: String) extends Context 
case class MobileContext(name: String) extends Context 


trait ContextWriter[T] { 
    def log(message: String, context: T) : Unit 
} 

object ContextWriterUtil { 
    def log[T](message: String, context: T)(implicit writer: ContextWriter[T]) = { 
     writer.log(message, context) 
    } 
} 

object ContextWriterImplicits { 
    implicit val webImpl = new ContextWriter[WebContext] { 
     override def log(message: String, context: WebContext) = println(s"I am in web context ${context} and the message is ${message}") 
    } 
    implicit val mobileImpl = new ContextWriter[MobileContext] { 
     override def log(message: String, context: MobileContext) = println(s"I am in mobile context ${context} and the message is ${message}") 
    } 
    implicit val baseImpl = new ContextWriter[Context] { 
     override def log(message: String, context: Context) = context match { 
     case s: WebContext => { 
      val writer = implicitly[ContextWriter[WebContext]] 
      writer.log(message, s) 
     } 
     case s: MobileContext => { 
      val writer = implicitly[ContextWriter[MobileContext]] 
      writer.log(message, s) 
     } 
     case _ => throw new Exception("don't understand this type") 
     } 
    } 
} 

import ContextWriterImplicits._ 
object MyApplication extends App { 

    // this is the generic method. 
    def call[T <: Context](message: String)(implicit context: T) = { 
     val actualContext = implicitly[Context] 
     ContextWriterUtil.log(message, actualContext) 
    } 
    def web() = { 
     implicit val webContext = WebContext("web") 
     call("I am calling the method") 
    } 
    def mobile() = { 
     implicit val mobileContext = MobileContext("mobile") 
     call("I am calling the method") 
    } 
    web() 
    mobile() 
} 

これは機能します。しかし、私はあまりにも冗長で扱いにくいと感じています。私はよりクリーンな方法でこれを記述したいと思います。

答えて

1

TLDR:コードから継承を削除します。

baseImpl: ContextWriter[Context]が必要な理由がわかりませんが、これを暗黙的に削除して、より正確なコンテキストを常に求めてください。 callは次のようになります。そのために

def call[T: ContextWriter](message: String)(implicit context: T) = { 
    ContextWriterUtil.log(message, context) 
} 

明示的に型パラメータを指定するwebmobileを更新する必要が動作するように。コードのコンパイルを行い、この型パラメータの単一のインスタンスがある場合でも、scalacはそれを把握することができません。

def web() = { 
    implicit val webContext = WebContext("web") 
    call[WebContext]("I am calling the method") 
} 

def mobile() = { 
    implicit val mobileContext = MobileContext("mobile") 
    call[MobileContext]("I am calling the method") 
} 

あなたがにContextContextWriterを組み合わせることで、明示的なタイピングの乗り心地を得ることができるかもしれません単一の暗黙のたとえば、Contextをインスタンス化するときに、明示的なContextWriter引数を取ってそれを終了させるのはなぜですか?

関連する問題