2

私はこれまで何度もこの設計上の問題に遭遇し、常に次の方法で解決してきました。しかし、これが正しい方法なのか、それとももっと良い方法があるのか​​と思っていました。言語はJavaですが、この問題は他の強い型のシステムでも発生すると思います。

問題は次のようになります。

abstract class P
{
   int p1; int p2;
}

class P1 extends P {
   int p3; int p4;
}

class P2 extends P {
   int p5; int p6;
}

abstract class PHandler {
    void handle(P p)
}

class P1Handler{
    void handle(P p) {
          P1 castedP = (P1) p;
          //.....handle logic with P1
    }
}

class P2Handler{
    void handle(P p) {
          P2 castedP = (P2) p;
          //.....handle logic with P2
    }
}

final class PHandlerFactory {
    PHandler getPhandler(P p) {
       //test on P , if/else on either InstanceOf or some attribute of P
       return concrete P
    }
}

// client code

P p = getPFromSomewhereAtRuntime();
PHandler pHandler = factory.get(p);
pHandler.handle(p);

さて、このコードは私に完全な満足を与えることはありませんでした。

第一に、私は具体的なPHandlerでのキャストが好きではありません。具体的なPHandlerは、コンパイル時に処理したいタイプを認識しています。なぜ実行時まで待つのですか?(これは、ダブルディスパッチなどの手法で回避できるJava言語の制限ですか?頭を悩ませることはできませんでした)

第二に、工場はOPEN-CLOSED-PRINCIPLEをスミレします。私は通常、OCPを回避するために、リフレクション/プロパティファイルによって実装します。編集:Pのインスタンスをさらに追加し続けると、ファクトリを変更し続ける必要があります(リフレクションを使用しない場合)

また、ConcretePの実装に注釈を使用しました。繰り返しになりますが、それはOCPをスミレにし、さらに悪いことに、Pは異なる目的を持つ異なるタイプのハンドラーを持つことができます。

私は本当にこれについて専門家の見解を知りたいです。繰り返しになりますが、このコードは主にSpringコンテナーで実行されるため、他のAOPソリューションにも興味があります。

4

2 に答える 2

2

@ bmorris591が言ったように、「ジェネリックはそれを片付ける方法です」。しかし、それはあなたの例がそのようにそれをするのに十分単純だからかもしれません。

それは本当に文脈に依存します。コードの書き方は、ビジターパターンを思い出させてくれます。それがダブルディスパッチを行う方法です。いくつかの長所と短所があります。最大の欠点は、コードが複雑になる可能性があることです。

状況によっては、PをPHandlerが次のように持つロジックのインターフェースにすることができます。

interface P
{
   void handle();
}

class P1 implements P {
   int p3; int p4;
   void handle () {...}
}

class P2 implements P {
   int p5; int p6;
   void handle () {...}
}

繰り返しますが、これには長所と短所があります。OOの観点からは、ハンドル実装をP実装内に配置することは意味がない場合があります。それはすべてコンテキストに関するものです。

于 2013-03-26T01:10:48.563 に答える
1

の具体的な実装を知っている場合P、つまり、どこかから提供されていない場合は、ビジターパターンを使用できます。

abstract class P {

    int p1;
    int p2;

    abstract void visit(final PVisitor visitor);
}

class P1 extends P {

    int p3;
    int p4;

    @Override
    void visit(PVisitor visitor) {
        visitor.doStuff(this);
    }
}

class P2 extends P {

    int p5;
    int p6;

    @Override
    void visit(PVisitor visitor) {
        visitor.doStuff(this);
    }
}

interface PVisitor {

    void doStuff(P1 p);

    void doStuff(P2 p);
}

したがってCollection<PVisitor>、ビジターロジックがあり、sをP使用してインスタンスにアクセスするPVisitorと、ポリモーフィズムによって何が何であるかがわかります。

編集

OPのコメントに続いて、私はアイデアを思いつきました。

ビジターパターンといくつかのジェネリックスを組み合わせた抽象的なファクトリパターンについてはどうでしょうか。

新しいタイプを追加する場合はHandler、何かを変更する必要があることを私たちは知っているという考え方です。論理的には、それはHandlerFactory実装であり、Psではありません。abstract HandlerFactoryがあり、各P実装がそれを処理するインスタンスを返す責任がある場合はどうなりますか。

ファクトリには、特定のハンドラーなどを返すいくつかのメソッドがありますgetDatabaseHandler。これらHandlerは、ジェネリックHandlerクラスから継承できます。

アイデアは次のようになります。

abstract class P {

    int p1;
    int p2;

    public abstract HandlerFactory<? extends P> getHandlerFactory();
}

class P1 extends P {

    int p3;
    int p4;

    @Override
    public HandlerFactory<P1> getHandlerFactory() {
        return new P1HandlerFactory(this);
    }
}

class P2 extends P {

    int p5;
    int p6;

    @Override
    public HandlerFactory<? extends P> getHandlerFactory() {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }
}

abstract class HandlerFactory<T extends P> {

    private T t;

    public HandlerFactory(T t) {
        this.t = t;
    }

    public T getT() {
        return t;
    }

    public abstract DatabaseHandler<T> getDatabaseHandler();

    public abstract JMSHandler<T> getJMSHandler();
}

abstract class Handler<T extends P> {

    private final T t;

    public Handler(T t) {
        this.t = t;
    }

    public T getT() {
        return t;
    }
}

abstract class DatabaseHandler<T extends P> extends Handler<T> {

    public DatabaseHandler(T t) {
        super(t);
    }

    public abstract void persist(Connection con);
}

abstract class JMSHandler<T extends P> extends Handler<T> {

    public JMSHandler(T t) {
        super(t);
    }

    public abstract void send();
}

class P1HandlerFactory extends HandlerFactory<P1> {

    public P1HandlerFactory(P1 t) {
        super(t);
    }

    @Override
    public DatabaseHandler<P1> getDatabaseHandler() {
        return new P1DatabaseHandler(getT());
    }

    @Override
    public JMSHandler<P1> getJMSHandler() {
        return new P1JMSHandler(getT());
    }
}

class P1DatabaseHandler extends DatabaseHandler<P1> {

    public P1DatabaseHandler(P1 p1) {
        super(p1);
    }

    @Override
    public void persist(Connection con) {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }
}

class P1JMSHandler extends JMSHandler<P1> {

    public P1JMSHandler(P1 p1) {
        super(p1);
    }

    @Override
    public void send() {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }
}

したがって、の各実装にPはコンパニオンが付属しておりHandlerFactory、の新しい実装を追加するには、の実装Pのみが必要HandlerFactoryです。ジェネリックスを使用すると、たとえば、P1A変更が必要ない場合は、P1Bから継承しP1てaを使用できます。P1Handler

新しいタイプHandlerが追加された場合、そのタイプを取得するためのメソッドを持つようにすべてのHandlerFactory実装を変更する必要がありますHandlerが、Psを変更する必要はありません。

使用法は次のようになります

final P1 p1 = new P1();        
final DatabaseHandler<P1> databaseHandler = p1.getHandlerFactory().getDatabaseHandler();

したがって、そのインスタンスからインスタンスの特定のデータベースハンドラーを取得できます。

于 2013-03-26T01:18:59.007 に答える