我正在编写一个类,它从文件中读取行,处理它们,将它们存储在累加器中,并且当它达到阈值时,批量插入到SqlServer数据库中。如下所示:
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,有什么想法吗?
编辑:为了澄清,我无法读取,处理,然后导入文件,因为处理过的行太多,无法同时保存在内存中。
请随时注意我提供的例子中的任何其他错误!
发布于 2017-10-11 18:00:53
下面是我的建议,如下代码所示。我正在努力解决以下问题:
总结一下:您的示例代码显示了一个可能有多种原因需要更改的类。因此,我建议了这样一种设计,即每个类都有一个更改代码的理由;对于新特性,还需要实现新的类并将它们注入到导入程序中(就像您已经在做的那样,但不是针对流程的所有步骤)。
发布于 2017-10-11 14:44:52
但我禁不住感到,我正在打破FooImporterToSqlServer类的封装。它做的太多了:它读取文件,使用累加器,然后插入数据库。
封装?这不是封装问题。多做是一项单一的责任原则。封装问题来自可公开访问的getter。这会暴露你的状态。这会让我想知道这个类甚至没有提到的代码需要使用您的ConnectionString、累加器、处理器和LastHierarchyId?
至于遵循单一责任原则,你是。FooImporterToSqlServer的唯一职责是导入文件。我不希望在这里找到任何没有帮助的东西。
发布于 2017-10-11 17:00:53
正如@CandiedOrange所指出的,我不认为你正在破坏SRP。您有一个用于处理数据的类、一个用于积累数据的类和一个用于将数据存储到数据库中的类。每个班都有自己的工作。唯一可以更好地将其解耦的方法是有一个类来执行文件解析,而这个类将封装上述四个类,但在这种情况下,我认为这将是一个过头。
不过,我将讨论以下段落:
编辑:为了澄清,我无法读取,处理,然后导入文件,因为处理过的行太多,无法同时保存在内存中。
解决这个问题的方法之一就是你实现的那个问题。另一种方法是实现消费者-生产者模式,其中一个线程将填充行的集合,另一个线程将在将数据导入数据库时将其清空。这样,缓冲区就会“呼吸”,内存消耗就会减少。
不过,如果您正在使用一些批量导入技术,那么您采用的方法可能会有更好的性能。使用您的方法,访问数据库的往返次数将更少。
https://softwareengineering.stackexchange.com/questions/358962
复制相似问题