构造对象的通用方法

Generic way of constructing objects

我有两个 classes:

class DoorRequest : IRequest
{
   string message;
}

class WindowRequest : IRequest
{
  string message;
}

它们并不相互继承,而是实现了一个共同的接口。 我想要一种通用的方法来创建这些 classes 的实例。语义上等同于:

public static T MakeOpenRequest<T>() where T : IRequest
{
  // If T is DoorRequest return new DoorRequest{message="please open the door"}
  // If T is WindowRequest return new WindowRequest{message="please open the window"}
}

对我来说重要的是函数的结果是 T,而不是 IRequest。我可以扩展 DoorRequestWindowRequest 的功能以提供实际的实现。可以想象 MakeOpenRequest 的现实逻辑要复杂得多,因此编写普通工厂将导致大量代码重复。此方法唯一关心的是它应该以命名方式创建某些类型的实例,即为每个 class 单独定义。

我怀疑常规构造函数在这里会有所帮助,因为几乎没有任何通用性可以利用。构造方法必须是静态的,所以我不能指望它们具有多态性。

处理此任务的最佳方法是什么?反射是作弊,但如果没有别的办法也可以。

TL;DR
你真的应该考虑一个不依赖泛型的解决方案;因为您的预期代码对于该泛型类型的所有具体表现并不相同。这是泛型不是正确方法的第一个大危险信号。


实例化泛型类型的唯一方法是所选类型是否具有无参数构造函数。这也要求您将其指定为泛型类型约束。

由于您希望您的代码在所有可能类型的情况下工作,您的方法实际上无法决定要使用的正确消息(因为这需要知道特定类型,这会导致违反 OCP)。因此,消息应该在具体 IRequest 实现本身中定义。

但是,这会使您的方法本身除了调用构造函数之外无事可做:

interface IRequest { }

class DoorRequest : IRequest
{
    public string Message { get; }

    public DoorRequest()
    {
        Message = "Please open the door";
    }
}

class WindowRequest : IRequest
{
    public string Message { get; }

    public WindowRequest()
    {
        Message = "Please open the window";
    }
}

static class Foo
{
    public static T MakeOpenRequest<T>() where T : IRequest, new()
    {
        var obj = new T();

        return obj;
    }
}

如果您需要在 MakeOpenRequest 函数中而不是在 IRequest 实现本身中决定消息,并且消息特定于所使用的类型 T,则唯一可能的解决方案是 MakeOpenRequest 根据特定类型更改其行为,这不是泛型的好用例。这表明设计不佳并且违反了 OCP。

在这种情况下,您应该寻找替代方法,因为泛型不是可行的方法。最有可能的是,您最好选择特定的方法(MakeDoorRequestMakeWindowRequest),因为对于消费者来说,是否必须调用 MakeOpenRequest<WindowRequest> 或 [= 并不重要17=],因为两者都需要相同的具体类型知识。

您在问题中提到事情可能会变得更加复杂。在这一点上,您应该考虑 factory pattern 而不是尝试对泛型做同样的事情。

你想错了。您想要一个根据传递的某些参数创建 IRequest 对象的工厂。

示例:

enum RequestTypes {
   DoorRequest,
   WindowRequest
}

class RequestFactory {
    public IRequest Create(RequestType requestType) {
        switch(requestType) {
            case RequestType.DoorRequest:
                return new DoorRequest();
            case RequestType.WindowRequest:
                return new WindowRequest();
            default:
                throw new ArgumentException("requestType");
        }
    }
}

不需要泛型,只需要老式的多态性。

这假定 IRequest 是正确的。应该是:

interface IRequest {
    string message;
}

永远 不需要将其转换为显式 DoorRequest,您只需使用返回的 IRequest