2016-12-29 18 views
2

Javaでオブジェクト指向技術を使用するステートマシン用のフレームワークを作成しようとしています。 Systemは、一度に1つだけ有効にすることができます。StateJava継承によるコード継承のコード再利用

public interface State {} 
public interface System { 
    State currentState(); 
} 

UpdateableSystemと呼ばれるのは、私はその状態を確認し、変更するために、定期的に更新されるべきシステムの新しいタイプを追加したいとしましょう。それはタイプがUpdateableStateである州だけを持つことができます。

public interface UpdateableState extends State { 
    /** 
    * Check to see if the system should switch to a different state. 
    * @returns new state of the system 
    */ 
    UpdateableState update(); 
} 

public interface UpdateableSystem extends System { 
    @Override 
    UpdateableState currentState(); 
    /** 
    * Runs update method of the current state, or otherwise updates the system. 
    * @returns the current state of the system after the update 
    */ 
    UpdateableState update(); 
} 

UpdateableSystemのみのタイプUpdateableStateのある状態を持つことができ、かつUpdateableSystemを使用して、クライアントがUpdateableStateのを期待していますので、私は、のみのみUpdateableState、ないStateを返すようにcurrentStateメソッドをオーバーライド。

より多くのサブクラスが作成されるにつれて、多くのメソッドを何度もオーバーライドしないようにするには、ジェネリックが解決策のようです。 ここに更新されたインターフェイスがあります。

public interface System<SystemState extends State> { 
    SystemState currentState(); 
} 

public interface UpdateableState<SystemState extends UpdateableState<SystemState>> extends State { 
    SystemState update(); 
} 

public interface UpdateableSystem<SystemState extends UpdateableState<SystemState>> extends System<SystemState> { 
    SystemState update(); 
} 

は、システムや状態が注意する必要があることを、私は別のタイプ、インターフェースContextを追加したいと言うことができます。 Contextはサブタイプすることができます。クライアントとContextStateサブタイプは、そのContextサブタイプの完全なインターフェースを使用できる必要があります。

public interface Context {} 
public interface ContextState<SystemContext extends Context, SystemState extends ContextState<SystemContext, SystemState>> extends UpdateableState<SystemState> { 
    SystemState updateWithContext(SystemContext context); 
} 
public interface ContextSystem<SystemContext extends Context, SystemState extends ContextState<SystemContext, SystemState>> extends UpdateableSystem<SystemState> { 
    // Some methods here that require SystemContext type 
} 

は突然、これらのパラメータ化された型は、混乱し、維持するのが困難であり、定型の全体トンになりました。

これは具体的な実装は、実施例のようになります。

class CarContext implements Context {...} 
interface CarState extends ContextState<CarContext, CarState> {...} 
    class CarDrivingState implements CarState {} 
    class CarIdleState implements CarState {} 
class Car implements ContextSystem<CarContext, CarState> {...} 

ContextStateから継承されたメソッドのシグネチャがCarContextによってパラメータ化されることになるので、各コンクリートCarStateが、CarContextインタフェースによって提供される追加のメソッドにアクセスすることができるであろうタイプ。したがって、明示的なキャストはコンテキスト・パラメータupdateWithContextには必要ありません。

最終的には、パラメータ化に追加の型を追加するときに問題があまりにも多いため、継承を使用してコードを再利用する設計の代替案がわかりません。また、可能な場合は明示的なキャストを避ける。このデザインを改善するための提案はありますか?

+4

Javaジェネリックはテンプレートタイプではありません。このインタフェースのCarStateはContextState (例えば)を拡張し、 'インタフェースCarState extends ContextState 'と書くこともできます - 型のローカル名は単なる* label *です。 –

+0

「ContextState」インターフェースの定義内でラベルとして機能しますが、 'CarState'インターフェースの定義内では、それらは実際のタイプでなければなりません。私の知る限り、実際のインターフェースやクラス。 – Ashes

+3

