2010-11-30 3 views
0

私はこれを設計する良い方法についていくつかの情報を得ることができるのだろうかと思います。私は私のアプローチをしますが、私はよりよい解決策があると思います(したがって質問:))。デザインヘルプ!列挙型の工場変圧器のJavaジェネリックス!

あるオブジェクトを別のオブジェクトから作成するためのアクセサを持つ列挙型(オプションをクリアし、シングルトンアーキテクチャを避けるため)を作成します。しかし、これらのオブジェクトはかなり柔軟です。

この変換のオプション数を制限する方法と考えてください。

私は少しの階層に行きましょう。

public enum ValueTransformer{ 
    VALUE_A{ 

    @Override 
    public <T> T createVo (Class<T> expectedRtn, Object obj) { 
     ValueA retObj = null; 

     if (expectedRtn == getReturnType()) { 
     if (obj != null && CanBeTranslatedToA.class == obj.getClass()) { 
      retObj = new ValueA(); 
      /*...*/ 
     } 
     } 

     return retObj; 
    } 

    @Override 
    public Class<ValueA> getReturnType() { return ValueA.class; } 

    }, 
    VALUE_B { 
    @Override 
    public Class<ValueB> getReturnType() { return ValueB.class; } 

    @Override 
    public <T> T createVo (Class<T> expectedRtn, Object obj) { 
     ValueB retObj = null; 

     if (expectedRtn == getReturnType()) { 
     if (obj != null && CanBeTranslatedToB.class == obj.getClass()) { 
      retObj = new ValueB(); 
      /*...*/ 
     } else if (obj != null && AnotherClassForB.class = obj.getClass()){ 
      retObj = new ValueB(); 
      /* ... */ 
     } 
     } 

     return retObj; 
    } 
    }; 

    public abstract <T> Class<T> getReturnType(); 
    public abstract <T> T createVo (Class<T> expectedRtn, Object obj); 
} 

これはまともなデザインです:私はこのような何かをやって考えていた

class Base {...} 
class ValueA extends Base {...} 
class ValueB extends Base {...} 

:私はこのような何かにオブジェクトの多様なセットからつもりですか?この列挙型はおそらく成長し、ValueAとValueBの作成元は変わる可能性があります(sysが成長すると)。私はこれらすべてのケースで「ベース」を返すことができますが、キャストと小切手が必要になります。私はそれを持っていないことを好むだろう。

私にはexpectedRtnパラメータが必要ですか?ジェネリックスをまったく使用すべきですか?私はかなりJavaに新しいので、私は常にこのケースを処理するための最善の方法ではないと思います。

ヒントありがとうございます。

+1

これらの列挙値がどのように使用されるかのコード例を挙げることはできますか?申し訳ありませんが、これはオーバーエンジニアリングのように見えます。 – StriplingWarrior

+0

呼び出し元は "期待される戻り値の型"を知る必要があるので、彼はそれをキャストするかもしれません。 – mhaller

+0

キャストを行う方がよいでしょうか?私はキャスティングが大きなノーノだと思った。ちょっと危険な。 私は簡単に間違っている可能性があります。このようにすると、デベロッパーは自分のしていることをまだ知っていなければなりませんが、間違っているかどうかを知ることはできません。間違ったキャストを強制することは可能でしょうか? – rybit

答えて

2

これはあまり良いデザインではありません。私は本当にこの列挙型が達成しようとしていることを伝えることさえできません。まず、のメソッドを使用しています。つまり、各列挙型の値が実装されています。つまり、メソッドの呼び出し元は、Tの型を決定しますが、メソッドは次のとおりです。実際にどのような種類のオブジェクトが返されるかについての意見があります。

Class<String> foo = ValueTransformer.VALUE_B.getReturnType(); 
String string = ValueTransformer.VALUE_A.createVo(String.class, ""); 

あなたのコードでは、上記は完全に合法ですが、あなたのコードは実際にこれを処理しません。一般的な方法は、あなたが思っていると思われることをしません。

具体的なタイプのオブジェクトをValueAまたはValueBのオブジェクトに変換する単純な方法です。

public class CanBeTranslatedToB { 
    ... 

    public ValueB toValueB() { 
    ValueB result = new ValueB(); 
    ... 
    return result; 
    } 
} 

その後、あなたはCanBeTranslatedToBのインスタンスを持っている、というよりもやっている場合:これを実行する最も簡単な方法は、単に、そのような各クラスにそれを行う方法を提供する。この方法で変換することができ、各クラスを持つことです:

CanBeTranslatedToB foo = ... 
ValueB b = ValueTransformer.VALUE_B.createVo(ValueB.class, foo); 

あなたがやるだけだろう:より明確だとエラーが発生しやすいない列挙型のバージョンのような

CanBeTranslatedToB foo = ... 
ValueB b = foo.toValueB(); 

を。

toValueA()toValueB()のメソッドを定義するインターフェイスを作成し、すべての実装で使用する必要がある共通の動作を提供するヘルパークラスを作成するなど、さまざまな作業を簡単に行うことができます。私は、あなたのような列挙型の使用方法は見当たりません。

編集:

あなたはValueBなどに変換する必要がクラスのためのコードを変更できない場合、あなたはいくつかのオプションがあります。次に、あなただけ書くことができます

// "from" would be another good name 
public static ValueB valueOf(CanBeTranslatedToB source) { 
    ... 
} 

public static ValueB valueOf(AnotherClassForB source) { 
    ... 
} 

CanBeTranslatedToB foo = ... 
ValueB b = ValueB.valueOf(foo); 

を、あなたならばそれはValueAとなどValueBにファクトリメソッドを追加することです処理するために(私の意見では、おそらく最良の)最も簡単な方法それらのメソッドをValueBに入れたくない場合は、newValueB(CanBeTranslatedToB)のようなメソッド名を持つ別のクラスでそれらのメソッドを持つことができます。

