2011-02-09 1 views
11

私はJava(または似たような言語)でプログラミングしていますが、インタフェースと実装クラスを使用して簡単なバージョンの戦略パターンを使用して、コードで特定のコンセプトの実行時に選択可能な実装を提供します。Scalaの戦略パターンの代わりの方が良いでしょうか?

非常に工夫した例として、私はJavaコードでノイズを発生させることができ、実行時に動物の種類を選択できるようにするための一般的な概念を持つことができます。だから私はこれらの行に沿ってコードを書くだろう:

interface Animal { 
    void makeNoise(); 
} 

class Cat extends Animal { 
    void makeNoise() { System.out.println("Meow"); } 
} 

class Dog extends Animal { 
    void makeNoise() { System.out.println("Woof"); } 
} 

class AnimalContainer { 
    Animal myAnimal; 

    AnimalContainer(String whichOne) { 
     if (whichOne.equals("Cat")) 
      myAnimal = new Cat(); 
     else 
      myAnimal = new Dog(); 
    } 

    void doAnimalStuff() { 
     ... 
     // Time for the animal to make a noise 
     myAnimal.makeNoise(); 
     ... 
    } 

シンプルで十分です。最近、私はScalaのプロジェクトに取り組んできましたが、私は同じことをしたいと思います。 - 特性とのインタフェースは本当にでないことは言うまでもありません

trait Animal { 
    def makeNoise:Unit 
} 

class Cat extends Animal { 
    override def makeNoise:Unit = println("Meow") 
} 

class AnimalContainer { 
    val myAnimal:Animal = new Cat 
    ... 
} 

はしかし、これは非常に機能的に非常にJavaに似ないようだ:それはこのようなもので、この使用して特性を行うのは簡単に十分なようです同じこと。だから私は実行時に抽象的なコンセプトの具体的な実装を選択できるように、私のScalaコードでStrategyパターン(またはそのようなもの)を実装するための慣用的な方法があるのだろうかと思っています。それとも、これを達成するための最良の方法を形質を使用していますか?

答えて

8

ケーキパターンを変形することができます。戦略パターンの点で

trait Animal { 
    def makenoise: Unit 
} 

trait Cat extends Animal { 
    override def makeNoise { println("Meow") } 
} 

trait Dog extends Animal { 
    override def makeNoise { println("Woof") } 
} 

class AnimalContaineer { 
    self: Animal => 

    def doAnimalStuff { 
     // ... 
     makeNoise 
     // ... 
    } 
} 

object StrategyExample extends Application { 
    val ex1 = new AnimalContainer with Dog 
    val ex2 = new AnimalContainer with Cat 

    ex1.doAnimalStuff 
    ex2.doAnimalStuff 
} 

、戦略上の自己の種類は、それがアルゴリズムのある種の特定の実装と混合されなければならない示します。

+0

Daniel!ありがとう、ダニエル!これは既存のコード(機能よりもOOです)に似ているためです。@ VonCの回答も素晴らしいですし、もっと機能的なコードベース。 – MattK

11

それは「Design pattern in scala」にその例のように行くことができる:

関数はファーストクラスのオブジェクトであるか、閉鎖が利用できる場合には、Strategyパターンは明らかである任意の言語のように。
たとえば、私は、実行時に、抽象概念の具体的な実装を選択することができるように

trait TaxPayer 
case class Employee(sal: Long) extends TaxPayer 
case class NonProfitOrg(funds: BigInt) extends TaxPayer 

//Consider a generic tax calculation function. (It can be in TaxPayer also). 
def calculateTax[T <: TaxPayer](victim: T, taxingStrategy: (T => long)) = { 
    taxingStrategy(victim) 
} 

val employee = new Employee(1000) 
//A strategy to calculate tax for employees 
def empStrategy(e: Employee) = Math.ceil(e.sal * .3) toLong 
calculateTax(employee, empStrategy) 

val npo = new NonProfitOrg(100000000) 
//The tax calculation strategy for npo is trivial, so we can inline it 
calculateTax(nonProfit, ((t: TaxPayer) => 0) 

:「課税」の例を考えてみましょう。

ここでは、TaxPayerのそれらのサブタイプにサブクラスでTの特殊化を制限するために、upper boundを使用しています。

+0

優れた点。私は戦略パターンを再現することに焦点を合わせ、機能的な代替案を提示するのを忘れてしまった。1つは本当に4のデザインパターンとScalaの本についてのブログでなければなりません。 :-) –

+0

@ダニエルああ! 「4の本」は実際には4人のギャングです!私はそれを理解するためにちょっと考えました... – pedrofurla

+0

非常にいいです。こちらの類似の例もここに表示されています:https://pavelfatin.com/design-patterns-in-scala/ #strategy – Philippe

3

Javaから来ていますが、私はまだOOスタイルの構文が好きです。私はちょうどDeriving Scalaz(免責事項)の最初の部分を見て、これをPimp My LibraryとImplicitsのコンセプトを自分自身に実証する少しの練習として使っています。私は自分の発見を分かち合うことができると思った。一般に、このように設定するにはプログラミングオーバーヘッドが少しありますが、私は個人的に使用法がより洗練されていると思います。

この最初のスニペットは、Pimpマイライブラリパターンを追加する方法を示しています。

trait TaxPayer 

/** 
* This is part of the Pimp My Library pattern which converts any subclass of 
* TaxPayer to type TaxPayerPimp 
*/ 
object TaxPayer { 
    implicit def toTaxPayerPimp[T <: TaxPayer](t: T) : TaxPayerPimp[T] = 
    new TaxPayerPimp[T] { 
     val taxPayer = t 
    } 
} 

/** 
* This is an extra trait defining tax calculation which will be overloaded by 
* individual TaxCalculator strategies. 
*/ 
trait TaxCalculator[T <: TaxPayer] { 
    def calculate(t: T) : Long 
} 

/** 
* This is the other part of the Pimp My Library pattern and is analogus to 
* Scalaz's Identity trait. 
*/ 
trait TaxPayerPimp[T <: TaxPayer] { 
    val taxPayer: T 
    def calculateTax(tc: TaxCalculator[T]) : Long = tc.calculate(taxPayer) 
} 


case class Employee(sal: Long) extends TaxPayer 

/** 
* This is the employee companion object which defines the TaxCalculator 
* strategies. 
*/ 
object Employee { 
    object DefaultTaxCalculator extends TaxCalculator[Employee] { 
    def calculate(e: Employee) = Math.ceil(e.sal * .3) toLong 
    } 

