-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 
Author Message
 Post subject: [VALIDATOR]Immutable objects that always fulfill constraints
PostPosted: Thu Nov 06, 2008 4:59 am 
Newbie

Joined: Fri Nov 30, 2007 6:29 am
Posts: 14
Location: Oslo
Hi all,

I've been fiddling around with the builder pattern and have combined it with hibernate validator to create immutable value objects. See my example for a typical Address class, it should be pretty self explanatory.

I believe this is a safe and powerful way to build your domain: the mutable inner builder class is used whenever you create objects incrementally from an untrusted source, for example user input from a web form, reading from the db, recieving objects from a web service etc. The immutable outer class is used for all kind of object sharing inside and outside the domain. Additionally it is always in a valid state.

Comments appreciated.

Code:
package org.schwin;

import org.hibernate.validator.ClassValidator;
import org.hibernate.validator.Length;
import org.hibernate.validator.Min;
import org.hibernate.validator.NotNull;
import org.hibernate.validator.Pattern;

public final class Address {
   private final String streetname;
   private final int streetnumber;
   private final String postalcode;
   private final String postalplace;

   private Address(final String streetname, final int streetnumber,
         final String postalcode, final String postalplace) {
      super();
      this.streetname = streetname;
      this.streetnumber = streetnumber;
      this.postalcode = postalcode;
      this.postalplace = postalplace;
   }

   public final Builder builder() {
      return new Builder(this);
   }

   public final String getStreetname() {
      return streetname;
   }

   public final int getStreetnumber() {
      return streetnumber;
   }

   public final String getPostalcode() {
      return postalcode;
   }

   public final String getPostalplace() {
      return postalplace;
   }

   public static final class Builder implements org.schwin.Builder<Address>,
         Validator {
      private String streetname;
      private int streetnumber;
      private String postalcode;
      private String postalplace;

      private boolean valid = false;
      private boolean validated = false;
      private static final ClassValidator<Builder> validator = new ClassValidator<Builder>(
            Builder.class);

      public Builder() {
         super();
      }

      private Builder(final Address address) {
         super();
         this.streetname = address.streetname;
         this.streetnumber = address.streetnumber;
         this.postalcode = address.postalcode;
         this.postalplace = address.postalplace;
      }

      public final void setStreetname(final String streetname) {
         invalidate();
         this.streetname = streetname;
      }

      @Length(min = 1)
      public final String getStreetname() {
         return streetname;
      }

      public final void setStreetnumber(final int streetnumber) {
         invalidate();
         this.streetnumber = streetnumber;
      }

      @Min(1)
      public final int getStreetnumber() {
         return streetnumber;
      }

      @NotNull
      @Length(min = 4, max = 8)
      @Pattern(regex = "^\\d*$")
      public final String getPostalcode() {
         return postalcode;
      }

      public void setPostalcode(final String postalcode) {
         invalidate();
         this.postalcode = postalcode;
      }

      @NotNull
      @Length(min = 1, max = 50)
      public final String getPostalplace() {
         return postalplace;
      }

      public void setPostalplace(final String postalplace) {
         invalidate();
         this.postalplace = postalplace;
      }

      private void invalidate() {
         validated = false;
      }

      private void validate() {
         valid = 0 == validator.getInvalidValues(this).length;
         validated = true;
      }

      @Override
      public final boolean isValid() {
         if (!validated)
            validate();
         return valid;
      }

      @Override
      public final Address build() {
         if (!isValid())
            throw DEFAULT_INVALID_EXCEPTION;
         return new Address(streetname, streetnumber, postalcode,
               postalplace);
      }
   }
}

Code:
package org.schwin;

public interface Builder<T> {
   public final static IllegalStateException DEFAULT_INVALID_EXCEPTION = new IllegalStateException(
         "build() called while builder was in an invalid state");

   public abstract T build() throws IllegalStateException;
}

Code:
package org.schwin;

public interface Validator {
   public abstract boolean isValid();
}


