tags:

views:

469

answers:

9

I am reading Josh Bloch's book Effective Java and he suggests using a builder design pattern when building objects that have large amounts of members. From what I can see it isn't the vanilla design pattern but looks like his variation. I rather like the look of it and was trying to use it in a C# web application that I am writting. This is the code written in Java and works perfectly

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

When I put this into what I think is the C# equivalent

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

Then I get compiler warnings. Most of them "cannot declare instance members in a static class".

So my question is firstly what have I missed? If I have missed something, can I do it in the manner Josh Bloch recommends but in C#, and lastly, and this one important too, is this thread-safe?

+1  A: 

I'm not sure what Java is doing with the static class declaration, but in C#, a static class is one that only has class-level members and, by definition, can not be implemented into an instance. It's like the old VB difference between Class and Module.

Jekke
+2  A: 

In Java a nested class is by default associated with a particular instance of its containing class. An instance of the nested class can access variables and methods of the containing instance. If the nested class has the "static" keyword then it is not associated with an instance of the outer class. It is in this sense that Bloch uses the "static" keyword on the Builder class.

"Static" means something different when applied to a nested class in C#. I don't know what keyword you would use in C#, or even if it is necessary. Did you try leaving the static keyword out of the class definitions?

Use of "static" in Java class definitions is discussed in Item 18 of Effective Java.

DJClayworth
I think this is the standard definition in Java: Nested classes can either be static or non-static. Static nested classes are just called that; non-static nested classes are called inner classes.
Zach Scrivena
Link to the Java Tutorial that explains this: http://java.sun.com/docs/books/tutorial/java/javaOO/nested.html
Zach Scrivena
Oops, little terminology hiccup there.
DJClayworth
I am using the 2nd edition and you might be referring to the first in terms of item 18. In my book it talks about preferring interfaces to abstract classes. Maybe you meant item 22 (for me :)) prefer static member classes?
uriDium
+13  A: 

public static class in Java means that you define a static nested class. That means that it is logically contained in another class but instances of it can exist without a reference to it's outer class. A non-static nested class is called an "inner class" and instances of it can only ever exist in relation to an instance of the outer class.

In C# a static class is one that can't be instantiated and thus can't have any non-static members. There is no direct language-level equivalent to this construct in Java, but you can easily prevent instantiation of a Java class by providing only a private constructor.

Short Java recap:

  • All Classes defined inside another Class are "nested Classes"
  • nested Classes that are not static are called inner Classes
  • instances of inner Classes can only exist in relation to an instance of the outer Class
  • static nested Classes have no separate name
  • static nested Classes are largely independent from their outer class (except for some privileged access).

I'd be happy if some C# guru told us how inner/nested classes are handled in C#/.NET.

Joachim Sauer
I think you meant *nested* instead of *inner* in line 1.
Zach Scrivena
You're right, I mix them up all the time.
Joachim Sauer
"Nested classes are divided into two categories: static and non-static. Nested classes that are declared static are simply called static nested classes. Non-static nested classes are called inner classes." -- From http://java.sun.com/docs/books/tutorial/java/javaOO/nested.html
Michael Myers
I'm no guru, but c# inner classes can always exist independent of an instance of the outer class. I'm not aware of a construct in c# that only allows an inner class to exist via its outer class, except perhaps making it private so that only the outer class can use it.
Greg D
A: 

I don't know why C# is complaining, but I can say that the code is thread-safe. If you were creating two or more instances of Property at the same time, each in their own threads, you wouldn't run into any problems.

Michael Angstadt
+1  A: 

I think you can achieve pretty much the same effect if you create Builder as a top level class ( for that's exactly what it is in Java ) and create a factory method to receive the builder in order to keep the constructor private ( which in turn would let you return subclasses instances if needed).

The point is to let the builder perform the steps needed to create the object.

So ( without knowing much about C# you could try something like this )

// 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. 
}

The whole point of having Builder as an static inner class of property is to create a high coupling among the two ( as if they where one ). That's why build method in Builder calls the private "Property" constructor.

Probably in C# you could use an alternate artifact to create the same coupling.

OscarRyz
I think i am going to try this way. Firstly because I can still have the notion of required values that must be set through the constructor and optional values that can be created through calls to the builder.
uriDium
+2  A: 

saua has the right answer, but I would like to be clear about your example in particular:

In the C# version, you should remove the static keyword from the inner class. It doesn't mean the same thing as the Java version, and indeed the effect it has in the Java version is the normal behaviour for inner classes in C#.

Weeble
Is there a way to get the effect of a non-static inner class from Java in C#? So that each instance of the inner class has an implicit reference to its outer class instance?
Joachim Sauer
I'm curious too.
Michael Myers
Yes I would like to know too.
Software Monkey
As far as I'm aware, it's not possible. Of course, there's nothing to stop you keeping an explicit reference, but there's no shortcut.
Weeble
A: 

I will try removing the static keyword. My other thought was, as others have already suggested, was to create the builder class as a top level class.

uriDium
That's exactly what you should do - don't make the builder a top-level class.
Software Monkey
@SMonkey: If the static keyword is removed, then there won't be a way to access builder without creating the Property first. :(
OscarRyz
A: 

To answer several comments about how to get Java's inner class behavior in C#, it would seem that the reference to the enclosing class needs to be passed in the constructor of the inner class (from a quick Google - C# may have since added the capability).

public class Outer 
{

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

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

So C# just makes explicit what Java did implicitly, including explicitly needing the reference to access members of the outer class.

Personally, I have never liked the Java implicit accesses to the outer classes members, since it seems too easy to trip up and accidently break encapsulation - I nearly always create my inner classes as static and pass them a reference to the outer class.

Software Monkey
A: 

Assuming that your class has publicly settable properties corresponding to the builder members, you don't need Bloch's builder pattern in C#. You can use Object Initializers:

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};

    }
}

This won't be possible if you need more encapsulation.

oefe
I might need to add overloaded methods for some of the fields to use either an id or the actual text value so properties doesn't work out all that well for me.
uriDium