【问题标题】:Java - Multiple decorators do not workJava - 多个装饰器不起作用
【发布时间】:2012-06-29 08:15:16
【问题描述】:

我目前正在尝试使用装饰器。我创建了一个 Tank 类和两个装饰器:DoubleGunTank(射门更强大)和 FasterTank(开得更快)。他们在这里:

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;
    }   
}

我想做的是用双枪和超速来装饰一辆坦克。所以我这样做:

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,求职面试的好问题。我实际上必须运行它才能发现问题。

标签: java virtual decorator


【解决方案1】:

不太确定装饰器模式,但czlog.drive() 调用Tank.drive() 因为DoubleGunTank 不会覆盖它。

您需要重写超类中的每个方法并将其委托给包含的fTank,这样才能像您希望的那样工作。

【讨论】:

    【解决方案2】:

    所有装饰器都需要覆盖所有装饰对象的方法:

    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;
        }
    
    }
    

    我在 中使用 代理时遇到了这个问题——也通过从我的类继承来利用装饰器模式。基类构造函数被调用两次。请参阅:CGLIB proxy method calls constructor twice?Spring AOP creates extra bean

    【讨论】:

    • 谢谢。我创建了一个 TankDecorator 类,它默认覆盖所有方法为
    • 公共方法() { return fTank.method(); }。而且效果很好。
    • @user1486773:+1,好点子!你介意我把它包括在我的答案中吗?
    • @user1486773:看看,如果我理解错了,请告诉我。
    【解决方案3】:

    我发现当您将装饰器模式与具体类一起使用时,这是一个常见问题。这就是我倾向于装饰接口的原因,但是,如果您需要装饰具体的类,那么为此创建一个基类通常会有所帮助:

    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();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2017-09-28
      • 1970-01-01
      • 1970-01-01
      • 2016-03-14
      • 2020-11-09
      • 2015-09-03
      • 2018-07-12
      相关资源
      最近更新 更多