2012-03-13 4 views
2

私はこの問題にJavaとScalaの両方のタグを付けました。主にJavaで開発していますが、Scalaのソリューションが異なるかどうか確認したいと思います。共通のインターフェイスで異なる動作をするクラスを設計する

私のアプリケーションのクラスを設計する際に問題があります。私は、異なる行動を持つ共通のオブジェクトのセットを持っています。私は最近、パターンについての本を読んでいたので、「わかりました、私はここで戦略パターンを使うことができます」、行動をフィールドオブジェクトとして定義し、すべてのロジックを委譲しました。そして、ここに私の問題が始まっています:)

私は飛ぶことができると私はいくつかのFlyBehaviourに飛行を委任する基本クラスを持っていると言いましょう。

しかし、私のアヒルは少し違っていて、私は行動がそれに依存してほしいと思っています。最初に私はSpaceDuckを持っています。SpaceDuckはSpaceDuckのためだけに定義されたspaceShipフィールドを使用します。次に、HelicopterDuckをできるだけ低速で飛ばして、HelicopterDuckのためだけに定義されている防空防空壕を使います。だから、コードでは、ここに私の行動の実装では、私が実際にspaceduckするための基準やhelicopterduckを持っていないと、このコードはコンパイルされません。この

class SpaceDuck extends Duck { 
    String spaceship; 
} 

class SpaceFlyBehaviour implements IFlyBehaviour { 
    void fly() { 
     System.out.println("Flying in space on spaceship: " + spaceduck.spaceship); 
    } 
} 

class HelicopterDuck extends Duck { 
    int flares; 
} 

class HelicopterFlyBehaviour implements IFlyBehaviour { 
    void fly() { 
     while(helicopterduck.flares > 0) { 
      System.out.println("I'm going low and using flares"); 
      helicopterduck.flares--; 
     } 
    } 
} 

のようなものです。私はちょうど私が想像した方法のサンプルを提供しました。私はIFlyBehaviourを修正し、アヒルをfly()メソッドの引数として渡すことができましたが、ダックの特定のフィールドにアクセスするためにダウンキャストしなければなりません。

明らかに、IFlyBehaviourを落として、各アヒルのfly()メソッドにロジックを移しているようです。しかし、私は、多くの異なる宇宙飛行行動とヘリコプター行動を期待しており、fly()は唯一の方法ではありません。それはsquack()、run()などであり、それぞれ異なる動作セットを持ちます。だから私のクラス階層は巨大になり、サポートされなくなります。

私の実際のアプリケーションでは、いくつかの実行可能かつ停止可能なインスタンスを実行し、異なる方法で停止できます。 1つのインスタンスはSSHスクリプトを介して起動され、もう1つはMBean(またはSSH、ユーザーの設定によります)、サードパーティなどを使用して3番目に起動されます。

正しい方向に私を押し込むという考えは非常に役に立ちます。前もって感謝します!

+1

これは興味があるかもしれません:http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/ –

+0

面白そうです。共有してくれてありがとう。 – Soteric

答えて

2

Scalaでは、traitsがこの種のもののためだけに含まれていました。

Javaでは、より難しいです。私は、ビヘイビアではなく、アヒルのサブクラスを取り除き、型固有のプロパティをビヘイビアクラスに移動させようとします。共通のインターフェースを介してのみアヒルを使用すると、これらのプロパティーは使用できなくなります。これらのプロパティーはインスタンシエーションの時点でのみ表示する必要があります。

+0

それは理にかなっています。私はこのようにします。ありがとう。 – Soteric

2

まず、DuckクラスのflyBehaviourを初期化していません。あなたはこのようにそれを初期化することができます:

class SpaceDuck extends Duck { 
    String spaceship; 
    public Duck() { 
     setFlyBehavior(new SpaceFlyBehaviour(this)); 
    } 
} 

代わりにあなたが行動の方法であひるの参照を提供することができます:あなたはScalaで

public abstract class Duck { 
    public abstract void fly(); 
} 

