1

我在这里看了很多帖子,我对这个问题的答案并不完全清楚。

我希望能够实现一个具有组合的接口,并在组件上调用所有方法:

  1. 为组件(即汽车)创建一个接口。
  2. 创建一个类,实现组件(即SimpleCar)
  3. 通过在 Component 成员上调用它们来创建另一个实现 Component 接口的类(即创建一个 DeluxeCar,它通过在 SimpleCar 成员上调用它们来实现这些方法)。

现在需要注意的是,重要的部分是我希望能够在不编写所有包装方法的情况下完成第 3 部分,以使 DeluxeCar 调用 SimpleCar 上的方法。最好只需要添加一个 C# 装饰器或一些 C# 模板魔法。

我见过的两个解决方案: 1. ExtensionMethods - 我相信您可以实现 CarComposite 接口并在其上放置扩展方法(类似于 C. Lawrence Wenham 的回答:Multiple Inheritance in C#中所做的事情)。但是,我不认为您可以让 Composite 接口也实现 Component 接口。至少我还没有得到一个可行的例子。也许我错了。2. 面向方面的编程。使用 PostSharp 或其他 AOP 框架执行第 3 步。我无法找到有关如何执行此操作的示例代码。此外,我真的很想避免使用 PostSharp,因为我不喜欢许可证并且想要一个完全免费的解决方案。我认为 PostSharp 声明你不能用他们的免费版本来做到这一点(参见 AspectInheritance http://www.)。Spring.Net 的一个工作示例将有很大帮助。

有人可以在使用标准 C# 库或 Spring.net 时提供#3 的示例吗?

更新

h.alex 的建议很有趣,但仍然不能满足我的需要。您对抽象类的使用有点限制。想象一下除了 Car 接口之外还有一个 Boat 接口。然后你想创建一个 AmphibiousVehicle 来实现 Boat 和 Car。在 C# 中没有多重继承,因此您不能同时继承 AbstractCar 和 AbstractBoat 类。您被迫添加更多样板并创建一个 AbstractAmphibiousVechicle 抽象类。如果您没有太多不同的课程,这可能会很好。但有时您想创建许多不同的类并混合和匹配这些行为。这在视频游戏中很常见。您可能希望能够对游戏对象进行大量不同的操作(推它、扔它、收集它等)。

我想要 PostSharp 拥有的类似的东西,但我真的很想知道是否有一个限制较少的框架可以做同样的事情。例如,这是否可以在 Spring.net 中轻松完成? http://doc.postsharp.net/postsharp-3.0/##PostSharp-3.0.chm/html/T_PostSharp_Aspects_CompositionAspect.htm

http://doc.postsharp.net/postsharp-3.0/Default.aspx#PostSharp-3.0.chm/html/20c81e23-af91-4688-a672-b0f3cce793a9.htm

4

1 回答 1

1

所以,我相信你在问继承。好吧,一个可以做到的香草方式是这样的:

public interface ICar
{
    void ShiftGearUp();
    void ShiftGearDown();
    void SwitchLightsOn();
    void SwitchLightsOff();
    void Brake();
    void Accelerate();
}

public class Car : ICar
{
    public virtual void ShiftGearUp() { }
    public virtual void ShiftGearDown() { }
    public virtual void SwitchLightsOn() { }
    public virtual void SwitchLightsOff() { }
    public virtual void Brake() { }
    public virtual void Accelerate() { }
}

public class DeluxeCar : Car
{
    public override void SwitchLightsOn()
    {
        //calls the implementation in the Car class.
        base.SwitchLightsOn();
        //implement custom behavior.
        this.AdjustCabinAmbientLighting();
    }

    private void AdjustCabinAmbientLighting() { }
}

通过这种方式,您只会覆盖您想要更改的行为。

但是,有一条世界著名的规则,即优先组合而不是继承

它将使我们以这种方式形成解决方案:

public interface ICar
{
    void ShiftGearUp();
    void ShiftGearDown();
    void SwitchLightsOn();
    void SwitchLightsOff();
    void Brake();
    void Accelerate();
}

public abstract class AbstractCar : ICar
{
    protected ITransmission Transmission;
    protected ILights Lights;
    protected IEngine Engine;
    protected IBrakes Brakes;

    public void ShiftGearUp()
    {
        this.Transmission.ShiftUp();
    }

    public void ShiftGearDown()
    {
        this.Transmission.ShiftDown();
    }

    public void SwitchLightsOn()
    {
        this.Lights.SwitchOn();
    }

    public void SwitchLightsOff()
    {
        this.Lights.SwitchOff();
    }

    public void Brake()
    {
        this.Brakes.Brake();
    }

    public void Accelerate()
    {
        this.Engine.Accelerate();
    }
}

public class Car : AbstractCar
{
    public Car()
    {
        this.Lights = new AcmeLights();
        //todo
        //this.Engine = init engine object;
        //this.Brakes = init brakes object;
        //this.Transmission = init transmission object;
    }
}

public class DeluxeCar : AbstractCar
{
    public DeluxeCar()
    {
        this.Lights = new FancyLights();
        //todo
        //this.Engine = init engine object;
        //this.Brakes = init brakes object;
        //this.Transmission = init transmission object;
    }
}

public interface ITransmission
{
    void ShiftUp();
    void ShiftDown();
}

public interface ILights
{
    void SwitchOn();
    void SwitchOff();
}

public interface IEngine
{
    void Accelerate();
}

public interface IBrakes
{
    void Brake();
}

public class AcmeLights : ILights
{
    private LightSwitch Switch;

    public void SwitchOn() { this.Switch.On(); }
    public void SwitchOff() { this.Switch.Off(); }
}

public class FancyLights : ILights
{
    private LightSwitch Switch;
    private CabinAmbientLightingController AmbientLightingController;

    public void SwitchOn()
    {
        this.Switch.On();
        this.AmbientLightingController.AdjustLightLevel();
    }

    public void SwitchOff()
    {
        this.Switch.Off();
        this.AmbientLightingController.AdjustLightLevel();
    }
}

public class LightSwitch
{
    public void On() { }
    public void Off() { }
}

public class CabinAmbientLightingController
{
    public void AdjustLightLevel() { }
}

编辑:然后您可以使用您选择的依赖项注入工具通过其构造函数注入 Car 和 Deluxe car 的依赖项。这可能是“AOP”。

编辑:为此目的,也许您可​​以使用动态代理。著名的教程就是这个。可能有点学习曲线(可能不是这个问题的最佳解决方案,但我不知道其他任何问题) - 但总而言之,它是一个很棒的框架和有趣的阅读。

我会做什么,请注意我的 DynProxy 有点生疏,声明一个空类,比如

public class RocketCar
{

}

然后生成一个没有目标的类代理并使用拦截器来转发调用。因此,我们应该编写一个通用的拦截器,我们可以向其中注入依赖项(就像上面两个具体的 Cars 一样)。因此,我们将只有一个这样的类,并根据需要配置和使用它。我相信这应该可以在一两天内编写代码:) 这个想法是这里的“不实现接口的类”部分。我们会让 RocketCar 实现 IRocket 和 ICar。

但!在一个类中实现多个接口并不是 imo 做事的最佳方式!您正在破坏SRP。如果我们仍然拥有所有这些接口,为什么不让客户端代码通过各个接口引用它们呢?毕竟,如果 RocketCar 实现了 ICar,那么它的 Accelerate 方法一定不会意识到它是不同汽车的一部分。换句话说,RocketCar 必须以这样一种方式运行,即我们可以在任何地方使用任何其他 ICar 实现来替换 RocketCar。否则我们会破坏LSP

因此,最好将 ICar 和 IRocket 放在单独的类中。这些的具体实现可以具有彼此的具体类型引用,这些引用对它们的交互进行建模。

我并不是说你不应该让类实现多个接口,但这应该是一个例外而不是规则。

于 2013-07-30T07:24:27.267 回答