构造对象的通用方法
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
。我可以扩展 DoorRequest
和 WindowRequest
的功能以提供实际的实现。可以想象 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。
在这种情况下,您应该寻找替代方法,因为泛型不是可行的方法。最有可能的是,您最好选择特定的方法(MakeDoorRequest
、MakeWindowRequest
),因为对于消费者来说,是否必须调用 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
。
我有两个 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
。我可以扩展 DoorRequest
和 WindowRequest
的功能以提供实际的实现。可以想象 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。
在这种情况下,您应该寻找替代方法,因为泛型不是可行的方法。最有可能的是,您最好选择特定的方法(MakeDoorRequest
、MakeWindowRequest
),因为对于消费者来说,是否必须调用 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
。