public class SpaceDuck extends Duck { 
    String spaceship; 
    public void fly() { 
     System.out.println("Flying in space on spaceship: " + spaceduck.spaceship); 
    } 
} 
1

interface IFlyable { void fly(); } 

interface IFlyBehaviour { void fly(IFlyable flyable); } 

または、最も簡単な方法を次のようなことができます:

abstract class Duck {def fly: Unit} 

class BasicDuck extends Duck { def fly {println("flying")} } 

trait SpaceshipFlight extends Duck { 
    def spaceship: String 
    abstract override def fly() { 
     super.fly 
     println("but now I'm in space on a: " + spaceship); 
    } 
} 

trait Flares extends SpaceshipFlight { 
    var flares: Int 
    abstract override def fly() { 
     super.fly 
     while(flares > 0) { 
      println("I'm going low: flares are at " + flares) 
      flares=flares-1 
     } 
    } 
} 
呼び出しサイトで

あなたは、私はScalaであなたも1つの形質はの動作をオーバーライドすることができますことを示すために少しをあなたの例を変更したあなたのアヒル

scala> new BasicDuck with SpaceshipFlight {def spaceship="rocket"} 
res1: BasicDuck with SpaceshipFlight = [email protected] 

scala> res1.fly 
flying 
but now I'm in space on a: rocket 

//now with flares! 
scala> new BasicDuck with SpaceshipFlight with Flares {def spaceship="rocket"; var flares=5} 
res2: BasicDuck with SpaceshipFlight with Flares = [email protected] 

scala> res2.fly 
flying 
but now I'm in space on a: rocket 
I'm going low: flares are at 5 
I'm going low: flares are at 4 
I'm going low: flares are at 3 
I'm going low: flares are at 2 
I'm going low: flares are at 1 

注意して、あなたが好きな特徴を混ぜることができあなたは私たちがSpaceshipFlightやフレア特性にやったように「スーパー」を使用し、特徴を自分で作ることができるので、以前のものは、「スタッカブル」(どこ

EDIT第二の方法を追加する(これは実際には「stackable trait pattern」と呼ばれています)インスタンス化後に異なる動作を注入することができます)

class Duck 
def fly[D <: Duck](duck:D, flyBehavior: (D => Unit)) { 
    flyBehavior(duck) 
}  

class SpaceDuck(val spaceship: String) extends Duck 

val d = new SpaceDuck("rocket") 

// a behavior is now simply a function (no need to wrap it in a class) 
val spacefly = (d: SpaceDuck) => println("flying on a " + d.spaceship) 
val normalfly = (d: SpaceDuck) => println("flying normally. Not using my " + d.spaceship) 

// using different behaviors at runtime 
fly(d, spacefly) // flying on a rocket 
fly(d, normalfly) // flying normally. Not using my rocket 
+0

この具体的なケースでは、Scalaの特性は大きくて強力ですが柔軟性は劣ります。実行時に動作を変更することはできません。また、ユーザーが提供するクラス名を使用して動作を選択することもできません(Javaでは、クラス名でオブジェクトをインスタンス化できます)。とにかくサンプルをありがとう、それは面白かったです。私はスカラが大好きですが、私の同僚はいません:) – Soteric

+1

@Soteric;私は、これをscalaでコーディングすることができる2番目の方法を追加しました。これにより、実行時に動作を変更することができます。文字列からクラスを作成する場合、scalaは 'Class.forName(" Duck ")。newInstance'を実行できますが、私はscalaのリフレクション(2.9のように)はそれほど強力ではないことに同意します。私は2.10が大きな改善をもたらすべきだと読んだ。また、私はあなたの(またはあなたの同僚)Javaの代わりにスカラを使用するように説得するためにこれを書いていません、私はちょうどあなたの質問のスカラ部分に答えることを試みています:) –

+0

はい、間違ってください。私は、形質の一部についての疑問を明確にしたかったのです。私は何かを私に納得させる試みのようにそれを扱わない:) – Soteric

関連する問題