用于创建对象并在其他方法中填充其属性的模式



我创建了一个照片类,其中包含许多属性,例如大小,文件名,纬度/经度,位置等。

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){
}

最好将此对象传递给一种管道或构建器。什么模式适合这种情况?

仅从您告诉我的内容来看,构建器模式可能是要走的路。可以创建它以支持流畅的接口(如果足以满足您的管道需求(,或者作为设置更专业的管道模式时使用的中间步骤。

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。它当然有助于非常大的结构。即使使用这种方法,我也敦促您只在最后公开对象,以保持构建器和对象本身的分离。还要确保你传递一个"不可变"的对象,即在调用Build后立即清除对该照片的任何引用,以避免在构建后无意中更改对象。

闲置的沉思:

我发现一开始就对你在这样一个构建器中放置的逻辑进行相当严格的限制是件好事。当然,这将取决于您的用例,但是在较大的系统中,某些逻辑通常会在许多地方使用,并且我不止一次地为从不那么干净的界面重构多用途功能而头疼。

在这种情况下,这可以转化为SetLocation(double longitude, double latitude, string locationName)WithCaptureDate(...).这将使构建器能够在更多场景中使用,并使您能够在以后需要时在其上创建特殊功能。

我发现它也有助于单元测试和组合。例如,您不会将构建器绑定到位置 API。当然,这也可以通过依赖注入(ILocationResolver或类似(来解决,但是您可能会看到一个使单一职责原则无效的对象。

最后,它在很大程度上取决于您正在构建的系统以及该特定类的要求。我会错误地创建另一个包装器来完成所有位置和流解析工作,但这来自主要处理功能重用和可组合性要求很高的大型互连系统的人。如果系统非常小,这可能只会带来不必要的复杂性。

最新更新