    object BelgianTaxCalculator extends TaxCalculator[Employee] { 
    def calculate(e: Employee) = Math.ceil(e.sal * .5) toLong 
    } 
} 

case class NonProfitOrg(funds: BigInt) extends TaxPayer 

/** 
* This is the NonProfitOrg companion which defines it's own TaxCalculator 
* strategies. 
*/ 
object NonProfitOrg { 
    object DefaultTaxCalculator extends TaxCalculator[NonProfitOrg] { 
    def calculate(n: NonProfitOrg) = 0 
    } 
} 



object TaxPayerMain extends Application { 

    //The result is a more OO style version of VonC's example 
    val employee = new Employee(1000) 
    employee.calculateTax(Employee.DefaultTaxCalculator) 
    employee.calculateTax(Employee.BelgianTaxCalculator) 

    val npo = new NonProfitOrg(100000000) 
    npo.calculateTax(NonProfitOrg.DefaultTaxCalculator) 

    //Note the type saftey, this will not compile 
    npo.calculateTax(Employee.DefaultTaxCalculator) 

} 

私はこれをもう少し詳しく説明します。

trait TaxPayer 
object TaxPayer { 
    implicit def toTaxPayerPimp[T <: TaxPayer](t: T) : TaxPayerPimp[T] = 
     new TaxPayerPimp[T] { 
     val taxPayer = t 
     } 
} 

trait TaxCalculator[T <: TaxPayer] { 
    def calculate(t: T) : Long 
} 

/** 
* Here we've added an implicit to the calculateTax function which tells the 
* compiler to look for an implicit TaxCalculator in scope. 
*/ 
trait TaxPayerPimp[T <: TaxPayer] { 
    val taxPayer: T 
    def calculateTax(implicit tc: TaxCalculator[T]) : Long = tc.calculate(taxPayer) 
} 

case class Employee(sal: Long) extends TaxPayer 

/** 
* Here we've added implicit to the DefaultTaxCalculator. If in scope 
* and the right type, it will be implicitely used as the parameter in the 
* TaxPayerPimp.calculateTax function. 
* 
* 
*/ 
object Employee { 
    implicit object DefaultTaxCalculator extends TaxCalculator[Employee] { 
    def calculate(e: Employee) = Math.ceil(e.sal * .3) toLong 
    } 

    object BelgianTaxCalculator extends TaxCalculator[Employee] { 
    def calculate(e: Employee) = Math.ceil(e.sal * .5) toLong 
    } 
} 

/** 
* Added implicit to the DefaultTaxCalculator... 
*/ 
case class NonProfitOrg(funds: BigInt) extends TaxPayer 
object NonProfitOrg { 
    implicit object DefaultTaxCalculator extends TaxCalculator[NonProfitOrg] { 
    def calculate(n: NonProfitOrg) = 0 
    } 
} 

object TaxPayer2 extends Application { 

    println("TaxPayer2") 

    val taxPayer = new Employee(1000) 

    //Now the call to calculateTax will 
    //implicitely use Employee.DefaultTaxCalculator 
    taxPayer.calculateTax 
    //But if we want, we can still explicitely pass in the BelgianTaxCalculator 
    taxPayer.calculateTax(Employee.BelgianTaxCalculator) 

    val npo = new NonProfitOrg(100000000) 

    //implicitely uses NonProfitOrg.defaultCalculator 
    npo.calculateTax 


} 
関連する問題