通常,我发现我必须实例化一堆对象,但我发现将这个实例化的参数作为一个人类可读的文本文件更容易,我手动编写并作为输入提供给程序.
例如,如果对象是a,Car
那么文件可能是一堆行,每行包含用制表符分隔的名称,速度和颜色(三个必需的构造函数参数):
My car 65 Red Arthur's car 132 Pink Old junk car 23 Rust brown
这对我来说很容易通过视觉检查,修改或由另一个程序生成.然后程序可以加载文件,获取每一行,解析相关参数,将它们提供给Car(string name, int speed, uint color)
构造函数并创建对象.
注意,这里是如何必须输入完成之前,它是用构造兼容一些工作:速度必须转换string
到int
与一个呼叫int.Parse
.通过查找英文颜色名称,颜色必须与RGB值匹配(可能程序将访问维基百科以找出每种颜色的值,或者参考预定义的地图名称 - > RGB某处).
我的问题是,从OOP的角度来看,谁应该进行这种解析?构造函数或调用构造函数的方法?
第一种选择,优点是简单.调用函数必须只做:
foreach(var row in input_file) list_of_objects_that_i_am_populating.Add(new Car(row));
并且所有丑陋的解析都可以很好地包含在构造函数中,无论如何它都没有太多其他代码,因此解析代码可以很容易地被读取和修改,而不会被非解析代码分散注意力.
缺点是代码重用超出了窗口,因为现在我的对象在臀部连接到输入格式(更糟糕的是,因为输入格式是临时的并且是手动编写的,所以它是短暂的并且可能不保证保持不变) .如果我在另一个程序中重用该对象,我决定稍微改变输入文件的格式是方便的,那么对象定义的两个版本现在是不同的.我经常发现自己在构造函数的注释部分定义输入格式,这看起来有点代码有点臭.
另一个缺点是我失去了批处理操作的能力.回想一下将颜色名称映射到值的早期示例问题:如果我使用需要1分钟处理每个单独请求的Web服务,无论该请求是要求转换一个颜色名称还是一百万个,该怎么办?对于一个非常大的输入文件,我会通过每行访问一次服务来大幅减慢我的应用程序,而不是为所有行提交一个大请求,然后根据回复实例化对象.
处理这种情况的"正确"方法是什么?我应该解析输入构造函数并将上述问题视为必须根据具体情况处理的特殊问题吗?我是否应该让我的调用方法进行解析(即使它可能已经因为错综复杂的程序逻辑而膨胀)?
我将创建和使用工厂方法通过设置/文件,csv加载,而不是将这些代码放在构造函数本身.
工厂版本1:
public class Car { ... your existing methods and data ... public static Car CreateFromCsv(string csv ) { .... } public static Car CreateFromFile(string fileName) { ...} }
或使用专用工厂:
public static class CarFactory { public static Car CreateFromCsv(string csv ) { .... } public static Car CreateFromFile(string fileName) { ...} }
或者专用的业务逻辑类:
namespace BusinessLogic; public class LoadCars { public Car ExecuteForCsv(string csv) { ...} public Car ExecuteForFile(string fileName) { ... } }
我的问题是,从OOP的角度来看,谁应该进行这种解析?构造函数或调用构造函数的方法?
通常,您应该避免在构造函数中执行此操作.这将违反单一责任原则.每种类型应仅负责该类型中所需的操作,而不是其他任何类型.
理想情况下,一个单独的类负责将数据解析为其正确的形式(而不是其他任何东西).创建实例的方法将获取该(解析的)数据并创建您的类型.