C#对传统工厂模式的改造--在ABP框架下

29 阅读1分钟

假如,有一个接口,有多种实现,

传统工厂模式

传统代码写法如下

定义一个工厂接口、工厂类,服务接口、服务类

抽象

public interface IScriptEngine : ISingletonDependency
{
    public object Execute(string expression, FormulaParams formulaParams);
}

public interface IScriptEngineFactory : ISingletonDependency
{
    IScriptEngine Create(string engineType);
}

实现

public  class ScriptEngineBase : IScriptEngine
public class JintEngine : ScriptEngineBase, IScriptEngine

//工厂类的部分实现代码
// 根据engineType返回不同的IScriptEngine实现
// 这里只是一个例子,实际情况可能需要更复杂的逻辑
switch (engineType)
{
    case ScriptEngineType.JavaScript:
        return _serviceProvider.GetService<JavaScriptEngine>();
    //todo:更多Engine的实现
    //case "Python":
    //    return _serviceProvider.GetService<PythonEngine>();
    default:
        return _serviceProvider.GetService<JavaScriptEngine>();
}

这里存在的问题:

工厂类中耦合了,假如要再加一种实现,需要修改代码

代码优化

在 ABP 中的解决思路,巧用 Option 类和 DependOn 特性

1、命名上

将 xxxFactory 命名为 xxxProvider

2、定义一个 Options 类

public class FormulaScriptOptions
{
    public FormulaScriptOptions()
    {
        Engines = new Dictionary<string, Type>();
    }

    public IDictionary<string, Type> Engines { get; }
    public string DefaultEngineName { get; set; } = "jint";
}

3、不同的实现创建不同的项目,避免 Domain 层代码污染

在项目中放实现类,在 Module 中,添加实现类的名称、类型,进行自描述

[DependsOn(
    typeof(AbpDddDomainModule),
    typeof(DynamicExpressionDomainModule)
)]
public class JintEngineModule : AbpModule
{
    public override void PreConfigureServices(ServiceConfigurationContext context)
    {
        AddScriptEngine(context.Services);
    }
    private static void AddScriptEngine(IServiceCollection services)
    {

        services.Configure<FormulaScriptOptions>(options =>
        {
            options.Engines[JintEngine.EngineName] = typeof(JintEngine);
        });
    }

}

4、在 xxxProvider 类中,注入 Options 类,根据名称拿到对应的实现

一般提供一个默认的Provider 类

public class DefaultScriptEngineProvider : IScriptEngineProvider
{
    private readonly IServiceProvider _serviceProvider;
    private readonly FormulaScriptOptions _options;
    public DefaultScriptEngineProvider(IServiceProvider serviceProvider, IOptions<FormulaScriptOptions> options)
    {
        _serviceProvider = serviceProvider;
        _options = options.Value;
    }

    public IScriptEngine Get(string engineName)
    {
        if (engineName.IsNullOrWhiteSpace())
            engineName = _options.DefaultEngineName;

        if (!_options.Engines.ContainsKey(engineName))
            throw new BusinessException($"找不到{engineName}的脚本引擎。");

        var type = _options.Engines.GetOrDefault(engineName!);

        return _serviceProvider.GetRequiredService(type) as IScriptEngine;

    }
}

5、调用侧,只需要依赖实现类的模块即可