Sonntag, 7. Oktober 2012

Builder Factory

A common problem is to initialize Java Collections within the builder pattern. The combination of the builder and the factory pattern solves this problem. Remember that the builder separates the construction of an object from its representation. The builder can therefore create objects which are variable initialized. The builder gives greater control over the construction process and isolates the construction code from the representation. This is easy in case of simple attribute types, but not for collections.

Collections have to be created first to store objects during the initialization process. Java inner classes support the isolation requirements of the creation process which is implemented in the builder. Isolation is one of the important principles of object oriented programming and often mentioned as a key principle of clean code. Why? The answer is simple: Isolation gives you the possibility to test your code.

The general rule is that only isolated loosely coupled objects are ready for testing. This rule is even more important whenever you start not with TDD which means you write your test code later. This is not the usual case but possible when the development team is separated from the test team. Take care and implement loosely coupled objects which are chained via Dependency Injection (DI) which is a principle integrated in the Java EE Standard as well as frameworks like Spring.

 Builder Factory Implementation:

import java.util.ArrayList;
import java.util.List;

public class BuilderFactoryBean {

    private final ItemBean itemBean;

    public BuilderFactoryBean(final Instance builderFactory) {
       
        itemBean = builderFactory.itemBean;
    }
   
    static class Instance {
       
        private final ItemBean itemBean = new ItemBean();
       
        public Instance cid(final String cid) {          
              
            itemBean.setCid(cid);
           
            return(this);
        }
       
        public Instance item(final String listItem) {          
                       
            initializeItemList();
           
            addItem(listItem);
           
            return(this);
        }

        private void initializeItemList() {
           
            if(null == itemBean.getListItems()) {
           
                final BuilderFactory<String> factory = new BuilderFactory<String>() {
   
                    @Override
                    public List<String> create() {
                       
                        return new ArrayList<String>();
                    }                   
                };
               
                itemBean.setListItems(factory.create());
            }
        }
       
        private void addItem(final String listItem) {
           
            itemBean.getListItems().add(listItem);
        }
       
        public BuilderFactoryBean build() {
           
            return(new BuilderFactoryBean(this));
        }
       
        private interface BuilderFactory<T> {
           
            public List<T> create();
        }
    }
   
    public ItemBean getItemBean() {
   
        return itemBean;
    }
}

 Item Bean:

import java.util.List;

public class ItemBean {

    private String cid;
    private List<String> listItems;

    public String getCid() {
        return cid;
    }

    public void setCid(String cid) {
        this.cid = cid;
    }

    public List<String> getListItems() {
        return listItems;
    }

    public void setListItems(List<String> listItems) {
        this.listItems = listItems;
    }

    @Override
    public String toString() {
       
        final StringBuilder strBuilder = new StringBuilder();
        strBuilder.append("cid=");
        strBuilder.append(cid);
       
        for(String listItem : listItems) {   
           
            appendItem(strBuilder, listItem);
        }
       
        return strBuilder.toString();
    }

    private void appendItem(final StringBuilder strBuilder, final String listItem) {
       
        strBuilder.append(",");
        strBuilder.append("item=");
        strBuilder.append(listItem);
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((cid == null) ? 0 : cid.hashCode());
        result = prime * result
                + ((listItems == null) ? 0 : listItems.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        ItemBean other = (ItemBean) obj;
        if (cid == null) {
            if (other.cid != null)
                return false;
        } else if (!cid.equals(other.cid))
            return false;
        if (listItems == null) {
            if (other.listItems != null)
                return false;
        } else if (!listItems.equals(other.listItems))
            return false;
        return true;
    }
}

 Builder Factory Test:

import org.junit.Assert;
import org.junit.Test;

public class BuilderFactoryBeanTest {

    @Test
    public void testBuilderFactory()  {
       
        final BuilderFactoryBean firstBuilderFactory = new BuilderFactoryBean.Instance().cid("cid-1").
                                                                                         item("item-1").item("item-2").build();
       
        final BuilderFactoryBean secondBuilderFactory = new BuilderFactoryBean.Instance().cid("cid-1").
                                                                                          item("item-1").item("item-2").build();
       
        Assert.assertEquals(firstBuilderFactory.getItemBean(), secondBuilderFactory.getItemBean());
    }
}



Der Rechtshinweis des Java Blog für Clean Code Developer ist bei der Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu beachten.