装饰混凝土class,不好的做法?

Decorating a concrete class, bad practice?

装饰器设计模式的典型 uml 图如下:https://www.dofactory.com/net/decorator-design-pattern

它们显示装饰器围绕着一个界面。

问题是,它是界面还是具体class真的重要吗?

首先,装饰器不应该挑剔包装对象的行为,它只是一层功能。所以我看不出这比使用界面有什么风险。

通常我会(喜欢)这样装饰:

class Foo{
    public void doSmth(){
    }
}
class LoggedFoo extends Foo{
    private Foo wrappedFoo;
    public LoggedFoo(Foo foo){wrappedFoo = foo;}
    @override
    public void doSmth(){
        System.out.println("doSmth start");
        wrappedFoo.doSmth();
        System.out.println("doSmth end");
    }
}

我认为我们不应该只看到具体的 class 继承,因为 "it's dangerous"。 所以,界面还是具体 class,这里 重要吗?

您应该使用接口,因为这是更好的做法。它们更容易测试和更改实现。如果您想在生产中使用不同类型的记录器(数据库、文件、控制台等)怎么办?

这很重要,因为 Decorators 的整个想法是在不采用实现继承路线的情况下添加功能。如果您接受实现继承并冒着改变对象现有行为的风险,那么根本不需要装饰器。

整个想法是在不破坏实现继承所做的现有行为的情况下扩展功能。

扩展 class 可能会产生意想不到的副作用。

如果 Foo 已经有一个(或多个)构造函数,您也必须实现它们并调用超级 class。

此外,如果 Foo 在构造函数中有私有状态或副作用,现在会执行 两次 。一次用于在构造函数中传递的 Foo,一次用于 LoggedFoo,它也是一个 Foo。

它可能会变得混乱并导致错误和资源消耗增加,但这完全取决于 class。如果 class 表现得像一个接口(默认构造函数,无副作用,无状态),它可能不会直接成为问题。但在这一点上,它只是一个 subclass 调用另一个实例,这可能是一个方便的构造,但我不会使用装饰器模式调用它。