structuremap - two implementations of same interface

前端 未结 4 469
悲哀的现实
悲哀的现实 2021-02-02 01:14

I have a service class with the following ctor:

public class (IMessageService emailService, IMessageService smsService)
{ ... }

and two impleme

4条回答
  •  不要未来只要你来
    2021-02-02 01:29

    You could use named instances or smart instances to solve this...

    // Named instances
    this.For().Use().Named("emailService");
    this.For().Use().Named("smsService");
    
    
    
    // Smart instances
    var emailService = this.For().Use();
    var smsService = For().Use();
    
    this.For().Use()
        .Ctor("emailService").Is(emailService)
        .Ctor("smsService").Is(smsService);
    

    But I would argue that your design needs some work. The fact that your service knows the difference between an emailService an an SMS service is a violation of the Liskov Substitution Principle. A better approach than injecting 2 parameters of the same type is to use a composite pattern.

    public class CompositeMessageService : IMessageService
    {
        private readonly IMessageService messageServices;
    
        public CompositeMessageService(IMessageService[] messageServices)
        {
            if (messageServices == null)
                throw new ArgumentNullException("messageServices");
            this.messageServices = messageServices;
        }
    
        public void Send(IMessage message)
        {
            foreach (var messageService in this.messageServices)
            {
                messageService.Send(message);
            }
        }
    }
    

    Your original service then needs to only accept a single instance of IMessageService. It does not need to know the details of what type of IMessageService it is dealing with.

    public SomeService(IMessageService messageService)
    

    In StructureMap, you can easily register all instances of IMessageService and it will automatically inject them into a constructor argument array of IMessageService.

    this.Scan(scan =>
            {
                scan.TheCallingAssembly();
                scan.AssemblyContainingType();
                scan.AddAllTypesOf();
            });
    

    Or you can inject instances explicitly.

            this.For().Use()
                .EnumerableOf().Contains(x =>
                {
                    x.Type();
                    x.Type();
                });
    

    This means your configuration can be changed to change the order of which service is called first. With your current design, those details are hard coded into the service that accepts the 2 parameters.

    Additionally, you gain the ability to add additional message services or remove existing message services without changing your design.

提交回复
热议问题