Search code examples
.netdesign-patternsmef

Design pattern strategy in MEF


I have method what returns objects implementing particular interface. Depends on argument method return different object. All implement the same interface so I can use the same method like Execute() on interface outside the method. This solution force me to avoid of using MEF. How can I use both solution at once? Importing constructor from MEF and isolation of different strategies in separate classes?

Here is an example code:

[Export(typeof(ICrowdMessageProcessorFactory))]
public class CrowdMessageProcessorFactory : ICrowdMessageProcessorFactory
{
    private readonly IDefaultCrowdRequestAnalyzer _defaultProcessor;

    [ImportingConstructor]
    public CrowdMessageProcessorFactory(IDefaultCrowdRequestAnalyzer defaultProcessor)
    {
        _defaultProcessor = defaultProcessor;
    }

    public Metadata PayloadMetadata { get; private set; }

    public ICrowdMessageProcessor Create(InsertCrowdsourcingEventRequest request, DateTime fireUtcDateTime)
    {
        if (request == null)
        {
            throw new ArgumentNullException(nameof(request));
        }

        PayloadMetadata = Metadata.Create(request);
        var marketRegion = PayloadMetadata?.GetMarketRegion();

        switch (marketRegion)
        {
            case MarketRegion.Uk:
                return new UkCrowdMessageProcessor();
        }

        return new DefaultCrowdMessageProcessorAdapter(request, fireUtcDateTime, _defaultProcessor);
    }
}

And here is use of the method

    [ImportingConstructor]
    public CrowdResponseAnalyzer(
        ICrowdMessageProcessorFactory processorFactory)
    {
             _processorFactory = processorFactory;
    }

    public void Execute(InsertCrowdsourcingEventRequest request, DateTime fireUtcDateTime)
    {
        Guard.ArgumentNotNull(request, "request");

        try
        {
            ICrowdMessageProcessor processor = _processorFactory.Create(request, fireUtcDateTime);
            processor.Execute();
        }
        //(...)
    }  

In summary: I like to separate different strategies to different class, here in UkCrowdMessageProcessor and DefaultCrowdMessageProcessorAdapter. But in new this kind of class (ICrowdMessageProcessor) i need to use ImportingConstructor also. How can I do it?


Solution

  • Solution 1: Use CompositionContainer and it method GetExportedValue. when it looks:

    public ICrowdMessageProcessor Create(InsertCrowdsourcingEventRequest request, DateTime fireUtcDateTime)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }
    
            PayloadMetadata = Metadata.Create(request);
    
            var marketRegion = PayloadMetadata?.GetMarketRegion();
            switch (marketRegion)
            {
                case MarketRegion.Uk:
                    return _cc.GetExportedValue<UkCrowdMessageProcessor>();
            }
    
            return PrepareDefaultCrowdMessageProcessor(request, fireUtcDateTime);
        }
    
        private ICrowdMessageProcessor PrepareDefaultCrowdMessageProcessor(InsertCrowdsourcingEventRequest request, DateTime fireUtcDateTime)
        {
            var processor = _cc.GetExportedValue<DefaultCrowdMessageProcessorAdapter>();
            processor.Initialize(request, fireUtcDateTime, _defaultProcessor);
            return processor;
        }
    

    Solution 2: Use ServiceLocator and GetInstance

    Solution 3: This one is the only one right from design point of view.

    [Export(typeof(ICrowdMessageProcessorFactory))]
    public class CrowdMessageProcessorFactory : ICrowdMessageProcessorFactory
    {
    
        private readonly IDefaultCrowdRequestAnalyzer _defaultProcessor;
    
        private readonly UkCrowdMessageProcessor _ukCrowdMessageProcessor;
    
        private readonly DefaultCrowdMessageProcessorAdapter _defaultCrowdMessageProcessor;
    
        [ImportingConstructor]
        public CrowdMessageProcessorFactory(
            IDefaultCrowdRequestAnalyzer defaultProcessor, 
            UkCrowdMessageProcessor ukCrowdMessageProcessor,
            DefaultCrowdMessageProcessorAdapter defaultCrowdMessageProcessor)
        {
            _defaultProcessor = defaultProcessor;
            _ukCrowdMessageProcessor = ukCrowdMessageProcessor;
            _defaultCrowdMessageProcessor = defaultCrowdMessageProcessor;
        }
    
        public Metadata PayloadMetadata { get; private set; }
    
        public ICrowdMessageProcessor Create(InsertCrowdsourcingEventRequest request, DateTime fireUtcDateTime)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }
    
            PayloadMetadata = Metadata.Create(request);
    
            var marketRegion = PayloadMetadata?.GetMarketRegion();
            switch (marketRegion)
            {
                case MarketRegion.Uk:
                    return _ukCrowdMessageProcessor;
            }
    
            return PrepareDefaultCrowdMessageProcessor(request, fireUtcDateTime);
        }
    
        private ICrowdMessageProcessor PrepareDefaultCrowdMessageProcessor(InsertCrowdsourcingEventRequest request, DateTime fireUtcDateTime)
        {
            _defaultCrowdMessageProcessor.Initialize(request, fireUtcDateTime, _defaultProcessor);
            return _defaultCrowdMessageProcessor;
        }
    }