2

私は現在、デコレータを試しています。Tank クラスと 2 つのデコレーターを作成しました。どうぞ:

public class Tank {
    public int shoot() {
        return 100;
    }

    public int drive() {
        return 10;
    }
}
public class FasterTank extends Tank {

    protected Tank fTank;

    public FasterTank(Tank tank) {
        fTank = tank;
    }

    public int drive() {
        return fTank.drive() * 2;
    }
}

public class DoubleGunTank extends Tank {

    protected Tank fTank;

    public DoubleGunTank(Tank tank) {
        fTank = tank;
    }
    public int shoot() {
        return fTank.shoot() * 2;
    }   
}

私がやろうとしているのは、1 つの戦車を 2 連装砲と超高速の両方で装飾することです。だから私はこのようにします:

Tank czolg = new Tank();
czolg = new FasterTank(czolg);
czolg = new DoubleGunTank(czolg);
System.out.println("Shoot: "+czolg.shoot());
System.out.println("Drive: "+czolg.drive());

しかし、結果は次のとおりです。

Shoot: 200
Drive: 10

DoubleGunTank クラスの両方のメソッドをアクティブにするデコレーターは 1 つだけのようです。だから私の質問は、どうすれば戦車をより強力に発射し、同時により速く走らせることができるでしょうか?

4

3 に答える 3

5

すべてのデコレータは、装飾されたすべてのオブジェクトのメソッドをオーバーライドする必要があります。

class FasterTank extends Tank {

    protected Tank fTank;

    public FasterTank(Tank tank) {
        fTank = tank;
    }

    public int drive() {
        return fTank.drive() * 2;
    }

    //crucial!  
    public int shoot() {
        return fTank.shoot();
    }
}

class DoubleGunTank extends Tank {

    protected Tank fTank;

    public DoubleGunTank(Tank tank) {
        fTank = tank;
    }
    public int shoot() {
        return fTank.shoot() * 2;
    }

    //crucial!  
    public int drive() {
        return fTank.drive();
    }

}

理由は次のとおりです。

Tank czolg = new DoubleGunTank(new FasterTank(new Tank()));

そして、czolg.drive()それを実際に呼び出すと、DoubleGunTankクラスのメソッドが呼び出されます。これは、から変更なしで継承されTankます。したがって、ターゲットの装飾されたメソッドを使用する代わりに、のfTank手付かずのメソッドを呼び出していますDoubleGunTank

Tankインターフェイスを使用することでこのような問題を回避できることに注意してください。これにより、常にすべてのメソッドを装飾する必要があります。また、ターゲットTankクラスに何らかの状態があるか、コンストラクターでいくつかの操作を実行する場合、各デコレーター(それから継承)はこの状態を複製し、コンストラクターで同じコードを呼び出します。

更新(OP自身が提案):

abstract TankDecoratorまたは、次のようにクラスを使用できます。

abstract class TankDecorator extends Tank {
    protected final Tank fTank;

    protected TankDecorator(Tank fTank) {
        this.fTank = fTank;
    }

    @Override
    public int shoot() {
        return fTank.shoot();
    }

    @Override
    public int drive() {
        return fTank.drive();
    }
}

class FasterTank extends TankDecorator {

    public FasterTank(Tank tank) {
        super(tank);
    }

    public int drive() {
        return fTank.drive() * 2;
    }

}

class DoubleGunTank extends TankDecorator {

    public DoubleGunTank(Tank tank) {
        super(tank);
    }
    public int shoot() {
        return fTank.shoot() * 2;
    }

}

プロキシを使用するときにこの問題に遭遇します。また、クラスから継承することでデコレータパターンを悪用します。基本クラスのコンストラクターは2回呼び出されます。参照:CGLIBプロキシメソッドはコンストラクターを2回呼び出しますか?そしてSpringAOPは余分なBeanを作成します

于 2012-06-27T20:04:55.747 に答える
2

Decorator パターンについてはよくわかりませんが、オーバーライドしないためczlog.drive()呼び出します。Tank.drive()DoubleGunTank

スーパークラスのすべてのメソッドをオーバーライドし、fTankこれを必要なように機能させるために含まれているメソッドに委譲する必要があります。

于 2012-06-27T20:03:38.973 に答える
0

具象クラスで Decorator パターンを使用している場合、これが一般的な問題であることがわかりました。そのため、インターフェイスを装飾する傾向がありますが、具象クラスを装飾する必要がある場合は、通常、このための基本クラスを用意すると便利です。

public class DecoratedTank extends Tank {
   private Tank delegate;

   public DecoratedTank(Tank delegate) {
       this.delegate = delegate;
   }

   @Override
   public int shoot() {
       return delegate.shoot();
   }

   @Override
   public int drive() {
       return delegate.drive();
   }
}

次に、ダブルガンタンクを実行します。

public class DoubleGunTank extends DecoratedTank {
    public DoubleGunTank(Tank delegate) {
        super(delegate);
    }

    @Override
    public int shoot() {
        return 2 * super.shoot();
    }
}
于 2012-06-27T22:47:44.160 に答える