You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity DI与工厂模式选型咨询:复杂计算器模块解耦方案

Refactoring to Factory Pattern to Decouple from Unity DI

Great question! Your code review team is absolutely right to flag the tight coupling with Unity—this makes your calculator hard to test, inflexible to DI framework changes, and mixes business logic with container management. Let’s walk through how to refactor this using the factory pattern to fix these issues.

First: Identify the Coupling Problems in Your Current Code

  • TotalAnnualCostCalculator directly initializes and depends on UnityContainer in its constructor, tying your core calculation logic to a specific DI framework.
  • The use of container.ResolveAll<ICalculator>() mixes dependency resolution with business logic, violating the single responsibility principle.
  • Reinitializing the container every time a TotalAnnualCostCalculator is created could lead to redundant object creation and resource waste.

Step 1: Define an Abstract Factory Interface

Create an abstraction that hides how calculators are retrieved, so your business logic doesn’t need to know about Unity at all:

public interface ICalculatorFactory
{
    // Returns all registered cost calculators
    IEnumerable<IEffectiveAnnualCostCalculator> GetAllCalculators();
}

Step 2: Implement a Unity-Specific Factory

Move all Unity-related resolution logic into a concrete factory implementation. This keeps the DI framework details contained in one place:

public class UnityCalculatorFactory : ICalculatorFactory
{
    private readonly IUnityContainer _container;

    // Inject the container (we only need this here, not in the core calculator)
    public UnityCalculatorFactory(IUnityContainer container)
    {
        _container = container;
    }

    public IEnumerable<IEffectiveAnnualCostCalculator> GetAllCalculators()
    {
        return _container.ResolveAll<IEffectiveAnnualCostCalculator>();
    }
}

Step 3: Refactor TotalAnnualCostCalculator to Depend on the Abstract Factory

Update your core calculator to use the factory instead of directly interacting with Unity. Now it only cares about getting calculators, not how they’re created:

public class TotalAnnualCostCalculator
{
    private readonly ICalculatorFactory _calculatorFactory;

    // Inject the abstract factory (no more container initialization here!)
    public TotalAnnualCostCalculator(ICalculatorFactory calculatorFactory)
    {
        _calculatorFactory = calculatorFactory;
    }

    public AnnualCostCharges CalculateTotalAnnualCost(Parameters product)
    {
        var calculators = _calculatorFactory.GetAllCalculators().ToList();
        var totalCharges = new AnnualCostCharges();

        // Fix: Parallel.ForEach was ignoring results before—add thread-safe aggregation
        Parallel.ForEach(calculators, calculator =>
        {
            var individualCharges = calculator.CalculateAnnualCost(product);
            
            // Use a lock to safely accumulate results (adjust based on your AnnualCostCharges structure)
            lock (totalCharges)
            {
                totalCharges.InvestmentManagementCharge += individualCharges.InvestmentManagementCharge;
                totalCharges.AdministrationCharge += individualCharges.AdministrationCharge;
                totalCharges.AdviceCharge += individualCharges.AdviceCharge;
                totalCharges.OtherCharge += individualCharges.OtherCharge;
            }
        });

        return totalCharges;
    }
}

Step 4: Update Container Registration

Register your new factory in ContainerFactory so Unity can inject it into TotalAnnualCostCalculator:

public static class ContainerFactory
{
    public static IUnityContainer Container { get; private set; }

    public static IUnityContainer InitializeContainer()
    {
        var container = new UnityContainer();
        RegisterDependencies(container);
        return container;
    }

    private static void RegisterDependencies(UnityContainer container)
    {
        // Register the factory first
        container.RegisterType<ICalculatorFactory, UnityCalculatorFactory>();

        // Keep your existing registrations
        container.RegisterType<ICalculatorStrategyFactory, CalculatorStrategyFactory>("Factory");
        container.RegisterType<IEffectiveAnnualCostCalculator, InvestmentManagementChargeCalculator>(
            "IMCChargeCalculator", 
            new InjectionConstructor(new ResolvedParameter<ICalculatorStrategyFactory>("Factory")));
        container.RegisterType<IEffectiveAnnualCostCalculator, AdministrationChargeCalculator>("AdministrationChargeCalculator");
        container.RegisterType<IEffectiveAnnualCostCalculator, AdviceChargeCalculator>("AdviceChargeCalculator");
        container.RegisterType<IEffectiveAnnualCostCalculator, OtherChargeCalculator>("OtherChargeCalculator");
        container.RegisterType<IInvestmentManagementChargeCalculator, LumpSumIMCCalculator>("LumpSumIMCCalculator");
        container.RegisterType<IInvestmentManagementChargeCalculator, DebitOrderIMCCalculator>("DebitOrderIMCCalculator");
    }

    public static void SetupContainer(IUnityContainer container)
    {
        Container = container;
    }
}

Step 5: Update API Usage

Now your API code resolves the calculator from the container (instead of newing it up directly), letting Unity handle the factory injection:

// Initialize container once (not every time you need a calculator)
var container = ContainerFactory.InitializeContainer();
ContainerFactory.SetupContainer(container);

// Resolve the calculator from the container
var calc = container.Resolve<TotalAnnualCostCalculator>();
var totalCharges = calc.CalculateTotalAnnualCost(prod);

Key Benefits of This Refactor

  1. Decoupling: TotalAnnualCostCalculator no longer knows anything about Unity—if you ever switch to another DI framework (like Autofac or Microsoft.Extensions.DependencyInjection), you only need to implement a new ICalculatorFactory for it.
  2. Testability: For unit tests, you can create a mock ICalculatorFactory that returns test-specific calculators, no need to spin up a real Unity container.
  3. Single Responsibility: Your core calculator focuses solely on calculating total costs, while the factory handles retrieving calculator instances.
  4. Reduced Resource Waste: The container is initialized once, not every time a calculator is created.

内容的提问来源于stack exchange,提问作者shashi kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:13:05