Last edited by eirirlar on Thu Nov 06, 2008 5:19 am, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: [VALIDATOR]Immutable objects that always fulfill constra
PostPosted: Thu Nov 06, 2008 5:17 am 
Newbie

Joined: Fri Nov 30, 2007 6:29 am
Posts: 14
Location: Oslo
Here's a test that demonstrates some of the functionality:

Code:
package org.schwin;

import static org.junit.Assert.assertFalse;

import org.junit.Test;

public class AddressTest {
   @Test(expected = IllegalStateException.class)
   public void testInvalid() {
      Builder<Address> builder = new Address.Builder();
      Address address = builder.build();
   }

   @Test
   public void testValid() {
      Address.Builder mutableAddress = new Address.Builder();
      mutableAddress.setStreetname("Thorvald Meyers gate");
      mutableAddress.setStreetnumber(2);
      mutableAddress.setPostalcode("Something illegal");
      mutableAddress.setPostalplace("OSLO");
      assertFalse(mutableAddress.isValid());
      mutableAddress.setPostalcode("1234");
      Address immutableAddress = mutableAddress.build();
      Address.Builder mutableCopy = immutableAddress.builder();
      mutableCopy.setStreetname("Karl Johans gate");
      Address immutableAddress2 = mutableCopy.build();
   }
}


Top
 Profile  
 
 Post subject:
PostPosted: Thu Nov 06, 2008 12:40 pm 
Hibernate Team
Hibernate Team

Joined: Fri Oct 05, 2007 4:47 pm
Posts: 2536
Location: Third rock from the Sun
hey this is a cool pattern,
you should blog about it.

What about having all setters return "this", so you can write
it like
Code:
Address immutableAddress = new Address.Builder()
      .setStreetname("Thorvald Meyers gate")
      .setStreetnumber(2)
      .setPostalcode("Something illegal")
      .setPostalplace("OSLO")
      .build();


this is how I know the builder pattern.

_________________
Sanne
http://in.relation.to/


Top
 Profile  
 
 Post subject:
PostPosted: Thu Nov 06, 2008 3:00 pm 
Newbie

Joined: Fri Nov 30, 2007 6:29 am
Posts: 14
Location: Oslo
s.grinovero wrote:
hey this is a cool pattern,
you should blog about it.

What about having all setters return "this", so you can write
it like
Code:
Address immutableAddress = new Address.Builder()
      .setStreetname("Thorvald Meyers gate")
      .setStreetnumber(2)
      .setPostalcode("Something illegal")
      .setPostalplace("OSLO")
      .build();


this is how I know the builder pattern.


Thanks for the feedback. Yeah, I should have mentioned that: some frameworks (JSF) don't like the setters to return stuff, they like the JavaBeans to follow the JavaBeans standard, otherwise exceptions might be thrown (don't know why, but anyway it's Good Practice to follow the standard).

There's a simple workaround to get the chaining you describe above to work with JSF though, just use builder.streetname("Tha Str33t") like Josh Bloch describes in More Effective Java 2nd edition.

I'm creating a small demo app (seam, jsf, hibernate validator) to show more of the concepts I'm thinking about. I'll provide a link to it in a while.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Nov 08, 2008 10:50 am 
Hibernate Team
Hibernate Team

Joined: Fri Oct 05, 2007 4:47 pm
Posts: 2536
Location: Third rock from the Sun
looking forward for you demo,
I'm curious to see how immutable objects could be useful to Seam/JSF,
it doesn't look natural but could be an interesting reading.

_________________
Sanne
http://in.relation.to/


Top
 Profile  
 
 Post subject: demo
PostPosted: Thu Nov 27, 2008 7:35 pm 
Newbie

Joined: Fri Nov 30, 2007 6:29 am
Posts: 14
Location: Oslo
s.grinovero wrote:
looking forward for you demo,
I'm curious to see how immutable objects could be useful to Seam/JSF,
it doesn't look natural but could be an interesting reading.


I have a demo about this on my blog now. Check it out and let me know what you think :)

http://larsen.blogsite.org

Cheers, Eirik


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.