【问题标题】:Design pattern for overcoming the reverse constructor order?克服反向构造函数顺序的设计模式?
【发布时间】:2019-02-22 05:34:46
【问题描述】:

我有以下问题:

基类希望接收一些数据,但数据是由派生类构造函数初始化的,在 C# 中,派生类构造函数在调用基构造函数之后调用。

上下文/我要解决的问题:

我们将基类称为Track,它的作用是构建一个表示视频游戏轨道的网格。

派生类,例如Track1 每个从特定文件格式获取轨道数据,有显着差异,禁止在基类 Track 中实现整个代码。

Track 的主要工作是抽象从派生类传入的数据,为此它具有派生类必须实现的抽象成员,例如int GetVertexCount, Vector3 GetVertex(int).

想一想它是一个可以从不同格式加载的IPicture 接口,例如BMP、JPEG,并将整个事物作为抽象返回。

我面临的问题:

在 C# 中,基类构造函数在派生类构造函数之前调用,但是我必须在 派生 类构造函数中初始化一些东西,然后我必须将其传递给 base 类构造函数。在我这样做的同时,我希望成员是不可变的,即readonly

问题:

如何先在派生类构造函数中运行一些代码,以便将结果传递给基构造函数?

答案:

按照 @Kit 的回答,这就是我最终的做法,这很好:

具有讽刺意味的是,它最终变成了一个类似 C 的 API :)

【问题讨论】:

  • 您可以在Base 类中创建abstractvirtual 方法并在_s = s.ToLower() 之前邀请它,然后在Derived 中覆盖该方法。
  • 对不起,我不够清楚并更新了我的问题,虽然你的方法确实很有趣,但我无法从该方法初始化 readonly 字段。
  • 为什么需要处理来自ctor的_o,只需将处理移到该方法中并作为返回值返回并在Base中使用...
  • 听起来像XY Problem。你到底想做什么?
  • 这些类不应彼此共享层次结构。加载轨道的东西本身并不是轨道。它应该产生一个轨道,而不是被加载的轨道。

标签: c# design-patterns constructor


【解决方案1】:

假设您不需要派生类的实例来执行所需的逻辑,您可以在调用基构造函数之前从派生构造函数调用静态方法。

这是一个简单的例子

public class Base
{
     protected Base(SomeType data)
     {
         // base logic using data
     }
}

public class DerivedOne : Base
{
    public DerivedOne(int some, string data) : base(DerivedLogic(some, data))
    {
        ...
    }

    private static SomeType DerivedLogic(int some, string data) => ...
}

public class DerivedTwo : Base
{
    public DerivedTwo (string moreStuff) : base(DerivedLogic(moreStuff))
    {
        ...
    }

    private static SomeType DerivedLogic(string moreStuff) => ...
}

这按以下顺序运行:

  1. 静态方法DerivedLogic
  2. 基类构造函数(使用来自DerivedLogic的值)
  3. 派生构造函数

现在,这有点奇怪。可能更好的是派生逻辑根本不是派生类的一部分。我是什么意思?我的意思是你有第三个类被传递到派生构造函数,然后传递到基构造函数。这会给你同样的效果。

public class Base
{
     protected Base(SomeOtherType dataWrapper)
     {
         var data = dataWrapper.DerivedLogic();
         // base logic using data
     }
}

public class DerivedOne : Base
{
    public DerivedOne(SomeOtherType otherType) : base(otherType)
    {
        ...
    }
}

或者在调用任何构造函数之前在某处计算SomeType,然后将其传入。这些方法中的任何一种都是更好的设计,因为它遵循SRP

  1. 基类负责它的工作。
  2. 构建轨道的逻辑只有一个责任。
  3. 派生类有它的单一职责。

【讨论】:

  • 谢谢,我得到了以下结果:protected Track(ITrackBuilder builder) 然后我在派生类中注入了一个适当类型的新实例。所以基本上我会得到一堆构建器,但作为回报,派生的轨道只是没有更多代码的代理。
【解决方案2】:

没有一种真正优雅的方式来完全按照您的要求进行操作,但我会质疑这是否真的有必要。在构造函数中看到逻辑通常是一种代码异味。

您可以采用许多其他方法,例如使用静态 Create() 方法。

class Derived : Base
{
    private readonly object _o;

    private Derived(object o, string s) : base(s)
    {
        _o = o;
    }

    public static Derived Create(string path)
    {
        var o = new object();// initialize from path
        var s = o.ToString(); // get s from o.
        return new Derived(o, s)
    }
}

您也可以考虑使用组合而不是继承:

class Base
{
    private readonly string _s;

    public Base(string s)
    {
        _s = s.ToLower();
    }
}

class Derived
{
    private readonly object _o;
    private readonly Base _b;

    public Derived(string path)
    {
        _o = new object();// initialize from path
        _b = new Base(_o.ToString());
    }       
}

但是,如果不知道您的实际目标和限制是什么,就很难知道这些方法中的哪一种可能是合适的。你告诉我们你想如何解决你的问题,而不是你想解决什么问题

【讨论】:

  • 刚刚更新了我的问题来详细解释问题。
猜你喜欢
  • 2014-10-12
  • 2023-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多