模式以创建对象并以其他方法填充其属性

时间:2020-06-15 20:41:40

标签: c# design-patterns

我创建了一个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; }
}

我想创建一个此类的对象并通过逐步的方法填充其属性,在该方法中,每个步骤都需要先执行某种操作,然后再在该对象上设置一些值。

在快速而又肮脏的解决方案中,我只是创建了一些无效方法来填充对象属性,但是我认为这是反模式。

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

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

1 个答案:

答案 0 :(得分:0)

只有您告诉我的内容,构建器模式才是可行的方法。可以创建它来支持流畅的界面(如果足够满足您的管道需求),或者作为设置更专门的管道模式时的中间步骤。

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(...)。这将使该构建器可以在更多场景中使用,并允许您稍后在需要时在其之上创建特殊功能。

我发现它也有助于单元测试和编写。例如,您没有将构建器绑定到Location API。当然,这也可以通过依赖注入(ILocationResolver或类似方法)来解决,但是您可能正在查看使单一职责原则无效的对象。

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