c# - 用于创建对象并在其他方法中填充其属性的模式
问题描述
我创建了一个 Photo 类,其中包含许多属性,例如大小、文件名、纬度/经度、位置等。
public class Photo {
public string FileName { get; set; }
public string Title { get; set; }
public double Size { get; set; }
public DateTime CapturedDate { get; set; }
public string Latitude { get; set; }
public string Longitude { get; set; }
public string LocationName { get; set; }
public byte[] Data { get; set; }
}
我想创建此类的一个对象并逐步填充其属性,其中每个步骤都需要在设置对象的某些值之前执行某种操作。
在一个快速而肮脏的解决方案中,我只是创建了一些 void 方法来填充对象属性,但我认为这是反模式。
var photo = new Photo();
photo.FileName = "Photo 1"
SetExifData(photo, photoStream);
SetLocationName(photo);
DoSomethingElse(photo);
void SetExifData(Photo photo, Stream photoStream) {
//Read exit data from the photo stream and update the object
photo.Latitude = "10,0";
photo.Longitude = "10,0";
photo.CapturedDate = ....
}
void SetLocationName(Photo photo){
//Call external API to get location name from lat/lng
photo.LocationName = "London"
}
void DoSomethingElse(Photo photo){
}
最好将此对象传递给某种管道或构建器。哪种模式适合这种情况?
解决方案
仅根据您告诉我的内容,构建器模式可能是要走的路。可以创建它以支持流畅的接口(如果足以满足您的 Pipeline 需求),或者作为设置更专业的 Pipeline 模式时使用的中间步骤。
class PhotoBuilder
{
// All properties goes here
private string Latitude { get; set; }
private string Longitude { get; set; }
...
public PhotoBuilder WithExifDataFromStream(Stream photoStream)
{
Latitude = ...;
Longitude = ...;
...
return this;
}
public Photo Build() => return new Photo(...);
}
我建议隐藏构建器中的所有状态,并且只在最后将对象创建为只读对象。它使推理更简单。
稍微不那么有吸引力的替代方法是将您的设置器隐藏为内部并从构建器中使用它们。出于纯度原因,我倾向于尽可能远离此类内部结构,但 YMMV。它当然有助于非常大的结构。即使使用这种方法,我也会敦促您只在最后公开对象以保持构建器和对象本身的分离。还要确保传递一个“不可变”对象,IEBuild
会在调用后立即清除对该照片的所有引用,以避免在构建后无意中更改对象。
空闲的沉思:
我发现对你在这样一个构建器中开始使用的逻辑类型进行相当限制是很好的。当然,这将取决于您的用例,但在较大的系统中,某些逻辑通常会在许多地方使用,而且我不止一次为从一个不如它干净的界面重构出多用途功能而头疼可能。
在这种情况下,这可以转换为SetLocation(double longitude, double latitude, string locationName)
and WithCaptureDate(...)
。这将使构建器能够在更多场景中使用,并允许您稍后在需要时在其之上创建特殊功能。
我发现它也有助于单元测试和组合。例如,您没有将构建器绑定到 Location API。当然,这也可以通过依赖注入来解决(ILocationResolver
或类似的),但是您可能正在查看一个使单一责任原则无效的对象。
最后,它非常依赖于您正在构建的系统和这个特定类的要求。我会错误地创建另一个包装器来执行所有位置和流解析的东西,但这是来自那些主要处理功能重用和可组合性要求很高的大型互连系统的人。如果系统非常小,这可能只会引入不必要的复杂性。
推荐阅读
- mysql - MySQL 5.7 中的并发查询执行速度很慢
- python - 如何过滤Django上的反向关系?
- ios - 使用 @State 变量不适用于从子视图更改 Body 中的视图状态
- reactjs - 如何使用 React 测试库和 Jest 测试所需的输入字段?
- php - 当我拉 php docker 图像时出现错误
- javascript - Grapesjs 自定义存储作为插件,没有获取选项并获取块部分的错误
- python - 变量不能被一组不特定的数字整除
- javascript - Uncaught ReferenceError: e is not defined / material ui data-grid
- snowflake-cloud-data-platform - 如何找到针对数据库/模式运行的 DDL 语句
- flutter - 将字符串传递给 Flutter 中的类?