6

我正在阅读 Josh Bloch 的《Effective Java 》一书,他建议在构建具有大量成员的对象时使用构建器设计模式。据我所知,这不是香草设计模式,而是看起来像他的变体。我更喜欢它的外观,并试图在我正在编写的 C# Web 应用程序中使用它。这是用Java编写的代码并且可以完美运行

public class Property { 

    private String title;
    private String area;

    private int sleeps = 0;

    public static void main(String[] args) {

        Property newProperty = new Property.Builder("Test Property").Area("Test Area").Sleeps(7).build();

    }

    private Property(Builder builder) {
        this.title = builder.title;
        this.area = builder.area;
        this.sleeps =builder.sleeps;
    }

    public static class Builder{
        private String title;
        private String area;

        private int sleeps = 0;

        public Builder (String title){
            this.title = title;
        }

        public Builder Area(String area){
            this.area = area;
            return this;
        }

        public Builder Sleeps(int sleeps){
            this.sleeps = sleeps;
            return this;
        }

        public Property build() {
            return new Property(this);
        }
    }   
}

当我把它放入我认为是 C# 等价物时

 public class Property
    {
    private String title;
    private String area;

    private Property(Builder Builder)
    {
        title = Builder.title;
        area = Builder.area;
    }


    public static class Builder
    {
        // Required parameters
        private String title;
        private String area;

        // Optional parameters
        private int sleeps = 0;

        public Builder(String val)
        {
            this.title = val;
        }

        public Builder Area(String val)
        {
            this.area = val;
            return this;
        }

        public Builder Sleeps(int val)
        {
            this.sleeps = val;
            return this;
        }

        public Property build()
        {
            return new Property(this);
        }
    }
    }

然后我得到编译器警告。他们中的大多数“不能在静态类中声明实例成员”。

所以我的问题首先是我错过了什么?如果我遗漏了某些东西,我可以按照 Josh Bloch 推荐的方式但在 C# 中进行操作,最后,这一点也很重要,这是线程安全的吗?

4

9 回答 9

13

public static class在 Java 中意味着您定义了一个静态嵌套类。这意味着它在逻辑上包含在另一个类中,但它的实例可以在不引用它的外部类的情况下存在。非静态嵌套类称为“内部类”,它的实例只能相对于外部类的实例存在。

在 C# 中,astatic class是不能被实例化的,因此不能有任何非静态成员。Java 中没有与此构造直接等效的语言级别,但您可以通过仅提供私有构造函数来轻松防止 Java 类的实例化。

简短的 Java 回顾:

  • 在另一个类中定义的所有类都是“嵌套类”
  • 不是嵌套的类static称为内部类
  • 内部类的实例只能相对于外部类的实例存在
  • static嵌套类没有单独的名称
  • static嵌套类在很大程度上独立于它们的外部类(除了一些特权访问)。

如果一些 C# 大师告诉我们如何在 C#/.NET 中处理内部/嵌套类,我会很高兴。

于 2009-02-04T18:41:55.603 回答
3

我认为如果您将 Builder 创建为顶级类(因为这正是 Java 中的内容)并创建一个工厂方法来接收构建器以保持构造函数私有(这反过来会如果需要,让您返回子类实例)。

关键是让构建器执行创建对象所需的步骤。

所以(在不了解 C# 的情况下,您可以尝试这样的事情)

// My naive C# attempt:P

public class Property 
{

    public static void main( String []args ) 
    {
         Property p = Property.buildFrom( new Builder("title").Area("area").Etc() )
    }
    public static Property buildFrom( Builder builder ) 
    {
        return new Propert( builder );
    }

    private Property ( Builder builder ) 
    {
        this.area = builder.area;
        this.title = builder.title;
        // etc. 
    }
}
public class Builder 
{
    public Builder ( String t ) 
    {
       this.title = t;
    }

    public Builder Area( String area )
    {
       this.area = area;
       return this;
    }
    // etc. 
}

将 Builder 作为属性的静态内部类的全部意义在于在两者之间创建高度耦合(就好像它们在哪里一样)。这就是 Builder 中的 build 方法调用私有“Property”构造函数的原因。

可能在 C# 中,您可以使用替代工件来创建相同的耦合。

于 2009-02-04T18:43:43.510 回答
2

在 Java 中,嵌套类默认与其包含类的特定实例相关联。嵌套类的实例可以访问包含实例的变量和方法。如果嵌套类具有“static”关键字,则它不与外部类的实例相关联。正是在这个意义上,Bloch 在 Builder 类上使用了“static”关键字。

当应用于 C# 中的嵌套类时,“静态”意味着不同的东西。我不知道你会在 C# 中使用什么关键字,或者即使有必要。您是否尝试将 static 关键字从类定义中删除?

在 Java 类定义中使用“静态”在 Effective Java 的第 18 条中讨论。

于 2009-02-04T18:40:51.690 回答
2

saua 有正确的答案,但我想特别清楚你的例子:

在 C# 版本中,您应该从内部类中删除 static 关键字。它与 Java 版本的含义不同,实际上它在 Java 版本中的作用是 C# 中内部类的正常行为。

于 2009-02-04T18:48:25.277 回答
1

我不确定 Java 对静态类声明做了什么,但在 C# 中,静态类是只有类级成员的类,根据定义,它不能实现到实例中。这就像类和模块之间的旧 VB 区别。

于 2009-02-04T18:33:23.793 回答
0

我不知道为什么 C# 会抱怨,但我可以说代码是线程安全的。如果您同时创建两个或多个实例Property,每个实例都在各自的线程中,您不会遇到任何问题。

于 2009-02-04T18:43:18.830 回答
0

我将尝试删除 static 关键字。正如其他人已经建议的那样,我的另一个想法是将构建器类创建为顶级类。

于 2009-02-04T18:59:10.373 回答
0

为了回答一些关于如何在 C# 中获取 Java 内部类行为的评论,似乎需要在内部类的构造函数中传递对封闭类的引用(来自快速谷歌 - C# 可能已经添加了该功能) .

public class Outer 
{

...
void SomeMethod() {
    Inner             workerBee=new Inner(this);
    }
...

    class Inner
    private Outer outer;
    {
    Inner(Outer out) {
        outer=out;
        }
    }
}

因此,C# 只是明确说明了 Java 隐式所做的事情,包括明确需要引用来访问外部类的成员。

就个人而言,我从不喜欢 Java 对外部类成员的隐式访问,因为它似乎太容易出错并意外破坏封装 - 我几乎总是将我的内部类创建为静态并将它们传递给外部类的引用。

于 2009-02-04T19:57:31.067 回答
0

假设您的类具有与构建器成员对应的可公开设置的属性,则您不需要 C# 中的 Bloch 构建器模式。您可以使用对象初始化器:

public class Property 
{ 

    public String Title {get; set};
    public String Area {get; set};
    public int Sleeps {get; set};

    public static void main(String[] args)
    {

        Property newProperty = new Property {Title="Test Property", Area="Test Area", Sleeps=7};

    }
}

如果您需要更多封装,这将是不可能的。

于 2009-02-04T21:30:21.287 回答