通过包装类重写类方法

时间:2013-09-08 15:24:38

标签: c# inheritance architecture polymorphism

我正试图想出一个非常简洁的方法来改变现有的课程。我将尝试用这个例子解释我的想法;

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

即使这确实有效,但它似乎非常混乱......有人有任何建议吗?

2 个答案:

答案 0 :(得分:0)

我认为你把作文与继承混为一谈。

当您在AlteredX对象上调用x.X()时,该对象会调用它的基础对象(WrappedX)的X方法。基础对象本身调用它已经包装的DefaultX类型的对象。现在,在DefaultX(_orig)的对象上调用Y方法。你希望_orig知道调用者的调用者有一些覆盖的东西!但是怎么样?

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

答案 1 :(得分:0)

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

注意:这非常复杂,我会避免这样做。如果可能,请使用标准装饰器,并仅依赖包装类的公共接口方法。此外,实用程序类不必是内部类。它们可以通过构造函数注入,这可能会使它更清洁。然后你也会明确地在实用程序上使用Decorator模式。

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