【问题标题】:Replacing inheritance with composition in generic classes?用泛型类中的组合替换继承?
【发布时间】:2020-06-16 22:24:59
【问题描述】:

我已经阅读了很多关于组合的内容,并试图弄清楚如何使用组合重构我的继承树。目前我的班级是这样的:

public abstract class BaseClass
{
    public abstract string displayText { get; }

    public abstract List<Parameter> parameters { get; }

    public abstract void FireEvent();
}

public abstract class SubClass<T> : BaseClass
{
    private string _displayText;
    public override string displayText { get { return _displayText; } }

    private List<Parameter> _parameters;
    public override List<Parameter> parameters { get { return _parameters; } }

    private T _value;    // ADDED TO SUBCLASS
    public abstract Event<T> Evt { get; } // ADDED TO SUBCLASS

    public override void FireEvent()
    {
        Evt.Raise(_value);
    }
}

public class IntClass : SubClass<int>{}
public class StringClass : SubClass<string>{} // more subclasses like this

据我了解,这里既有继承又有组合。

子类Has-A:(组成)

  1. Parameters列表
  2. Event 的字段
  3. Event&lt;T&gt; 的行为在它自己的 FireEvent 方法中调用

子类Is-A:基类(继承)

IntClass/StringClass Is-A:子类和基类

创建BaseClass 的原因是因为我需要多态列表。这样我可以创建一个List&lt;BaseClass&gt; 并在列表中的每个元素上调用FireEvent() 并在循环中访问displayTextList&lt;Parameter&gt;

将来我需要一个没有Event 的子类和一个接受FireEvent() 参数的子类。可能会出现其他变体。

如何用基于组合的方法完全替换我当前的结构?它甚至可行吗?

【问题讨论】:

  • 您为什么要这样做?所以你不会破坏 Liskov?
  • 这不是更适合codereview.stackexchange.com吗?
  • 如果你继承了一个类,那么它就是继承,当你将不同的类存储为字段或属性时,它就是组合。您应该根据自己的用例使用其中一个或两个,而不是一个接一个。
  • 你看不到组合的好处。因为您只有 1 个行为(FireEvent())。假设你在 BaseClass 中有 5 个方法,在 SubClass 中有 10 个方法。然后你的 IntCass 和 StringClass 从上面继承了 15 个方法,但是 IntClass 只使用了 7 个,而 StringClass 只使用了 2 个。现在想象一下在你的 BaseClass 中更改 1 个方法,你不知道你是否有任何东西。这就是你想把你的 15 种方法放入服务的地方(可以是 1 个服务或多个服务)
  • @Alex-TinLe 是的,这正是我所关心的。我将在未来添加新的行为。我只是不确定如何为未来的灵活性重构它。您能否详细说明服务以及它的外观?

标签: c# oop generics inheritance composition


【解决方案1】:

在此示例中,您有 3 个行为。 Int 用了 3,String 只用了 2。如果你这样放代码,当你更改 AnotherServiceB 时,你不必担心它会破坏 String(因为 String 没有那个服务)

public class FireEventService<T>
{
    private T _value;    // ADDED TO SUBCLASS
    public abstract Event<T> Evt { get; } // ADDED TO SUBCLASS

    public override void FireEvent()
    {
        Evt.Raise(_value);
    }
}

public class AnotherService
{

}

public class AnotherServiceB
{

}

public abstract class SubClass<T> : BaseClass
{
    private readonly FireEventService<T> _fireEventService;
    private readonly AnotherService _anotherService;

    private string _displayText;
    public override string displayText { get { return _displayText; } }

    private List<Parameter> _parameters;
    public override List<Parameter> parameters { get { return _parameters; } }

    public SubClass(FireEventService<T> fireEventService, AnotherService anotherService)
    {
        // All those services should use with interface and Dependency Injection
        _fireEventService = fireEventService;
        _anotherService = anotherService;
    }

    public void FireEvent() => _fireEventService.FireEvent();
}

public class IntClass : SubClass<int>
{
    private readonly AnotherServiceB _anotherServiceB;
    public IntClass(FireEventService<int> fireEventService, AnotherService anotherService, AnotherServiceB anotherServiceB)
        : base(fireEventService, anotherService)
    { }
    public void DoSomethingUsingServiceB()
    {
        //_anotherServiceB.DoSomething();
    }
}
public class StringClass : SubClass<string> 
{
    public StringClass(FireEventService<string> fireEventService, AnotherService anotherService)
        : base(fireEventService, anotherService)
    { }
}

【讨论】:

  • 在这种情况下多态列表将如何工作。如果我创建一个 List 并在其中放入一个 IntClass 和 StringClass ,则在遍历此列表时,我需要检查某种行为是否可用,对吗?我将如何进行这项检查?
  • 我认为你误解了多态性。该组合不能帮助您进行多态性。但是接口可以。
  • 是的,我仍然围绕着这些概念。所以我需要使用接口和组合来实现我想要的?
猜你喜欢
  • 1970-01-01
  • 2018-09-27
  • 2015-09-15
  • 2018-08-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-05
  • 1970-01-01
相关资源
最近更新 更多