通过包装类重写类方法

本文关键字:类方法 重写 包装类 | 更新日期: 2023-09-27 18:30:39

我正在尝试想出一种非常巧妙的方法来更改现有类。我将尝试使用此示例解释我想出的内容;

abstract class AbstractX
{
    public abstract string X();
    protected internal abstract int Y();
}
// Execute all methods on another instance of AbstractX
// This is why the method(s) are 'protected *internal*'
class WrappedX : AbstractX
{
    AbstractX _orig;
    public WrappedX(AbstractX orig)
    {
        _orig = orig;
    }
    public override string X()
    {
        return _orig.X();
    }
    protected internal override int Y()
    {
        return _orig.Y();
    }
}
// The AbstractX implementation I start with
class DefaultX : AbstractX
{
    public override string X()
    {
        // do stuff
        // call Y, note that this would never call Y in WrappedX
        var y = Y();
        return y.ToString();
    }
    protected internal override int Y()
    {
        return 1;
    }
}
// The AbstractX implementation that should be able to alter *any* other AbstractX class
class AlteredX : WrappedX
{
    public AlteredX(AbstractX orig)
        :base(orig)
    {
    }
    protected internal override int Y()
    {
        Console.WriteLine("Sweet, this can be added to any AbstractX instance!");
        return base.Y();
    }
}

对,所以我打算使用它的方式是;

AbstractX x = new DefaultX();
x = new AlteredX(x);
Console.WriteLine(x.X()); // Should output 2 lines

或者暂时离开抽象的例子,让它更具体(应该是不言自明的);

FileWriterAbstract writer = new FileWriterDefault("path/to/file.ext");
writer = new FileWriterSplit(writer, "100MB");
writer = new FileWriterLogged(writer, "path/to/log.log");
writer.Write("Hello");

但是(回到抽象的例子)这是行不通的。调用AlteredX.X()的那一刻(未被覆盖)它进入WrappedX.X(),当然运行DefaultX.X()使用它自己的Y()方法,而不是我在AlteredX.中定义的方法,它甚至不知道它的存在。

我希望很明显为什么

我希望它起作用,但我会进一步解释以确保;

如果我不使用WrappedX来创建AlteredX, AlteredX将无法"应用"到任何AbstractX实例,从而使上述FileWriter之类的事情变得不可能。而不是;

FileWriterAbstract
FileWriterDefault : FileWriterAbstract
FileWriterWrap : FileWriterAbstract
FileWriterSplit : FileWriterWrap
FileWriterLogged : FileWriterWrap

它将成为;

FileWriterAbstract
FileWriterDefault : FileWriterAbstract
FileWriterSplit : FileWriterDefault
// Implement Logged twice because we may want to use it with or without Split
FileWriterLogged : FileWriterDefault
FileWriterLoggedSplit : FileWriterSplit

如果我然后创建了一个新的,我将不得不实现它 4 次,因为我希望它可以使用;

Default
Split
Logged
Split+Logged

等等...

因此,考虑到这一点,实现这一目标的最佳方法是什么?我能想到的最好的(未经测试的)是;

class DefaultX : AbstractX
{
    protected internal override Func<string> xf { get; set; }
    protected internal override Func<int> yf { get; set; }
    public DefaultX()
    {
        xf = XDefault;
        yf = YDefault;
    }
    public override string X()
    {
        return xf();
    }
    protected override int Y()
    {
        return yf();
    }
    string XDefault()
    {
        var y = Y();
        return y.ToString();
    }
    int YDefault()
    {
        return 1;
    }
}
class AlteredX : WrappedX
{
    Func<int> _yfOrig { get; set; }
    public AlteredX()
    {
        // I'm assuming this class member doesn't get overwritten when I set
        // base.yf in the line below.
        _yfOrig = base.yf;
        base.yf = YAltered;
    }
    private int YAltered()
    {
        Console.WriteLine("Sweet, this can be added to any AbstractX instance!");
        return yfOrig();
    }
}

即使这确实有效,它似乎真的很混乱......有人有什么建议吗?

通过包装类重写类方法

处理此问题的一种方法是将所有内部操作推迟到一个单独的内部实用程序类,并为包装类提供一种方法来替换实用程序类的实现。 注意:此示例需要任何具体的非包装类来实现实用程序类。 包装类可能会也可能不会选择包装实用程序类。 这里的关键是基(抽象)类中实用程序类的 getter/setter 不允许它被覆盖,因此每个继承类都使用由其构造函数定义的实用程序类。 如果它选择不创建自己的实用程序,则默认为要包装的类的实用程序 - 如果需要,最终会一直返回到具体的、未包装的复合根类。

注意:这非常复杂,我会避免这样做。 如果可能,请使用标准装饰器,并且仅依赖于包装类的公共接口方法。 此外,实用程序类不必是内部类。 它们可以通过构造函数注入,这可能会使其更干净一些。 然后,您还将在实用程序上显式使用装饰器模式。

public interface IFoo
{
    string X();
}
public abstract class AbstractFoo : IFoo
{
    public abstract string X();
    protected internal Footilities Utilities { get; set; }
    protected internal abstract class Footilities
    {
        public abstract int Y();
    }
}
public class DefaultFoo : AbstractFoo
{
    public DefaultFoo()
    {
        Utilities = new DefaultFootilities();
    }
    public override string X()
    {
        var y = Utilities.Y();
        return y.ToString();
    }
    protected internal class DefaultFootilities : Footilities
    {
        public override int Y()
        {
            return 1;
        }
    }
}
public abstract class AbstractWrappedFoo : AbstractFoo
{
    protected readonly AbstractFoo Foo;
    public AbstractWrappedFoo(AbstractFoo foo)
    {
        Foo = foo;
    }
    public override string X()
    {
        return Foo.X();
    }
}
public class LoggedFoo : AbstractWrappedFoo
{
    public LoggedFoo(AbstractFoo foo)
        : base(foo)
    {
        Foo.Utilities = new LoggedUtilities(Foo.Utilities);
    }
    public override string X()
    {
        return Foo.X();
    }

    protected internal class LoggedUtilities : Footilities
    {
        private readonly Footilities _utilities;
        public LoggedUtilities(Footilities utilities)
        {
            _utilities = utilities;
        }
        public override int Y()
        {
            Console.WriteLine("Sweet");
            return _utilities.Y();
        }
    }
}

现在,这个程序

class Program
{
    static void Main(string[] args)
    {
        AbstractFoo foo = new LoggedFoo(new DefaultFoo());
        Console.WriteLine(foo.X());
    }
}

生产

Sweet!
1

我认为您将组合与继承混为一谈。

当您在 AlteredX 对象上调用 x.X() 时,该对象会调用其基本对象 (WrappedX) 的 X 方法。基对象本身调用它已包装的 DefaultX 类型的对象。现在,在 DefaultX (_orig) 的对象上调用 Y 方法。您希望_orig知道调用方的调用方中存在覆盖的内容!但是怎么做呢?

在这个调用链中,我没有看到任何涉及覆盖方法 Y 的点。