C#设计模式——抽象工厂模式(重点)
【代码】C#设计模式——抽象工厂模式(重点)
·
文章目录
项目地址
- 教程作者:
- 教程地址:
- 代码仓库地址:
- 所用到的框架和插件:
dbt
airflow
一、抽象工厂模式
- 工厂方法模式依然存在一个问题就是,一堆的 switch用来根据符号判断;if或者switch都是描述了一段关系,运算符和具体工厂对象的对应关系,如何直接根据符号就可以自动获取对应的类,并且自动创建实例呢?
1.1 特性
- 什么是特性?
通过Attribute特性,根据输入的符号,直接将符号+和cal = new Add();对应起来,特性类似于一个装饰器,装饰器装饰的类,就拥有了这个对应关系;
往往和反射一起结合使用
- 创建一个特性,用来描述运算符和具体类的关系
//①使用特性Attribute来标记类的作用
public class OperToFactoryAttribute : Attribute
{
public string Oper { get; }
public OperToFactoryAttribute(string oper)
{
this.Oper = oper;
}
}
- 使用创建好的特性去标记运算类
//②使用特性Attribute来标记类的作用
//2.通过子类实现加法的创建
[OperToFactoryAttribute("+")]
public class AddFactory : ICalFactory
{
public ICal GetCalculator()
{
return new Add();
}
}
至此,运算符和对应需要的类已经创建完成,程序运行后,当用户输入了符号,我们应该通过这个对应关系,就可以找到这个类,然后进行实例化,计算
1.2 使用反射获取特性标记的类
- 根据用户的操作符,返回一个对象,
- 通过字典来存储对应关系
- 通过反射获取所有的程序集
- 获取所有的类型
- 通过判断获取AddFactory,SubFactory,MulFactory,DivFactory,找到他们的共性,都是继承了ICalFactory接口,并且排除ICalFactory自己
1.3 完整代码
using System.ComponentModel;
using System.Reflection;
public class program
{
static void Main()
{
Console.WriteLine("输入number1:");
double d1 = Convert.ToDouble(Console.ReadLine());
Console.WriteLine("输入number2:");
double d2 = Convert.ToDouble(Console.ReadLine());
Console.WriteLine("输入运算符:");
string op = Console.ReadLine();
//13.执行反射里的构造函数,创建符号和类的对应关系的字典
ReflectionFactory rf = new ReflectionFactory();
//15.通过运算符来创建工厂
ICalFactory calFactory = rf.CreateFactory(op);
//16.通过具体的运算类来计算结果
ICal calculator = calFactory.GetCalculator();
double res = calculator.getResult(d1, d2);
Console.WriteLine(res);
}
}
//1.创建特性Attribute来标记类的作用
public class OperToFactoryAttribute : Attribute
{
public string Oper { get; }
public OperToFactoryAttribute(string oper)
{
this.Oper = oper;
}
}
//声明一个接口,先将创建对象的这个过程封装成抽象
public interface ICalFactory
{
ICal GetCalculator();
}
//3.通过反射,程序运行后,获取特性标记的类,然后通过反射创建对象
public class ReflectionFactory
{
//4.创建字典,存储符号和类的对应关系
Dictionary<string, ICalFactory> dic = new Dictionary<string, ICalFactory>();
//5.构造函数
public ReflectionFactory()
{
//6.通过反射获取所有的程序集
Assembly asm = Assembly.GetExecutingAssembly();
//7.获取所有的类型
Type[] types = asm.GetTypes();
foreach (var type in types)
{
//8.通过判断获取AddFactory,SubFactory,MulFactory,DivFactory
if (typeof(ICalFactory).IsAssignableFrom(type) && !type.IsInterface)
{
//9.获取特性
OperToFactoryAttribute otfa = type.GetCustomAttribute<OperToFactoryAttribute>();
//10.判断是否为空
if (otfa != null)
{
//11.将特性和类的对应关系存储到字典中,根据type创建对象
dic.Add(otfa.Oper, Activator.CreateInstance(type) as ICalFactory);
}
}
}
}
//12.通过运算符来创建工厂
public ICalFactory CreateFactory(string oper)
{
//13. 从字典里查找对应关系
if (dic.ContainsKey(oper))
{
return dic[oper];
}
return null;
}
}
//2.使用特性Attribute来标记类的作用
[OperToFactoryAttribute("+")]
public class AddFactory : ICalFactory
{
public ICal GetCalculator()
{
return new Add();
}
}
[OperToFactoryAttribute("-")]
public class SubFactory : ICalFactory
{
public ICal GetCalculator()
{
return new Sub();
}
}
[OperToFactoryAttribute("*")]
public class MulFactory : ICalFactory
{
public ICal GetCalculator()
{
return new Mul();
}
}
[OperToFactoryAttribute("/")]
public class DivFactory : ICalFactory
{
public ICal GetCalculator()
{
return new Div();
}
}
//计算类的接口
public interface ICal
{
double getResult(double num1, double num2);
}
public class Add : ICal
{
public double getResult(double num1, double num2)
{
return num1 + num2;
}
}
public class Sub : ICal
{
public double getResult(double num1, double num2)
{
return num1 - num2;
}
}
public class Mul : ICal
{
public double getResult(double num1, double num2)
{
return num1 * num2;
}
}
public class Div : ICal
{
public double getResult(double num1, double num2)
{
return num1 / num2;
}
}
二、策略+工程+DI+反射+特性
- 实现一个读取将不同格式的文件读取,写入到sql数据库的表里
2.1 定义FileTypeAttribute特性
- 首先定义一个特性 FileTypeAttribute,用于标记每个文件读取器类的文件类型。
using System;
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class FileTypeAttribute : Attribute
{
public string FileType { get; }
public FileTypeAttribute(string fileType)
{
FileType = fileType;
}
}
2.2 创建文件读取器接口 IFileReader
- 接着,定义一个通用的文件读取器接口 IFileReader,用于各个具体文件类型的实现。
using System.Collections.Generic;
using System.Threading.Tasks;
public interface IFileReader
{
Task<IEnumerable<Dictionary<string, object>>> ReadFileAsync(string filePath);
}
2.3 实现不同的文件读取器
- 然后,为不同的文件类型(如 JSON、TXT、Excel)实现具体的文件读取器。每个读取器都需要通过 FileTypeAttribute 特性来标记。
- json读取
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
[FileType("json")]
public class JsonFileReader : IFileReader
{
public async Task<IEnumerable<Dictionary<string, object>>> ReadFileAsync(string filePath)
{
var jsonContent = await File.ReadAllTextAsync(filePath);
var data = JsonSerializer.Deserialize<List<Dictionary<string, object>>>(jsonContent);
return data ?? new List<Dictionary<string, object>>();
}
}
- Excel 文件读取器
using System.Collections.Generic;
using System.Threading.Tasks;
[FileType("excel")]
public class ExcelFileReader : IFileReader
{
public async Task<IEnumerable<Dictionary<string, object>>> ReadFileAsync(string filePath)
{
// 假设使用某种库(例如 EPPlus 或 NPOI)来读取 Excel 文件
var data = new List<Dictionary<string, object>>();
// 模拟 Excel 数据
data.Add(new Dictionary<string, object> { { "Column1", "Value1" } });
data.Add(new Dictionary<string, object> { { "Column1", "Value2" } });
return await Task.FromResult(data);
}
}
2.4 实现工厂
- 我们创建一个工厂类 FileReaderFactory,它通过反射查找带有 FileTypeAttribute 特性的类,并将其映射为文件类型。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
public class FileReaderFactory
{
private readonly IServiceProvider _serviceProvider;
private readonly Dictionary<string, Type> _fileReaderMappings;
public FileReaderFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
// 通过反射加载所有带有 FileTypeAttribute 特性的类
_fileReaderMappings = Assembly.GetExecutingAssembly()
.GetTypes()
.Where(t => t.GetCustomAttribute<FileTypeAttribute>() != null)
.ToDictionary(
t => t.GetCustomAttribute<FileTypeAttribute>().FileType,
t => t
);
}
public IFileReader GetFileReader(string fileType)
{
if (_fileReaderMappings.TryGetValue(fileType, out var readerType))
{
var fileReader = _serviceProvider.GetService(readerType) as IFileReader;
if (fileReader == null)
{
throw new InvalidOperationException($"FileReader not registered for type {fileType}");
}
return fileReader;
}
throw new InvalidOperationException($"Unsupported file type: {fileType}");
}
}
2.5 注册工厂
using Microsoft.Extensions.DependencyInjection;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// 注册文件读取器
services.AddTransient<JsonFileReader>();
services.AddTransient<TxtFileReader>();
services.AddTransient<ExcelFileReader>();
// 注册文件读取工厂
services.AddTransient<FileReaderFactory>();
}
}
2.6 使用工厂类读取文件
- 根据传入的文件,自动获取文件类型,并且处理数据
using System;
using System.Threading.Tasks;
public class FileProcessor
{
private readonly FileReaderFactory _fileReaderFactory;
public FileProcessor(FileReaderFactory fileReaderFactory)
{
_fileReaderFactory = fileReaderFactory;
}
public async Task ProcessFileAsync(string fileType, string filePath)
{
try
{
var fileReader = _fileReaderFactory.GetFileReader(fileType);
var data = await fileReader.ReadFileAsync(filePath);
// 在这里执行写入数据库等操作
Console.WriteLine($"Successfully read {data.Count()} rows from {filePath}.");
}
catch (Exception ex)
{
Console.WriteLine($"Error processing file: {ex.Message}");
}
}
}
更多推荐
所有评论(0)