最後に、別のオプションはGuavaを使用し、変換ごとにFunctionを作成することです。これはオリジナルのデザインに最も近いものですが、タイプセーフであり、Guavaが提供するすべてのFunction-受け入れユーティリティでうまく動作します。このようにFunctionの実装をクラスにまとめることができます。ここでFooからValueBへの変換を実装するシングルトンの例です:

public static Function<Foo, ValueB> fooToValueB() { 
    return FooToValueB.INSTANCE; 
} 

private enum FooToValueB implements Function<Foo, ValueB> { 
    INSTANCE; 

    @Override public ValueB apply(Foo input) { 
    ... 
    } 
} 

しかし、私は、変換を行うための唯一方法としてこれを使用することはありません...静的を持っている方が良いだろうvalueOf上記のメソッドは、アプリケーションがオブジェクトのコレクション全体を一度にたくさん変換する必要がある場合にのみ便宜的にしか提供しません(Function)。

+0

私はそうすることを好むでしょう。しかし、問題は、私がCanBeTranslatedToBなどのようなものの内部にアクセスできないということです。 – rybit

+0

最初のケースでは、私はdef defを見ます!私はちょうどのような何かをしなければならないと思う: Base createVo(obj) それはヌルの戻り値が何を意味するかをより明確にする。 – rybit

+0

編集ありがとう! 面白いことに、ValueBオブジェクトコードを変更することはできません。( 一意の機能を持たない単純なファクトリを簡単に作成しようとしていますが、 – rybit

0

ジェネリックについては、Javaには「本物の」ジェネリックスがありません。この場合、有益でも有害でもあります。ジェネリックの使用は、コンパイル時に、あなたが扱っているオブジェクトのタイプを正確に知らないと難しいです。この情報を消費するコードが、実際にValueTransformer.ValueA.createVoの呼び出しから予想されるオブジェクトのタイプを知っている場合、正直なところ、返された値をキャストする必要があります。私は、コールがもっとこのように見えることを期待する:私はこの方法のうち、間違った種類を取得していた場合、私はむしろ、このライン上にキャスト例外を見ることが

MyTypeA myType = (MyTypeA)ValueTransformer.ValueA.createVo(sourceObject); 

(問題が本当に起こった場合)より後でヌルポインタ例外が発生する。これは正しい "フェイル・ファースト"の練習です。

本当にの場合は明示的なキャスティングが嫌いですが、私は暗黙のうちにこれらのことをキャストできるクールなトリックを見ました。しかし、それはまだ実行時にキャストを実行するため、私は本当にこの方法をお勧めしませんが、誰もがあなたの使用コードを見て、それを疑うないだろう

public abstract <T> T createVo (Object obj) {...} 

MyTypeA myType = ValueTransformer.ValueA.createVo(sourceObject); 

:私はそれがこのような何かを行くと思います。

私はあなたが達成することを望んですることができるいくつかの目標を見ることができます:

  1. は、与えられた基本クラスのすべてのオブジェクトのために行くために、単一の「真実の源」を持っています。
  2. オブジェクトをリクエストするたびに、そのオブジェクトのインスタンスを作成できるようにします。
  3. タイプセーフティを備え、実行時にキャストを避けます。

あなたは私は考えていないよ、他の要件がない限り、それは工場のように思えるが望ましいだろう。

public class ValueFactory 
{ 
    public ValueA getValueA(Object obj) {return new ValueA();} 
    public ValueB getValueB(Object obj) {return new ValueB();} 
} 

これは、上記のすべての要件を満たしています。さらに、ValueAオブジェクトを生成するために必要なオブジェクトのタイプがわかっている場合は、入力値に対してより明示的な型を使用できます。

0

私はしばらく時間を費やし、ついにあなたが探しているもののように見えるenumベースの工場を実装することができました。ここで

は、私の工場のソースコードである:ここで

import java.net.Socket; 

public enum EFactory { 
    THREAD(Thread.class) { 
     protected <T> T createObjectImpl(Class<T> type) { 
      return (T)new Thread(); 
     } 
    }, 
    SOCKET(Socket.class) { 
     protected <T> T createObjectImpl(Class<T> type) { 
      return (T)new Socket(); 
     } 
    }, 
    ; 

    private Class<?> type; 

    EFactory(Class<?> type) { 
     this.type = type; 
    } 

    protected abstract <T> T createObjectImpl(Class<T> type); 

    public <T> T createObject(Class<T> type) { 
     return assertIfWrongType(type, createObjectImpl(type)); 
    } 

    public <T> T assertIfWrongType(Class<T> type, T obj) { 
     if (!type.isAssignableFrom(obj.getClass())) { 
      throw new ClassCastException(); 
     } 
     return obj; 
    } 
} 

は、私はそれを使用する方法です。

Thread t1 = EFactory.THREAD.createObject(Thread.class); 
String s1 = EFactory.THREAD.createObject(String.class); // throws ClassCastException 

個人的に私はこの実装があまりにも嫌いです。 EnumはEnumとして定義されているため、クラスレベルでパラメータ化することはできません。これは、クラスパラメータ(私の例ではThreadとSocket)をファクトリメソッド自体に渡さなければならない理由です。また、ファクトリ実装自体に警告を生成するキャストが含まれています。しかし、他の手から、少なくともこの工場を使用するコードはきれいであり、警告を出さない。

+0

私はこのメソッドが好きです!私はlib型のものを作ろうとしています。私はそれを使用する限り、内部が複雑になることを心配していません。シンプルです。可能であれば、コンパイル時にすべてが型に安全であることを確認したい。 – rybit