web-dev-qa-db-ja.com

ファクトリパターンでのReflectionの使用

ファクトリパターンでリフレクションを使用することは良い習慣ですか?

public class MyObjectFactory{
private Party party;

public Party getObject(String fullyqualifiedPath)
{
  Class c = Class.forName(fullyqualifiedPath);
  party = (PersonalParty)c.newInstance();
  return party;
}
}

PersonalPartyはPartyを実装します

16
Ullas

ファクトリパターンの目的は、消費するオブジェクトの実行時タイプから一部のコードを切り離すことです。

_// This code doesn't need to know that the factory is returning
// an object of type `com.example.parties.SurpriseParty`
AbstractParty myParty = new PartyFactory().create(...);
_

このようなコードを使用すると、PartyFactoryは、使用するランタイムタイプを正確に決定または把握する責任があります。

必要なクラスの完全修飾名を渡すことで、そのメリットを放棄します。これはどのように...

_// This code obviously DOES know that the factory is returning
// an object of type `com.example.parties.SurpriseParty`.
// Now only the compiler doesn't know or enforce that relationship.
AbstractParty myParty = new PartyFactory().create("com.example.parties.SurpriseParty");
_

...単にmyPartyを_com.example.parties.SurpriseParty_型であると宣言することと何か違いはありますか?結局、コードは同じように結合されますが、静的型の検証をあきらめました。つまり、Java強く型付けされていることの利点のいくつかを放棄している間は、まったく利益がないということです。_com.example.parties.SurpriseParty_を削除しても、コードはコンパイルされますが、IDEはエラーメッセージを表示せず、実行時までこのコードと_com.example.parties.SurpriseParty_の間に関係があったことに気付かないでしょう-それは悪いことです。

少なくとも、メソッドの引数が完全修飾名ではなく単純なクラス名になるように、少なくともこのコードを変更することをお勧めします。

_// I took the liberty of renaming this class and it's only method
public class MyPartyFactory{

    public Party create(String name)
    {
      //TODO: sanitize `name` - check it contains no `.` characters
      Class c = Class.forName("com.example.parties."+name);
      // I'm going to take for granted that I don't have to explain how or why `party` shouldn't be an instance variable.
      Party party = (PersonalParty)c.newInstance();
      return party;
    }
}
_

次へ:Class.forName(...)を使用するのは悪い習慣ですか?これは、代替手段が何であるか、およびそれらのString引数(name)とこのファクトリが提供するクラスとの関係によって異なります。代替案が大きな条件付きの場合:

_if("SurpriseParty".equals(name) {
    return new com.example.parties.SurpriseParty();
}
else if("GoodbyeParty".equals(name)) {
    return new com.example.parties.GoodbyeParty();
}
else if("PartyOfFive".equals(name)) {
    return new com.example.parties.PartyOfFive();
}
else if(/* ... */) {
    // ...
}
// etc, etc etc
_

...それはスケーラブルではありません。このファクトリが作成するランタイムタイプの名前とname引数の値の間には明らかな観察可能な関係があるため、代わりに_Class.forName_の使用を検討する必要があります。これにより、システムに新しいFactory型を追加するたびに、Partyオブジェクトをコード変更する必要がなくなります。


あなたが考えることができる他の何かは、代わりに AbstractFactory パターンを使用することです。消費するコードが次のようになっている場合:

_AbstractParty sParty = new PartyFactory().create("SurpriseParty");
AbstractParty gbParty = new PartyFactory().create("GoodByeParty");
_

...要求される頻繁に発生するパーティーの種類が限られている場合は、それらのさまざまな種類のパーティーに対してさまざまな方法を検討する必要があります。

_public class PartyFactory {

    public Party getSurpriseParty() { ... }
    public Party getGoodByeParty() { ... }

}
_

...これにより、Javaの静的型付けを活用できるようになります。

ただし、このソリューションは、新しいタイプのPartyを追加するたびに、ファクトリオブジェクトを変更する必要があることを意味します。したがって、反射ソリューションとAbstractFactoryのどちらが優れたソリューションであるかは、実際にはPartyタイプを追加する頻度と速度。毎日新しいタイプ?リフレクションを使用します。 10年ごとに新しいパーティータイプ? AbstractFactoryを使用します。

13

このようにリフレクションを使用すると(Class.forName)、ほとんどの場合、アプリケーションの設計が不適切であることを示します。たとえば、外部ライブラリまたはプラグインのある種の動的ロードを実行している場合など、使用に問題がない種類がいくつかあります。

1
Martin Perry

APIと、ユーザーがプラグインのクラス名を追加できるXML構成ファイルを提供するAPIに使用できます。次に、はい、これを使用できます

1