构建器模式与数据封装

问题描述

使用builder模式时,如何保留OOP的封装原则?我的意思是,builder 应该在对象和使用它的代码之间提供抽象层,以便它可以逐部分构造,需要为我们通常在构造函数中传递的对象的每个参数制作 setter。在某些情况下,这再次可能是不可取的,因为我不希望客户端能够修改我必须通过构建器修改的值。举例说明我的意思如下:

public class Cat
{
    private string _race;
    private string _name;
    public Cat()
    {
         _race = "common";
         _name = string.Empty;
    }
    public void setRace(string race) { _race = race; }
    public void setName(string name) { _name = name; }
}

public class CatBuilder
{
    private Cat _objectUnderConstruction;
    public CatBuilder() { _objectUnderConstruction = new Cat(); }
    public CatBuilder WithName(string name)
    {
         _objectUnderConstruction.setName(name);
         return this;
    }
    public CatBuilder OfRace(string race)
    {
         _objectUnderConstruction.setRace(race);
         return this;
    }

}

这不是生产代码,我现在编写它时考虑的是演示,所以不要对它的构造方式感到生气。

在上面的例子中,需要设置猫的种族,因为我们需要该信息用于对象填充,所以我们需要将信息传递给它。同时我不希望任何人在我的猫的一生中改变它的种族(例如它会在处理过程中从埃及变为英国)通常我会摆脱访问器方法,但我需要构建器.这样,数据的封装会受到伤害(因为直接 get 和 set 不封装任何东西),我想避免它。

这个例子很简单,我可以在构造函数中传递参数,但是想象更大的类,那里有很多这样的字段,在这种情况下怎么办?我应该在内部传递一些配置对象(这几乎像构建器,但更简单,因此构建器毫无意义)还是将构建器本身传递给构造函数(这很奇怪,但我知道什么)?

我该怎么做?

解决方法

如果您的构建器与您的类紧密耦合,您可以创建正在构建的对象的 Builder 子类:

public class Cat
{
    private string _race;
    private string _name;
    public Cat()
    {
         _race = "common";
         _name = string.Empty;
    }
    private void setRace(string race) { _race = race; }
    private void setName(string name) { _name = name; }

    public class Builder
    {
         private Cat _objectUnderConstruction;
         public CatBuilder() { _objectUnderConstruction = new Cat(); }
         public CatBuilder WithName(string name)
         {
             _objectUnderConstruction.setName(name);
             return this;
         }
         public CatBuilder OfRace(string race)
         {  
             _objectUnderConstruction.setRace(race);
             return this;
         }
    }
}

这样,您就可以在 Builder 中访问 Cat 的私有字段和方法,并像使用 new Cat.Builder().OfRace("").OfName("").Build() 一样使用它。