如何使用依赖注入替代工厂模式

3
我有以下情况:需要根据每个对象的特定属性(该属性定义为枚举)将对象集合发送给不同的第三方。我打算使用下面这种工厂模式来实现。
这个可以重构成使用依赖注入吗?
public class ServiceA: IThirdParty
{
    public void Send(Ticket objectToBeSent)
    {
        // a call to the third party is made to send the ticket
    }
}

public class ServiceB: IThirdParty
{
    public void Send(Ticket objectToBeSent)
    {
        // a call to the third party is made to send the ticket
    }
}

public interface IThirdParty
{
    void Send(Ticket objectToBeSent);
}

public static class ThirdPartyFactory
{
    public static void SendIncident(Ticket objectToBeSent)
    {
        IThirdParty thirdPartyService = GetThirdPartyService(objectToBeSent.ThirdPartyId);
        thirdPartyService.Send(objectToBeSent);
    }

    private static IThirdParty GetThirdPartyService(ThirdParty thirdParty)
    {
        switch (thirdParty)
        {
            case ThirdParty.AAA:
                return new ServiceA();

            default:
                return new ServiceB();
        }
    }
}

2
你为什么想要使用依赖注入?感觉像是一个作业任务。另外,你已经尝试过什么了吗? - User 12345678
DI和工厂模式并不是互斥的。最好将工厂注入到需要它的消费者中。 - Steven
我已经按照上述实现了工厂模式,但我在想在这种情况下是否使用依赖注入会更合适。您认为使用 DI 会有哪些显著优势? - Anca
@Anca - “你认为使用 DI 有哪些显著的优势?”是的,例如 DI 可以使单元测试更容易。 - Joe Ratzer
5个回答

2

可以重构这段代码 - 将服务注入到SendIncident或其类中。


1

我认为你应该先意识到SendIncident不应该是IThirdPartyFactory的一部分。工厂负责创建对象,而发送事件不属于此类。

因此,引入一个负责发送工单的类:

public TicketSender
{
    IThirdPartyFactory _factory

    public void TicketSender(IThirdPartyFactory factory)
    {
        _factory = factory;
    }


    public void SendIncident(Ticket ObjectToBeSent)
    {
         var service = _factory.GetThirdPartyService(ObjectToBeSent.ThirdPartyId);
         service.SendIncident(ObjectToBeSent);
    }
}

使用以下工厂:
public class ThirdPartyFactory : IThirdPartyFactory
{
    IThirdParty serviceA;
    IThirdParty serviceB;

    public ThirdPartyFactory(IThirdParty serviceA, IThirdParty serviceB)
    {
        _serviceA = serviceA;
        _serviceB = serviceB;
    }

    public IThirdParty GetThirdPartyService(ThirdParty thirdParty)
    {
        switch (thirdParty)
        {
            case ThirdParty.AAA:
                return serviceA;

            default:
                return serviceB;
        }
    }
}

如果您正在使用像Windsor这样的IOC容器,大多数工厂都将为您生成,您只需要定义接口和选择逻辑。

谢谢,我明白你所说的“单一职责原则”。 - Anca
有一件事我不太明白。在将服务注入到工厂构造函数时,它们都实现了IThirdParty接口,那么它是如何知道服务的顺序呢? - Anca
它不应该。构造函数应该将IEnumerable<IThirdParty>服务作为参数。然后,GetThirdParty方法只需执行:return _services.FirstOrDefault(s=>s.Type==thirdParty),如果有必要,可以检查null并返回默认服务。但是,您需要更好的命名方式。 - MikeSW
所以提供的两个解决方案都是错误的。你提供的替代方案,使用集合看起来更好,但这意味着需要引入一些命名约定。在这种情况下,坚持工厂模式不是更好吗? - Anca
我猜你想要将这个与IOC容器结合使用。由于ServiceA和B似乎是不同的服务,并且你无法从服务中找出它可以处理的ID(我更喜欢这种方式)。我不会使用集合。相反,我会配置我的容器来解析两个构造函数参数为Service A和B。你是否在使用特定的IOC容器? - Marwijn
显示剩余2条评论

1
首先,Marwijn是正确的,你应该有一个单独的TicketSender类,该类需要IThirdPartyFactory依赖项。
你可以创建一个新的接口:
public interface IThirdPartyId
{
    int Id { get; }
}

让所有IThirdParty的实现也按照惯例实现IThirdPartyId。然后,您可以像Marwijn建议的那样将IThirdParty集合注入到ThirdPartyFactory中。工厂可以将它们重新转换为IThirdPartyId以获取每个ID,并使用它来匹配工厂方法的ID参数。这将使您的查找逻辑保留在工厂中,而不是分散在工厂和IoC配置之间。

1

0
为什么不呢?只需稍微调整ThirdPartyFactory,使其成为抽象工厂,并且不使用静态即可。
public class ThirdPartyFactory : IThirdPartyFactory
{
    public ThirdPartyFactory(IThirdParty serviceA, IThirdParty serviceB)
    {
        this.serviceA = serviceA;
        this.serviceB = serviceB;
    }
    IThirdParty serviceA;
    IThirdParty serviceB;

    public void SendIncident(Ticket objectToBeSent)
    {
        IThirdParty thirdPartyService = GetThirdPartyService(objectToBeSent.ThirdPartyId);
        thirdPartyService.SendIncident(objectToBeSent);
    }

    private IThirdParty GetThirdPartyService(ThirdParty thirdParty)
    {
        switch (thirdParty)
        {
            case ThirdParty.AAA:
                return serviceA;

            default:
                return serviceB;
        }
    }
}

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