这些要求适用哪种创建型设计模式?

4
我有一个场景,其中我将一个名为“request”的数据对象传递到一个服务中,该服务本身必须根据请求中的数据创建多个不同的“处理器”。
每个处理器本身可以是多种不同类型之一。因此,例如,一个粗糙且丑陋的实现可能如下所示:
public Collection<IProcessor> UglyCreationalMethod(Request request)
{
    var processors = new Collection<IProcessor>();

    if(request.Type == RequestType.SomeVal)
    {
        if(request.Id > 1000)
        {
            processors.Add(new ProcessLargeRequest(request));
        }
        else
        {
            processors.Add(new ProcessSmallRequest(request));
        }
    }
    else (request.Type == RequestType.SomeOtherVal)
    {
        if(request.Source == RequestSource.User)
        {
            processors.Add(new ProcessUserRequest(request));
        }
        else
        {
            processors.Add(new ProcessCorpRequest(request));
        }
    }

    if(request.SomeProp == "blah")
        processors.Add(new ProcessBlahRequest(request));

    // ... etc ad infinitum :)

    return processors;
}

我正在寻找一种可扩展的模式,可以隐藏决定服务需要创建哪种处理器类型的糟糕逻辑,使代码更加简洁和易于维护,比上面那段丑陋的代码更好一些。我知道工厂方法,但仅靠这些是不够的。欢迎提供建议。

出于好奇,为什么工厂方法不够用? - Joeblackdev
3个回答

2

我想到的一个模式是责任链(也许不是创造性的模式)

首先,你需要RequestHandlers

public interface IRequestHandler
    {
        bool CanHandle(Request req);

        void Handle(Request req);
    }

    public class LargeRequestHandler : IRequestHandler
    {
        public bool CanHandle(Request req)
        {
            return (req.Type == RequestType.SomeVal && req.id > 1000);
        }

        public void Handle(Request req)
        {
            processors.Add(new ProcessLargeRequest(request));
        }
    }

    public class SmallRequestHandler : IRequestHandler
    {
        public bool CanHandle(Request req)
        {
            return (req.Type == RequestType.SomeVal && req.id < 1000);
        }

        public void Handle(Request req)
        {
            processors.Add(new SmallLargeRequest(request));
        }
    }

...同样,根据需要不断添加处理程序的类。

然后创建一个这些处理程序的链。

public class RequestChain
    {
        IRequestHandler[] handlers;

        public RequestChain()
        {
            handlers = new[] { new LargeRequestHandler(), new SmallRequestHandler() };
        }

        public void ProcessRequest(Request req)
        {
            foreach (var handler in handlers)
            {
                if (handler.CanHandle(req))
                {
                    handler.Handle(req);
                }
            }
        }
    }

希望这能有所帮助。干杯!


1
大概来说,OP实际上想在之后对返回的实例进行一些操作,很可能需要多个步骤(例如调用方法X,然后调用方法Y)。虽然这并不是绝对清楚的,但CoR会限制你可以做的事情。 - dotnetnate
dotnetnate - 你的假设是正确的,我想要对这些实例进行操作,因此这种模式对我来说不太合适,尽管它很有趣。 - MalcomTucker
理想情况下,我不会在handle方法中新建ProcessLargeRequest和ProcessSmallRequest对象。我可能会将它们构造函数或属性注入到我的处理程序中。这将使代码更易于测试,并且您可以在迭代链之后检查对象的状态。 - Raghu

1

工厂可能是正确的选择,但您需要更多的支持,即配置。

例如,您可能需要一个类似于以下内容的配置文件

<processor type="typename">
  <rules>
    <rule type="requestIdThresholdRule">
      <configuration evaluationType="ExceedsThreshold" threshold="1000"/>
    </rule>
  </rules>
</processor>
<processor type="othertypename">
  <rules>
    <rule type="yadda">
       <configuration evaluationType="DoesNotMeetThreshold" threshold="1000"/>
    </rule>
  </rules>

这使您可以非常灵活地定义基于上下文的运行时评估创建哪些类型。您不需要在工厂方法本身中编写大量代码,而是遵循一些规则,这些规则主要由配置值驱动。代码量少了很多,更加灵活。

然后您只需像这样调用它:

 List<ISomething> items = ISomethingFactory.CreateISomethingsForContext(context);

1
你想做的是创建一个工厂,主要问题是你想如何配置它。我喜欢遵循一种方法,即选择应该创建哪个方法应该由工厂负责,而不是在被创建的类中 - 这会导致更好的可配置性,并且更容易管理。
我会创建像这样的东西:
    public struct ProcessorCreationSettings
    {
        public Predicate<Request> Predicate;
        public Func<Request, IProcessor> Creator;
    }

    public class ProcessorFactory
    {
        protected static List<ProcessorCreationSettings> settings = new List<ProcessorCreationSettings>();

        static ProcessorFactory()
        {
            settings.Add(new ProcessorCreationSettings
            {
                Predicate = r => r.Type == RequestType.SomeOther && r.Id > 1000,
                Creator = r => new ProcessLargeRequest(r)
            });
            settings.Add(new ProcessorCreationSettings
            {
                Predicate = r => r.Type == RequestType.SomeOther && r.Id <= 1000,
                Creator = r => new ProcessSmallRequest(r)
            });
        }

        public List<IProcessor> Create(Request request)
        {
            return settings
                .Where(s => s.Predicate(request))
                .Select(s => s.Creator(request))
                .ToList();
        }
    }

配置部分是通过静态列表完成的,但如果IoC容器具有此功能,您也可以使用它来完成。


我认为将规则放入代码中并不是一个好的实践。规则经常会发生变化...使用配置来指定哪些规则控制实例化。 - dotnetnate
@dotnetnate:我会持相反的观点。首先,在大多数情况下,规则经常更改只是一个谎言。其次,试着维护数千行的 XML 文件——Java 的人们几乎为所有的事情都这样做——问问他们这对他们来说意味着什么。你为什么认为通过流接口进行配置现在如此受欢迎?第三,在 CI 和自动单元测试时代,我认为使用 XML 进行快速需求更改没有任何优势。第四——假设确实将配置放在 XML 中,那么你是否允许用户随意更改它?如果不允许,你仍需要重新编译。 - kstaruch
这个答案最接近我想要的,我认为我可以用它来工作。 - MalcomTucker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接