如何在运行时解析类型以避免multipe if else

Sim*_*ons 6 .net c# dependency-injection inversion-of-control solid-principles

我有我的代码根据请求类型进行Web服务调用.

为此,我有以下代码;

public class Client
{
    IRequest request;


    public Client(string requestType)
    {
        request = new EnrolmentRequest();
        if (requestType == "Enrol")
        {
            request.DoEnrolment();
        }
        else if (requestType == "ReEnrol")
        {
            request.DoReEnrolment();
        }
        else if (requestType == "DeleteEnrolment")
        {
            request.DeleteEnrolment();
        }
        else if (requestType == "UpdateEnrolment")
        {
            request.UpdateEnrolment();
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

所以按照开放原则,我可以像子类一样子类:

Class EnrolmentRequest:IRequest
{
    CallService();
}
Class ReEnrolmentRequest:IRequest
{
    CallService();
}
Class UpdateEnrolmentRequest:IRequest
{
    CallService();
}
Run Code Online (Sandbox Code Playgroud)

现在我的客户端类看起来像这样:

public class Client
{
    public Client(string requestType)
    {
        IRequest request;

        if (requestType == "Enrol")
        {
            request = new EnrolmentRequest();
            request.CallService();
        }
        else if (requestType == "ReEnrol")
        {
            request = new REnrolmentRequest();
            request.CallService();
        }
        else if (requestType == "DeleteEnrolment")
        {
            request = new UpdateEnrolmentRequest();
            request.CallService();
        }
        else if (requestType == "UpdateEnrolment")
        {
            request = new UpdateEnrolmentRequest();
            request.CallService();
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

现在,我仍然必须使用if和else,并且如果有任何新的请求类型,则必须更改我的代码.

所以,它绝对不是修改的结果.

我错过了关于SOLID的任何事情吗?

我可以使用依赖注入,在运行时解析类型吗?

das*_*ght 8

编写新代码以处理新需求的需求不会消失.目标是在处理新需求时不必更改旧代码,并且您的类结构处理它.

您可以通过使用其他创建新实例的其他机制替换条件链来最小化更改.例如,您可以构建字典,或使用依赖注入框架将类型与字符串相关联.

这是一个不使用DI框架的实现:

private static readonly IDictionary<string,Func<IRequest>> ReqTypeMapper =
    new Dictionary<string,Func<IRequest>> {
        {"Enrol", () => new EnrolmentRequest() }
    ,   {"ReEnrol", () => new ReEnrolmentRequest() }
    ,   ...
    };
Run Code Online (Sandbox Code Playgroud)

现在调用将如下所示:

Func<IRequest> maker;
if (!ReqTypeMapper.TryGetValue(requestType, out maker)) {
    // Cannot find handler for type - exit
    return;
}
maker().CallService();
Run Code Online (Sandbox Code Playgroud)


Ste*_*ven 5

除非您恢复使用反射,否则无法完全删除if- elseswitch- case语句列表.在系统的某个地方,您肯定会进行某种调度(使用硬编码列表或通过反射).

但是,您的设计可能会受益于更基于消息的方法,其中的接收请求是消息,例如:

class DoEnrolment { /* request values */ }
class DoReenrolment { /* request values */ }
class DeleteEnrolment { /* request values */ }
class UpdateEnrolment { /* request values */ }
Run Code Online (Sandbox Code Playgroud)

这允许您为此类请求的"处理程序"创建单一界面防御:

interface IRequestHandler<TRequest> {
    void Handle(TRequest request);
}
Run Code Online (Sandbox Code Playgroud)

您的处理程序将如下所示:

class DoEnrolmentHandler : IRequestHandler<DoEnrolment> {
    public void Handle(DoEnrolment request) { ... }
}

class DoReenrolmentHandler : IRequestHandler<DoReenrolment> {
    public void Handle(DoReenrolment request) { ... }
}

class DeleteEnrolmentHandler : IRequestHandler<DeleteEnrolment> {
    public void Handle(DeleteEnrolment request) { ... }
}
Run Code Online (Sandbox Code Playgroud)

这样做的好处是应用横切关注点是一件轻而易举的事,因为定义一个通用装饰器非常简单,因为它IRequestHandler<T>实现了日志记录等功能.

这仍然使我们回到当然的调度.可以从客户端提取调度,在它自己的抽象之后:

interface IRequestDispatcher {
    void Dispatch<TRequest>(TRequest request);
}
Run Code Online (Sandbox Code Playgroud)

这允许客户端简单地发送它所需的请求:

// Client
this.dispatcher.Dispatch(new DoEnrolment { EnrolId = id });
Run Code Online (Sandbox Code Playgroud)

请求调度程序的实现可能如下所示:

class ManualRequestDispatcher : IRequestDispatcher {
    public void Dispatch<TRequest>(TRequest request) {
        var handler = (IRequestHandler<TRequest>)CreateHandler(typeof(TRequest));
        handler.Handle(request);
    }

    object CreateHandler(Type type) =>
        type == typeof(DoEnrolment)? new DoEnrolmentHandler() :
        type == typeof(DoReenrolment) ? new DoReenrolment() :
        type == typeof(DeleteEnrolment) ? new DeleteEnrolment() :
        type == typeof(UpdateEnrolment) ? new UpdateEnrolment() :
        ThrowRequestUnknown(type);

    object ThrowRequestUnknown(Type type) {
        throw new InvalidOperationException("Unknown request " + type.Name);
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,如果您使用DI容器,则可以使用以下内容批量注册请求处理程序(取决于您使用的库当然):

container.Register(typeof(IRequestHandler<>), assemblies);
Run Code Online (Sandbox Code Playgroud)

您的调度员可能看起来如下:

class ContainerRequestDispatcher : IRequestDispatcher {
    private readonly Container container;
    public ContainerRequestDispatcher(Container container) {
        this.container = container;
    }

    public void Dispatch<TRequest>(TRequest request) {
        var handler = container.GetInstance<IRequestHandler<TRequest>>();
        handler.Handle(request);
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以在此处此处找到有关此类设计的更多信息.


小智 3

您可以添加简单工厂类,如下所示:

public class ServiceFactory : Dictionary<string, Type>
{
    public void Register(string typeName, Type serviceType) {
        if (this.ContainsKey(typeName)) {
            throw new Exception("Type registered");
        }
        this[typeName] = serviceType;
    }

    public IRequest Resolve(string typeName) {
        if (!this.ContainsKey(typeName)) {
            throw new Exception("Type not registered");
        }
        var type = this[typeName];
        var service = Activator.CreateInstance(type);
        return service as IRequest;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在一个地方注册服务,例如:

 var serviceFactory = new ServiceFactory();
        serviceFactory.Register("Enrol", typeof(EnrolmentRequest));
        serviceFactory.Register("ReEnrol", typeof(REnrolmentRequest));
        serviceFactory.Register("DeleteEnrolment", typeof(UpdateEnrolmentRequest));
        serviceFactory.Register("UpdateEnrolment", typeof(UpdateEnrolmentRequest));
Run Code Online (Sandbox Code Playgroud)

并称其为:

var service = serviceFactory.Resolve(requestType);
service.CallService();
Run Code Online (Sandbox Code Playgroud)

还需要添加适当的错误处理