もう一度お試しください。 Javaジェネリックは、コンパイル時型チェックシステムです。コンパイル後(実行時のオーバーヘッドはありません)、java.langには[** erased **](https://docs.oracle.com/javase/tutorial/java/generics/erasure.html)されます。オブジェクト ';あなたが期待しているように見えるのは、それらがC++テンプレートのように動作することです。彼らはしない。 –

答えて

1

これについて考えていて、サンプルコードで少し遊んだ後は、Java Generics code style standardsへの適切な遵守の恩恵を受ける可能性がありますが、この問題の最適な解決策があると思います。

インターフェイスの宣言でジェネリック型のフルネームを使用することで、実際にメソッドにアクセスしたときに、実際にそれらを読みにくくすることができます。ジェネリック型は、実際のインターフェイス名やクラス名と混同しやすいです。彼らはより多くのスペースを占めることは言うまでもありません。

public interface State{} 
public interface System<S extends State> { 
    S currentState(); 
} 

public interface UpdateableState<S extends UpdateableState<S>> extends State { 
    S update(); 
} 
public interface UpdateableSystem<S extends UpdateableState<S>> extends System<S> { 
    S update(); 
} 

public interface Context{} 
public interface ContextState<C extends Context, S extends ContextState<C, S>> extends UpdateableState<S> { 
    S updateWithContext(C context); 
} 
public interface ContextSystem<C extends Context, S extends ContextState<C, S>> extends UpdateableSystem<S> { 
    // Some methods here that require SystemContext type 
} 

class CarContext implements Context {...} 
interface CarState extends ContextState<CarContext, CarState> {...} 
class CarDrivingState implements CarState {} 
class CarIdleState implements CarState {} 
class Car implements ContextSystem<CarContext, CarState> {...} 

宣言はまだ冗長ですが、それほど控えています。さらに、完全参照型と汎用型のパラメータを明確に区別しています。

最も重要なことは、このソリューションが機能することです。しかし


、あなたはこれをさらに簡素化したい場合、あなたは、リスクがこれらのインターフェイスは、悪用される可能性があることを。例えば:ここで

public interface State{} 
public interface System<S> { 
    S currentState(); 
} 

public interface UpdateableState<S> extends State { 
    S update(); 
} 
public interface UpdateableSystem<S> extends System<S> { 
    S update(); 
} 

public interface Context{} 
public interface ContextState<C, S> extends UpdateableState<S> { 
    S updateWithContext(C context); 
} 
public interface ContextSystem<C, S> extends UpdateableSystem<S> { 
    S updateWithContext(C context); 
} 

public class ExampleContext implements Context{} 
public class ExampleContextState implements ContextState<ExampleContext, ExampleContextState>{ 
    @Override 
    public ExampleContextState updateWithContext(ExampleContext context) 
    { 
     return this; 
    } 

    @Override 
    public ExampleContextState update() 
    { 
     return this; 
    } 
} 
public class ExampleContextSystem implements ContextSystem<ExampleContext, ExampleContextState>{ 
    private ExampleContextState currentState; 
    public ExampleContextSystem(){ 
     currentState = new ExampleContextState(); 
    } 
    @Override 
    public ExampleContextState updateWithContext(ExampleContext context) 
    { 
     this.currentState = currentState.updateWithContext(context); 
     return currentState; 
    } 

    @Override 
    public ExampleContextState update() 
    { 
     this.currentState = currentState.update(); 
     return currentState; 
    } 

    @Override 
    public ExampleContextState currentState() 
    { 
     return currentState; 
    } 

} 

、私はインターフェイス宣言が大幅が減少していると思います。これまでのすべてのものは以前と同じようにスムーズに動作します。問題は、誰かが行くなどダム何かを...

public class BadExampleContextState implements ContextState<Integer, String>{ 
    @Override 
    public String updateWithContext(Integer context) 
    { 
     return "Garbage"; 
    } 

    @Override 
    public String update() 
    { 
     return "Garbage"; 
    } 
} 

は幸運なことに、この誤用は本当に私たちのExampleContextSystemにその方法を見つけることができませんので、我々はまだStringを割り当てるしようとして終わることができないということですExampleContextStateの参考文献に記載されていますが、これらのインタフェースがどのように使用されるかについて私たちが何らかの制御を失っていることがまだ実証されています。それでも、我々は一般的な具体的なシステムクラスを作る場合でも、...

public class GenericSystem<S extends UpdateableState<S>> implements UpdateableSystem<S>{ 
    private S currentState; 
    public GenericSystem(S startingState){ 
     this.currentState = startingState; 
    } 
    @Override 
    public S update() 
    { 
     this.currentState = currentState.update(); 
     return currentState; 
    } 

    @Override 
    public S currentState() 
    { 
     return currentState; 
    } 

} 

型システムは、TL

GenericSystem<BadExampleContextState> s = new GenericSystem<>(new BadExampleContextState()); 
// error: type argument Generics.BadExampleContextState is not within bounds of type-variable S 

...爆破から私たちを防ぐのに十分まだ強いです; DR:私は思います実行時の失敗のリスクが(もしあれば)大部分を取らずに、より控えめなインタフェース宣言を与えることで、一般的な制約を緩和することができます。