2

我试图想出一种非常巧妙的方法来改变现有的课程。我将尝试用这个例子来解释我想出什么;

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

即使这确实有效,它似乎真的很混乱......有没有人有任何建议?

4

2 回答 2

0

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

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

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

于 2013-09-08T16:52:23.963 回答
0

处理此问题的一种方法是将所有内部操作推迟到一个单独的,也许是内部实用程序类,并为包装类提供一种方法来替换实用程序类的实现。注意:此示例需要任何具体的非包装类来实现实用程序类。包装类可能会或可能不会选择包装实用程序类。这里的关键是基础(抽象)类中实用程序类的 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
于 2013-09-08T19:42:43.053 回答