假如,有一个接口,有多种实现,
传统工厂模式
传统代码写法如下
定义一个工厂接口、工厂类,服务接口、服务类
抽象
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、调用侧,只需要依赖实现类的模块即可