【问题标题】:Overriding methods in the Decorator design pattern (Java)装饰器设计模式 (Java) 中的重写方法
【发布时间】:2020-05-12 05:03:50
【问题描述】:

我有一个关于装饰器设计模式的问题。

我有这门课:

class SimpleShip<T extends Weapon> implements Comparable<SpaceShip>{

    private T weapon;
    private int points = 100;

    public SimpleShip(T weapon){
        this.weapon = weapon;
    }

    public int <D extends Weapon> compareTo(SpaceShip<?> other){
        return T.quality - D.quality;
    }

    public void attack(SpaceShip<?> other){
        other.gotHit(T.fire());
    }

    public void gotHit(int reduction){
        this.points -= reduction;
    }
}

我想创建两种类型的宇宙飞船(两个附加功能) - 两者都是 gotHit 方法的不同实现,我按照以下方式进行:

abstract class ShipDecorator<T extends Weapon> extends SimpleShip<T>{

    private SimpleShip<T> simpleShip;

    public ShipDecorator(SimpleShip<T> simpleShip){
        this.simpleShip = simpleShip;
    }

    @Override
    public void attack(SpaceShip<?> other){
        simpleShip.attack();
    }

    @Override
    public int <D extends Weapon> compareTo(SpaceShip<?> other){
        return simpleShip.compareTo(other);
    }

    @Override
    public void gotHit(int reduction){
        simpleShip.gotHit(reduction);
    }
}

class GuardedShip<T extends Weapon> extends ShipDecorator<T>{
    public GuardedShip(SimpleShip<T> other, T weapon){
        super(other);
    }

    @Override
    private void gotHit(int reduction){
        simpleShip.gotHit(reduction);
        Random r = new Random();
        simpleShip.gotHit(r.nextInt(1,25));
    }
}

class SneakyShip<T extends Weapon> extends ShipDecorator<T>{
    public SneakyShip(SimpleShip<T> other, T weapon){
        super(other);
    }

    @Override
    private void gotHit(int reduction){
        if(new Random().nextDouble() > 0.25){
            simpleShip.gotHit(reduction);
        }
    }
}

我的问题是 - SimpleShip API 中的所有方法都应该被装饰器覆盖吗? 我认为他们应该这样做,因为您必须将请求委托给已保存的 SimpleShip 实例,但我为这个问题提供的学校解决方案并没有覆盖它,这让我感到困惑。

【问题讨论】:

  • 它覆盖但调用了我猜的上一个方法
  • 请记住,您始终可以通过在子类中调用super 来访问扩展类。此外,您的 ShipDecorator 构造函数不会调用 SimpleShip 构造函数。请看这里:stackoverflow.com/questions/2056097/…
  • 我想问的是我的抽象类是否还可以——我是否已经重写了所有的方法(而不仅仅是那些我想要装饰的方法)
  • @PhysicsPrincess 我的建议是将您的代码放入(在线)IDE 并使其运行 - 在您拥有可编译和可执行的代码之后,您可以再次考虑装饰器模式。您似乎遗漏了一些在 Java 中正确执行所需的概念。
  • 抱歉,我已经查看了我的评论(第一条评论),但没有意义,因此我将其删除。我目前不同意这两个答案中的任何一个。第一个模仿了我的担忧,我已经撤回了,第二次调用 super 方法对我来说没有意义。我确实同意,如果隐式地调用其他超级方法是没有意义的。

标签: java oop decorator


【解决方案1】:

装饰器模式应该在您突出显示以向船舶添加附加功能时,因为核心功能已经存在于基本船舶中。这个SimpleShip 总是带有这些功能。

您对装饰器模式的看法大部分是正确的,但是当您装饰船时,您必须保留核心功能以及其他功能。这意味着gotHit 应该做SimpleShip 做的事情+ GuardedShip 做的其他事情。

那么,就代码而言,这意味着什么?

在你装饰的船上做其他事情之前,你应该打电话给原始的gotHit()

class SneakyShip<T extends Weapon> extends ShipDecorator<T>{
    public SneakyShip(SimpleShip<T> other, T weapon){
        super(other);
    }

    @Override
    private void gotHit(int reduction){
        super.gotHit(reduction); // core behaviour of every ship
        // additional feature of guarded ships
        if(new Random().nextDouble() > 0.25){
            // more damage
        }
    }
}

您在代码中所做的是完全改变有关 gotHit 行为的船舶行为。

这里有一些references

【讨论】:

    【解决方案2】:

    我不会说它必须覆盖SimpleShip的方法,如果它实际上没有在覆盖的方法中做任何事情并且它等于

    @Override
    public void method() {
      super.method();
    }
    

    那么你只会生成更多的代码行。如果你想装饰任何东西,那么你显然需要覆盖。

    重用你的例子,虽然有点奇怪:

    class SneakyShip<T extends Weapon> extends ShipDecorator<T>{
        public SneakyShip(SimpleShip<T> other, T weapon){
            super(other);
        }
    
        // This is fine, it adds functionality.
        @Override
        private void gotHit(int reduction){
            super.gotHit(reduction); // You forgot to call the methods of the superclasses.
            if(new Random().nextDouble() > 0.25){
                simpleShip.gotHit(reduction);
            }
        }
    
        // This is unnecessary, because it is implicitly done.
        @Override
        public void attack(SpaceShip<?> other){
            super.attack(other);
        }
    }
    

    如果给你的编程语言不会自动调用被覆盖的方法而不明确告诉它,你将被要求这样做。

    有关更多信息,请参阅四人组的《设计模式》一书。

    【讨论】:

    • 但是我认为 super.method() 不会有帮助..您需要使用 simpleShip.method().. 将请求委托给装饰对象,还是我错了?跨度>
    • 你需要调用超类的方法,所以super.method()在你做任何实际的装饰之前,因为否则你会忽略所有先前方法调用的结果。但是,如果您不添加任何功能,则不必覆盖它。在您的船示例中,如果您要添加一些函数来绘制船或其他东西,则不需要覆盖此中的 gotHit() 方法,比如说 PaintDecorator,因为您不会在船被击中时改变它的特性(忽略掉油漆或由于一层油漆而更加硬化)。
    • 我的抽象类还好吗?应该这样实现吗?
    • 如果 SimpleShip other 应该是对被覆盖的 SimpleShip 实例的引用,那么不是。您不需要显式引用它,您可以使用super 来引用它。此外,您不需要在ShipDecorator 中调用超类的构造函数SimpleShip,您需要这样做。请参阅我对您对此问题的评论。
    猜你喜欢
    • 1970-01-01
    • 2020-05-13
    • 2010-10-05
    • 1970-01-01
    • 2012-01-28
    • 1970-01-01
    • 1970-01-01
    • 2019-10-28
    • 2013-11-28
    相关资源
    最近更新 更多