C# 如何创建返回不同接口类型和抽象类的泛型工厂
我有一个抽象基类,它包含大量共享代码和配置属性。我已经将大部分共享代码分解为逻辑接口,这些接口也由基类实现。 每个客户都有一系列基类的实现 我目前为每个接口都有一个工厂。每个工厂都有相同的开关语句。我想创建一个通用工厂,它将根据类的声明方式返回不同的功能子集 我的基类:C# 如何创建返回不同接口类型和抽象类的泛型工厂,c#,C#,我有一个抽象基类,它包含大量共享代码和配置属性。我已经将大部分共享代码分解为逻辑接口,这些接口也由基类实现。 每个客户都有一系列基类的实现 我目前为每个接口都有一个工厂。每个工厂都有相同的开关语句。我想创建一个通用工厂,它将根据类的声明方式返回不同的功能子集 我的基类: 公共抽象类BaseParser:IDatabaseCreation、IBulkImport、IAnalyticFeature //…许多配置字段、方法和抽象方法 客户类别: 类apaser:BaseParser { 专用int
公共抽象类BaseParser:IDatabaseCreation、IBulkImport、IAnalyticFeature
//…许多配置字段、方法和抽象方法
客户类别:
类apaser:BaseParser
{
专用int组件指示或列;
公共分离机(ILogger日志):基本(日志){
//…配置值和抽象方法实现
当前基础工厂:
class BulkImportFactory
{
public IBulkImport CreateDatabaseCreationObject(string key, ILogger log)
{
switch (key)
{
case "A":
return new AParser(log);
case "B":
return new BParser(log);
case "C":
return new CParser(log);
default:
throw new NotImplementedException("Not Recognized or Not Registered in Factory");
}
}
}
类基工厂
{
公共BaseParser CreateParser(字符串键、ILogger日志)
{
开关(钥匙)
{
案例“A”:
返回新的服务器(日志);
案例“B”:
返回新的BParser(日志);
案例“C”:
返回新的CParser(日志);
违约:
抛出新的NotImplementedException(“未被认可或未在工厂注册”);
}
}
}
样本接口工厂:
class BulkImportFactory
{
public IBulkImport CreateDatabaseCreationObject(string key, ILogger log)
{
switch (key)
{
case "A":
return new AParser(log);
case "B":
return new BParser(log);
case "C":
return new CParser(log);
default:
throw new NotImplementedException("Not Recognized or Not Registered in Factory");
}
}
}
这是我尝试在一个不起作用的通用工厂:
公共类GenericFactory
{
公共T CreateVariableInterfaceObject(字符串键,ILogger日志),其中T:BaseParser
{
开关(钥匙)
{
案例“A”:
返回新的服务器(日志);
案例“B”:
返回新的BParser(日志);
案例“C”:
返回新的CParser(日志);
违约:
抛出新的NotImplementedException(“未识别或未在GenericFactory中注册”);
}
}
}
正如你所看到的,工厂中的逻辑是相同的和重复的。但是我不能让一个通用解析器工作。不确定我缺少什么语法
我想做的是让所有这些都成为一个工厂:
ParserFactory ParserFactory=new ParserFactory();
BaseParser=parserFactory.CreateParser(queueMessage.key,log);
BulkImportFactory BulkImportFactory=new BulkImportFactory();
IBulkImport bulkImporter=bulkImportFactory.CreateDatabaseCreationObject(键,日志);
AnalyticFeatureFactory parserFactory=新的AnalyticFeatureFactory();
IAnalyticFeatureParser=parserFactory.CreateAnalyticFeatureObject(键,日志);
让我看看是否可以澄清您的意图
您有一组解析器(apaser、BParser、CParser)具有BaseParser
某些函数的特定实现。您试图做的是在运行时为AParser
、BParser
等的特定实例提供一些特殊功能。因此,假设您需要AParser
但默认的AParser
实现不支持>ParseFoo()
但在运行时,您希望赋予它ParseFoo()
功能而不提前定义它吗
如果是这样的话,我想你将不得不考虑一个可能与你的工厂一起。与装饰器,你将实现在他们自己的类中的新功能的功能,然后你的工厂将返回装饰器,简单地包装<代码> BaseParser < /代码>混凝土类。这是你的需要吗
sealed class GenericFactory<TKey, TOption, TObject>
{
readonly IReadOnlyDictionary<TKey, Func<TKey, TOption, TObject>> _factories;
public GenericFactory(
IReadOnlyDictionary<TKey, Func<TKey, TOption, TObject>> factories)
{
_factories = factories;
}
public bool TryCreate(TKey key, TOption option, out TObject @object)
{
@object = default;
if (!_factories.TryGetValue(key, out var factory))
return false; // Cannot create; unknown key
@object = factory(key, option);
return true;
}
}
static class GenericFactoryExtensions
{
public static TObject CreateOrFail<TKey, TOption, TObject>(
this GenericFactory<TKey, TOption, TObject> factory,
TKey key,
TOption option)
{
if (!factory.TryCreate(key, option, out var @object))
throw new NotImplementedException($"Not Recognized or Not Registered in {nameof(GenericFactory<TKey, TOption, TObject>)}");
return @object;
}
}
void SimpleUseFactory()
{
var baseParserFactory = new GenericFactory<string, ILogger, BaseParser>(new Dictionary<string, Func<string, ILogger, BaseParser>>
{
["A"] = (key, logger) => new AParser(logger),
["B"] = (key, logger) => new BParser(logger)
});
var parser = baseParserFactory.CreateOrFail("A", logger);
parser.DoStuff();
}
class Factories
{
public Func<string, ILogger, BaseParser> BaseParserFactory { get; }
public Func<string, ILogger, IBulkImport> BulkImportFactory { get; }
public Func<string, ILogger, SomethingElse> SomethingElseFactory { get; }
public Factories(
Func<string, ILogger, BaseParser> baseParserFactory,
Func<string, ILogger, IBulkImport> bulkImportFactory,
Func<string, ILogger, SomethingElse> somethingElseFactory)
{
BaseParserFactory = baseParserFactory;
BulkImportFactory = bulkImportFactory;
SomethingElseFactory = somethingElseFactory;
}
}
void ComplexUseFactory()
{
var mappedFactories = new Dictionary<string, Factories>
{
["A"] = new Factories(
baseParserFactory: (key, logger) => new AParser(logger),
bulkImportFactory: (key, logger) => new ABulkImport(logger),
somethingElseFactory: (key, logger) => new ASomethingElse(logger)),
["B"] = new Factories(
baseParserFactory: (key, logger) => new BParser(logger),
bulkImportFactory: (key, logger) => new BBulkImport(logger),
somethingElseFactory: (key, logger) => new BSomethingElse(logger))
};
var baseParserFactory = new GenericFactory<string, ILogger, BaseParser>(
mappedFactories.ToDictionary(
keySelector: kvp => kvp.Key,
elementSelector: kvp => kvp.Value.BaseParserFactory));
var bulkImportFactory = new GenericFactory<string, ILogger, IBulkImport>(
mappedFactories.ToDictionary(
keySelector: kvp => kvp.Key,
elementSelector: kvp => kvp.Value.BulkImportFactory));
var somethingElseFactory = new GenericFactory<string, ILogger, SomethingElse>(
mappedFactories.ToDictionary(
keySelector: kvp => kvp.Key,
elementSelector: kvp => kvp.Value.SomethingElseFactory));
var parser = baseParserFactory.CreateOrFail("A", logger);
parser.DoStuff();
}
ModuleB.cs
Register<AParser>().As<BaseParser>();
Register<ABulkImport>().As<IBulkImport>();
Register<BParser>().As<BaseParser>();
Register<BBulkImport>().As<IBulkImport>();
public class CommonThing
{
readonly BaseParser _parser;
readonly IBulkImport _bulkImport;
public CommonThing(
BaseParser parser,
IBulkImport bulkImport)
{
_parser = parser;
_bulkImport = bulkImport;
}
public void DoFancyStuff(string data)
{
var parsed = _parser.Parse(data);
_bulkImport.Import(parsed);
}
}
public class Application
{
readonly CommonThing _commonThing;
public Application(
CommonThing commonThing)
{
_commonThing = commonThing;
}
public void Run()
{
var json = "{\"key\":\"value\"}";
_commonThing.DoFancyStuff(json);
}
}
public class ModuleNameAttribute : Attribute
{
public string Name { get; }
...
}
[ModuleName("A")]
public class ModuleA
{
...
}
[ModuleName("B")]
public class ModuleB
{
...
}
单成分根
switch (module)
{
case "A":
RegisterModule<ModuleA>();
break;
case "B":
RegisterModule<ModuleB>();
break;
default:
throw new NotImplementedException($"Unexpected module {module}");
}
Register<CommonThing>();
Register<Application>();
var moduleType = GetAllTypesInAppDomain()
.Select(type => (type, type.GetCustomAttribute<ModuleNameAttribute>()))
.Where(tuple => tuple.Item2 != null)
.Where(tuple => tuple.Item2.Name == module)
.FirstOrDefault();
if (moduleType == null)
throw new NotImplementedException($"No module has a {nameof(ModuleNameAttribute)} matching the requested module {module}");
RegisterModule(moduleType);
...
Program.cs(或您选择的入口点)
模块a.cs
Register<AParser>().As<BaseParser>();
Register<ABulkImport>().As<IBulkImport>();
Register<BParser>().As<BaseParser>();
Register<BBulkImport>().As<IBulkImport>();
public class CommonThing
{
readonly BaseParser _parser;
readonly IBulkImport _bulkImport;
public CommonThing(
BaseParser parser,
IBulkImport bulkImport)
{
_parser = parser;
_bulkImport = bulkImport;
}
public void DoFancyStuff(string data)
{
var parsed = _parser.Parse(data);
_bulkImport.Import(parsed);
}
}
public class Application
{
readonly CommonThing _commonThing;
public Application(
CommonThing commonThing)
{
_commonThing = commonThing;
}
public void Run()
{
var json = "{\"key\":\"value\"}";
_commonThing.DoFancyStuff(json);
}
}
public class ModuleNameAttribute : Attribute
{
public string Name { get; }
...
}
[ModuleName("A")]
public class ModuleA
{
...
}
[ModuleName("B")]
public class ModuleB
{
...
}
ModuleB.cs
Register<AParser>().As<BaseParser>();
Register<ABulkImport>().As<IBulkImport>();
Register<BParser>().As<BaseParser>();
Register<BBulkImport>().As<IBulkImport>();
public class CommonThing
{
readonly BaseParser _parser;
readonly IBulkImport _bulkImport;
public CommonThing(
BaseParser parser,
IBulkImport bulkImport)
{
_parser = parser;
_bulkImport = bulkImport;
}
public void DoFancyStuff(string data)
{
var parsed = _parser.Parse(data);
_bulkImport.Import(parsed);
}
}
public class Application
{
readonly CommonThing _commonThing;
public Application(
CommonThing commonThing)
{
_commonThing = commonThing;
}
public void Run()
{
var json = "{\"key\":\"value\"}";
_commonThing.DoFancyStuff(json);
}
}
public class ModuleNameAttribute : Attribute
{
public string Name { get; }
...
}
[ModuleName("A")]
public class ModuleA
{
...
}
[ModuleName("B")]
public class ModuleB
{
...
}
单成分根
switch (module)
{
case "A":
RegisterModule<ModuleA>();
break;
case "B":
RegisterModule<ModuleB>();
break;
default:
throw new NotImplementedException($"Unexpected module {module}");
}
Register<CommonThing>();
Register<Application>();
var moduleType = GetAllTypesInAppDomain()
.Select(type => (type, type.GetCustomAttribute<ModuleNameAttribute>()))
.Where(tuple => tuple.Item2 != null)
.Where(tuple => tuple.Item2.Name == module)
.FirstOrDefault();
if (moduleType == null)
throw new NotImplementedException($"No module has a {nameof(ModuleNameAttribute)} matching the requested module {module}");
RegisterModule(moduleType);
...
var moduleType=GetAllTypesInAppDomain()
.Select(type=>(type,type.GetCustomAttribute())
.Where(tuple=>tuple.Item2!=null)
.Where(tuple=>tuple.Item2.Name==module)
.FirstOrDefault();
if(moduleType==null)
抛出新的NotImplementedException($“没有模块的{nameof(ModuleNameAttribute)}与请求的模块{module}匹配”);
注册模块(moduleType);
...
请注意,始终使用依赖项注入的好处之一(这意味着注册/解析应用程序本身,就像上面提到的Program.cs
那样)缺少注册会导致非常早期的运行时异常。这通常不需要某种编译时保证所有正确的东西都在正确的位置
例如,如果
module
被定义为“C”,那么NotImplementedException
在“singlecompositionroot”中将在应用程序启动时抛出。或者,如果模块C确实存在,但未能注册IBulkImport
的实现,则IoC容器将在尝试解决应用程序的常见问题时抛出运行时异常,再次在应用程序启动时抛出。因此,如果应用程序启动,则您知道l依赖项已解决或可以解决。似乎您的代码违反了坚实的原则,例如单一用途类。如果您想解决这一问题,而不是解决实现每个接口的基类,则它们可以包含每个接口的属性,例如解析器、批量导入器、分析
但是如果您想用当前的体系结构简单地解决这个问题,您可以执行以下操作
您已经定义了一个基本解析器实现3个接口。从同一工厂获取每个接口
//Note: you probably want to change the name of your factory
ParserFactory parserFactory = new ParserFactory();
BaseParser parser = parserFactory.CreateParser(queueMessage.key,log);
IBulkImport bulkImporter = parserFactory.CreateParser(queueMessage.key,log);
如果出于某种原因,您需要添加其他非解析器的批量导入器,那么只需创建一个封装逻辑的类,例如
在你的身边