首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >SRP处理文件逐行

SRP处理文件逐行
EN

Software Engineering用户
提问于 2017-10-11 13:42:12
回答 3查看 186关注 0票数 3

我正在编写一个类,它从文件中读取行,处理它们,将它们存储在累加器中,并且当它达到阈值时,批量插入到SqlServer数据库中。如下所示:

代码语言:javascript
复制
class FooImporterToSqlServer
{
    private string ConnectionString { get;} //initilized via constuctor
    private AccumulatorCollection Accumulators { get;} //initilized via constuctor
    private FooProcessor Processor { get;} //initilized via constuctor
    private int LastHierarchyId { get; set;} //initilized via constuctor, starts at 0

    public void Import(string path)
    {
        var lines = File.ReadAllLines(path);
        foreach(var line in lines)
        {
            var processedLine = Processor.Process(line, ref LastHierarchyId);
            Accumulators.Add(processedLine);
            if(Accumulators.HaveAnyFull())
                UnloadAccumulators();
        }
    }

    private void UnloadAccumulator()
    {
        var fullAccumulators = Accumulators.GetFullAccumulators();
        foreach(var fullAccumulator in fullAccumulators)
        {
            //bulk insert black magic
        }
    }
}

但是我禁不住觉得我打破了FooImporterToSqlServer课程的单一责任。它做的太多了:它读取文件,使用累加器,然后插入数据库。但我也不知道如何区分这些问题,在这种情况下,我需要阻止他半途而废。特别维护不同插入之间的状态。通常情况下,会有类似于FooReaderToMemory,然后是FooInsertToSqlServer的东西,但在这种情况下,它们是相同的。对于我如何重构这样的过程以不破坏SRP,有什么想法吗?

编辑:为了澄清,我无法读取,处理,然后导入文件,因为处理过的行太多,无法同时保存在内存中。

请随时注意我提供的例子中的任何其他错误!

EN

回答 3

Software Engineering用户

回答已采纳

发布于 2017-10-11 18:00:53

下面是我的建议,如下代码所示。我正在努力解决以下问题:

  • 如果我需要改变提取数据的方式(目前是从文件中提取数据),我只想更改一个类(如果需要创建额外的类,没有问题);
  • 如果我需要更改数据库或导入数据的方式,我只想更改一个类(如果需要创建额外的类,则没有问题);
  • 如果我想更改整个进程(通过添加或更改步骤),我只想更改一个类;类FooImporter {私有IFooImporter导入器{ get;} //不再有连接字符串;现在导入器被注入私有AccumulatorCollection累加器{ get;} //通过constuctor私有FooProcessor处理器{ get;} //通过constuctor私有IFooExtractor extractor { get;} //启动不再打开文件,这是由提取器注入的私有IFooExtractor{get;set;set;;}//通过构造器初始化,从0公共void ()//开始,路径为注入抽取器{使用(Extractor.BeginExtract()) { while (Extractor.HasNext()) { var line = Extractor.GetNext();var processedLine = Processor.Process(line,ref LastHierarchyId);Accumulators.Add(ProcessedLine))的私有详细信息;如果(Accumulators.HaveAnyFull()) UnloadAccumulators();}}私有void UnloadAccumulator() { var fullAccumulators = Accumulators.GetFullAccumulators();foreach(var fullAccumulator in fullAccumulators) { Importer.Import(fullAcumulator);}}类SqlServerFooImporter : IFooImporter //您可以有不同的进口商{私有字符串ConnectionString { get;} //通过构造器私有空洞导入(累加器fullAcumulator) { //bulk插入黑色魔术}初始化。

总结一下:您的示例代码显示了一个可能有多种原因需要更改的类。因此,我建议了这样一种设计,即每个类都有一个更改代码的理由;对于新特性,还需要实现新的类并将它们注入到导入程序中(就像您已经在做的那样,但不是针对流程的所有步骤)。

票数 2
EN

Software Engineering用户

发布于 2017-10-11 14:44:52

但我禁不住感到,我正在打破FooImporterToSqlServer类的封装。它做的太多了:它读取文件,使用累加器,然后插入数据库。

封装?这不是封装问题。多做是一项单一的责任原则。封装问题来自可公开访问的getter。这会暴露你的状态。这会让我想知道这个类甚至没有提到的代码需要使用您的ConnectionString、累加器、处理器和LastHierarchyId?

至于遵循单一责任原则,你是。FooImporterToSqlServer的唯一职责是导入文件。我不希望在这里找到任何没有帮助的东西。

票数 5
EN

Software Engineering用户

发布于 2017-10-11 17:00:53

正如@CandiedOrange所指出的,我不认为你正在破坏SRP。您有一个用于处理数据的类、一个用于积累数据的类和一个用于将数据存储到数据库中的类。每个班都有自己的工作。唯一可以更好地将其解耦的方法是有一个类来执行文件解析,而这个类将封装上述四个类,但在这种情况下,我认为这将是一个过头。

不过,我将讨论以下段落:

编辑:为了澄清,我无法读取,处理,然后导入文件,因为处理过的行太多,无法同时保存在内存中。

解决这个问题的方法之一就是你实现的那个问题。另一种方法是实现消费者-生产者模式,其中一个线程将填充行的集合,另一个线程将在将数据导入数据库时将其清空。这样,缓冲区就会“呼吸”,内存消耗就会减少。

不过,如果您正在使用一些批量导入技术,那么您采用的方法可能会有更好的性能。使用您的方法,访问数据库的往返次数将更少。

票数 1
EN
页面原文内容由Software Engineering提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://softwareengineering.stackexchange.com/questions/358962

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档