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

本地TXT数据通用加载器实现:最优设计模式咨询

针对你的场景,我推荐采用策略模式(Strategy Pattern)搭配泛型抽象基类,再结合一个全局工厂类的组合方案——这既能彻底解决当前单例模式带来的逻辑冗余、依赖过重问题,又能轻松支持未来新增的模型。下面具体拆解实现思路:

核心设计逻辑

首先,你的现有问题根源在于:每个模型的单例都重复实现了「文件读取、逐行处理」这类通用逻辑,同时把「模型解析、验证」这类特有逻辑和通用逻辑耦合在一起。我们需要把这两部分拆分开,让通用逻辑只写一次,特有逻辑按需实现。

1. 抽象基类封装通用加载流程

先创建一个泛型抽象基类DataLoader<T>,把所有模型都需要的通用逻辑(读取文件、跳过空行/表头、字段拆分)封装在这里,只把模型特有的「验证规则」和「字段转对象」逻辑抽象成抽象方法,交给子类实现:

public abstract class DataLoader<T>
{
    // 通用文件加载逻辑:所有模型共享
    public List<T> Load(string filePath)
    {
        var items = new List<T>();
        if (!File.Exists(filePath)) return items;

        var lines = File.ReadAllLines(filePath);
        if (lines.Length <= 1) return items; // 跳过表头行

        var headerFields = lines[0].Split(';').Select(h => h.Trim()).ToArray();

        for (int i = 1; i < lines.Length; i++)
        {
            var line = lines[i].Trim();
            if (string.IsNullOrWhiteSpace(line)) continue;

            var entryFields = line.Split(';').Select(f => f.Trim()).ToArray();
            // 先验证格式,无效则跳过
            if (!IsValidEntry(entryFields, headerFields)) continue;

            // 转换为对应模型对象
            var item = ConvertToModel(entryFields, headerFields);
            if (item != null) items.Add(item);
        }

        return items;
    }

    // 抽象方法:子类实现模型特有验证规则
    protected abstract bool IsValidEntry(string[] entryFields, string[] headerFields);

    // 抽象方法:子类实现字段到模型对象的转换
    protected abstract T? ConvertToModel(string[] entryFields, string[] headerFields);
}

2. 策略模式实现模型特有逻辑

每个模型对应一个DataLoader<T>的子类,只需要专注实现自己的验证和转换逻辑,不用再重复写文件读取的代码。比如Person对应的加载器:

public class PersonLoader : DataLoader<Person>
{
    protected override bool IsValidEntry(string[] entryFields, string[] headerFields)
    {
        // 验证Person的字段规则:比如id必须是数字,name和surname不能为空
        if (entryFields.Length != headerFields.Length) return false;

        var idIndex = Array.IndexOf(headerFields, "id");
        if (!int.TryParse(entryFields[idIndex], out _)) return false;

        var nameIndex = Array.IndexOf(headerFields, "name");
        var surnameIndex = Array.IndexOf(headerFields, "surname");
        return !string.IsNullOrWhiteSpace(entryFields[nameIndex]) 
            && !string.IsNullOrWhiteSpace(entryFields[surnameIndex]);
    }

    protected override Person? ConvertToModel(string[] entryFields, string[] headerFields)
    {
        var idIndex = Array.IndexOf(headerFields, "id");
        var nameIndex = Array.IndexOf(headerFields, "name");
        var surnameIndex = Array.IndexOf(headerFields, "surname");

        return new Person
        {
            Id = int.Parse(entryFields[idIndex]),
            Name = entryFields[nameIndex],
            Surname = entryFields[surnameIndex]
        };
    }
}

Role和Task的加载器同理,只需要编写对应模型的验证和转换代码即可,代码量极少且逻辑清晰。

3. 工厂类管理全局实例与缓存

如果需要全局访问加载后的数据,可以创建一个静态工厂类DataLoaderFactory,用字典缓存每个模型的加载器实例和已加载的数据,替代单例的全局访问功能,同时更灵活:

public static class DataLoaderFactory
{
    private static readonly Dictionary<Type, object> _loaderCache = new();
    private static readonly Dictionary<Type, object> _dataCache = new();

    // 获取指定模型的加载器实例
    public static DataLoader<T> GetLoader<T>()
    {
        var modelType = typeof(T);
        if (!_loaderCache.ContainsKey(modelType))
        {
            // 自动查找对应加载器(命名约定:ModelName + Loader)
            var loaderType = Type.GetType($"{modelType.Namespace}.{modelType.Name}Loader");
            if (loaderType == null)
                throw new InvalidOperationException($"No loader found for model {modelType.Name}");
            
            _loaderCache[modelType] = Activator.CreateInstance(loaderType)!;
        }

        return (DataLoader<T>)_loaderCache[modelType];
    }

    // 获取指定模型的缓存数据(自动加载如果未缓存)
    public static List<T> GetCachedData<T>(string filePath)
    {
        var modelType = typeof(T);
        if (!_dataCache.ContainsKey(modelType))
        {
            var loader = GetLoader<T>();
            _dataCache[modelType] = loader.Load(filePath);
        }

        return (List<T>)_dataCache[modelType];
    }
}

4. 实际使用方式

在需要数据的地方,只需要一行代码就能获取全局缓存的模型数据:

// 获取Person数据(自动加载并缓存)
var persons = DataLoaderFactory.GetCachedData<Person>("Persons.txt");
// 获取Role数据
var roles = DataLoaderFactory.GetCachedData<Role>("Roles.txt");
方案优势
  • 消除冗余:通用逻辑只实现一次,每个模型只关注自己的特有规则,代码复用率大幅提升。
  • 易于扩展:新增模型时,只需要创建对应的XxxLoader子类,实现两个抽象方法即可,完全符合开闭原则,不需要修改现有代码。
  • 解耦依赖:模型和加载逻辑分离,单例模式带来的依赖过重问题彻底解决。
  • 全局访问:通过工厂类的缓存机制,确保数据只加载一次,满足全局访问需求,同时比单例更灵活(比如可以随时清空缓存重新加载)。
额外优化建议
  • 可以用属性标记(比如[MappingField("id")])来标记模型属性对应的字段名,然后通过反射自动完成字段到对象的转换,进一步减少转换代码的冗余。
  • 可以添加日志记录,把无效行的内容和原因记录下来,方便后续排查数据格式问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:41:57