【问题标题】:What creational design pattern would suit these requirements?什么样的设计模式可以满足这些要求?
【发布时间】:2011-08-06 10:11:50
【问题描述】:

我有一个场景,我将数据“请求”对象传递给服务,并且服务本身必须根据请求中的数据创建许多不同的“处理器”。

每个处理器本身都可以是多种不同类型中的一种。因此,例如,粗略的丑陋实现可能如下所示:

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;
}

我正在寻找一种可扩展的模式,它隐藏了决定服务需要创建的处理器类型的令人讨厌的逻辑,因此它比上述丑陋的代码更简洁,更易于维护。

我知道工厂方法,但仅这些是不够的。

建议表示赞赏。

【问题讨论】:

  • 出于好奇,为什么工厂方法不够用?

标签: c# java design-patterns


【解决方案1】:

脑海中浮现的一种模式是责任链(也许不是创造模式)

首先,你需要 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);
                }
            }
        }
    }

希望这会有所帮助。干杯。

【讨论】:

  • 可能 OP 实际上想要对之后返回的实例做一些事情,很可能不止一步(即调用方法 X,然后调用方法 Y)。虽然这不是很清楚,但 CoR 限制了你可以做的事情。
  • dotnetnate - 你的假设是对的,我确实想对实例做一些事情,因此这种模式并不适合我,尽管它很有趣。跨度>
  • 理想情况下,我不会在 handle 方法中新建 ProcessLargeRequest 和 ProcessSmallRequest 对象。我可能会将构造函数或属性注入到我的处理程序中。这将使代码更具可测试性,并且您可以在迭代链后检查对象的状态。
【解决方案2】:

您要做的是创建一个工厂,主要问题是您要如何配置它。我喜欢遵循这样一种方法,即选择应该创建的方法驻留在 Factory 的职责范围内,而不是在正在创建的类中 - 它可以带来更好的可配置性并且更易于管理。

我会创建这样的东西:

    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:我的观点恰恰相反。首先,在大多数情况下,经常更改规则只是一个神话。其次,尝试维护数千行 xml 文件——Java 人过去几乎对所有事情都这样做——问他们对他们来说是哪种 PITA。为什么你认为通过 Fluent Interfaces 进行配置现在如此流行?第三,在 CI 和自动单元测试的时代,我认为 xml 对快速需求变化没有任何优势。第四 - 假设 conf。确实是放在xml里的,你允许用户改成自己喜欢的吗?如果没有,还需要重新编译。
  • 这是最接近我正在寻找的答案,我想我可以使用它。
【解决方案3】:

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

例如,您可能想要一个看起来像这样的配置文件

<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);

【讨论】:

    猜你喜欢
    • 2015-02-06
    • 1970-01-01
    • 1970-01-01
    • 2019-09-15
    • 2014-08-17
    • 2021-09-03
    • 1970-01-01
    • 2020-05-26
    • 1970-01-01
    相关资源
    最近更新 更多