Tuesday, February 8, 2011

Creational Patterns - Builder Pattern

Definitions

* Construct a complex object from simple objects step by step.

* Defines an instance for creating an object but letting subclasses decide which class to instantiate and Allows a finer control over the construction process.

Where to use & benefits



Example

To build a house, we will take several steps:

  1. build foundation,
  2. build frame,
  3. build exterior,
  4. build interior.

Let's use an abstract class HouseBuilder to define these 4 steps. Any subclass of HouseBuilder will follow these 4 steps to build house (that is to say to implement these 4 methods in the subclass). Then we use a WorkShop class to force the order of these 4 steps (that is to say that we have to build interior after having finished first three steps). The TestBuilder class is used to test the coordination of these classes and to check the building process.

import java.util.*;



class WorkShop {

//force the order of building process

public void construct(HouseBuilder hb) {

hb.buildFoundation();

hb.buildFrame();

hb.buildExterior();

hb.buildInterior();

}

}



//set steps for building a house

abstract class HouseBuilder {

protected House house = new House();



protected String showProgress() {

return house.toString();

}



abstract public void buildFoundation();

abstract public void buildFrame();

abstract public void buildExterior();

abstract public void buildInterior();

}



class OneStoryHouse extends HouseBuilder {



public OneStoryHouse(String features) {

house.setType(this.getClass() + " " + features);

}

public void buildFoundation() {

//doEngineering()

//doExcavating()

//doPlumbingHeatingElectricity()

//doSewerWaterHookUp()

//doFoundationInspection()

house.setProgress("foundation is done");

}



public void buildFrame() {

//doHeatingPlumbingRoof()

//doElectricityRoute()

//doDoorsWindows()

//doFrameInspection()

house.setProgress("frame is done");

}



public void buildExterior() {

//doOverheadDoors()

//doBrickWorks()

//doSidingsoffitsGutters()

//doDrivewayGarageFloor()

//doDeckRail()

//doLandScaping()

house.setProgress("Exterior is done");

}



public void buildInterior() {

//doAlarmPrewiring()

//doBuiltinVacuum()

//doInsulation()

//doDryWall()

//doPainting()

//doLinoleum()

//doCabinet()

//doTileWork()

//doLightFixtureBlinds()

//doCleaning()

//doInteriorInspection()

house.setProgress("Interior is under going");

}

}



class TwoStoryHouse extends HouseBuilder {



public TwoStoryHouse(String features) {

house.setType(this.getClass() + " " + features);

}

public void buildFoundation() {

//doEngineering()

//doExcavating()

//doPlumbingHeatingElectricity()

//doSewerWaterHookUp()

//doFoundationInspection()

house.setProgress("foundation is done");

}



public void buildFrame() {

//doHeatingPlumbingRoof()

//doElectricityRoute()

//doDoorsWindows()

//doFrameInspection()

house.setProgress("frame is under construction");

}



public void buildExterior() {

//doOverheadDoors()

//doBrickWorks()

//doSidingsoffitsGutters()

//doDrivewayGarageFloor()

//doDeckRail()

//doLandScaping()

house.setProgress("Exterior is waiting to start");

}



public void buildInterior() {

//doAlarmPrewiring()

//doBuiltinVacuum()

//doInsulation()

//doDryWall()

//doPainting()

//doLinoleum()

//doCabinet()

//doTileWork()

//doLightFixtureBlinds()

//doCleaning()

//doInteriorInspection()

house.setProgress("Interior is not started yet");

}

}



class House {

private String type = null;

private List features = new ArrayList();



public House() {



}



public House(String type) {

this.type = type;

}



public void setType(String type) {

this.type = type;

}



public String getType() {

return type;

}



public void setProgress(String s) {

features.add(s);

}



public String toString() {

StringBuffer ff = new StringBuffer();

String t = type.substring(6);

ff.append(t + "\n ");

for (int i = 0; i < one =" new" two =" new" shop =" new">


Before

This implementation is arguably preferable. Each table class encapsulates a different layout.

class JTable_Table

{


private JTable m_table;



public JTable_Table(String[][] matrix)

{

m_table = new JTable(matrix[0].length, matrix.length);

TableModel model = m_table.getModel();

for (int i = 0; i < class="keyword">for (int j = 0; j < class="keyword">public Component get_table()

{

return m_table;

}

}



class GridLayout_Table

{


private JPanel m_table = new JPanel();



public GridLayout_Table(String[][] matrix)

{

m_table.setLayout(new GridLayout(matrix[0].length, matrix.length));

m_table.setBackground(Color.white);

for (int i = 0; i < class="keyword">for (int j = 0; j < class="keyword">new Label(matrix[j][i]));

}

public Component get_table()

{

return m_table;

}

}



class GridBagLayout_Table

{


private JPanel m_table = new JPanel();



public GridBagLayout_Table(String[][] matrix)

{

GridBagConstraints c = new GridBagConstraints();

m_table.setLayout(new GridBagLayout());

m_table.setBackground(Color.white);

for (int i = 0; i < class="keyword">for (int j = 0; j < gridx =" i;" gridy =" j;" class="keyword">new Label(matrix[i][j]), c);

}

}

public Component get_table()

{

return m_table;

}

}



public class BuilderDemo

{


public static String[][] read_data_file(String file_name)

{

String[][] matrix = null;

try

{

BufferedReader br = new BufferedReader(new FileReader(file_name));

String line, cell = "";

String[] tokens;

boolean first_line = true;

int row = 0, col = 0;

while ((line = br.readLine()) != null)

{

// Use "whitespace" to tokenize each line

// java.sun.com/docs/books/tutorial/extra/

// regex/pre_char_classes.html

tokens = line.split("\\s");

int i = 0;

if (first_line)

{

matrix = new String[Integer.parseInt(tokens[0])

][Integer.parseInt(tokens[1])];

i = 2;

first_line = false;

}

for (; i < class="keyword">if (tokens[i].equals(""))

{

matrix[col][row++] = cell;

cell = "";

col = 0;

}

else if (tokens[i].equals(""))

{

matrix[col++][row] = cell;

cell = "";

}

else

{

cell += " " + tokens[i];

}

}

matrix[col][row] = cell;

br.close();

}

catch (Exception ex)

{

ex.printStackTrace();

}

return matrix;

}

public static void main(String[] args)

{

JFrame frame = new JFrame("BuilderDemo - " + args[0]);

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

String[][] matrix = read_data_file("BuilderDemo.dat");

if (args[0].equals("JTable_Table"))

frame.getContentPane().add(new JTable_Table(matrix).get_table());

else if (args[0].equals("GridLayout_Table"))

frame.getContentPane().add(new GridLayout_Table(matrix).get_table());

else if (args[0].equals("GridBagLayout_Table"))

frame.getContentPane().add(new GridBagLayout_Table(matrix).get_table()

);

frame.pack();

frame.setVisible(true);

}

}







After

The main() creates a reader/parser, and configures it with a builder (an object that implements a standard interface and knows how to create one of many possible “results”. The reader reads and parses the common input and delegates the construction to the configured builder.

This implementation demonstrates the spirit of the Builder pattern, but it is more intricate, and probably cannot be justified for this fairly limited context.

class Reader

{


private Builder m_builder;



public Reader(Builder b)

{

m_builder = b;

}



public void construct(String file_name)

{

try

{

BufferedReader br = new BufferedReader(new FileReader(file_name));

String line, cell = "";

String[] tokens;

boolean first_line = true;

while ((line = br.readLine()) != null)

{

tokens = line.split("\\s");

int i = 0;

if (first_line)

{

m_builder.set_width_and_height(Integer.parseInt(tokens[0]),

Integer.parseInt(tokens[1]));

i = 2;

first_line = false;

}

for (; i < class="keyword">if (tokens[i].equals(""))

{

m_builder.build_cell(cell);

cell = "";

m_builder.start_row();

}

else if (tokens[i].equals(""))

{

m_builder.build_cell(cell);

cell = "";

}

else

{

cell += " " + tokens[i];

}

}

m_builder.build_cell(cell);

br.close();

}

catch (Exception ex)

{

ex.printStackTrace();

}

}

}



interface Builder

{


void set_width_and_height(int width, int height);

void start_row();

void build_cell(String value);

Component get_result();

}



class JTable_Builder implements Builder

{


private JTable m_table;

private TableModel m_model;

private int i = 0, j = 0;



public void set_width_and_height(int width, int height)

{

m_table = new JTable(height, width);

m_model = m_table.getModel();

}

public void start_row()

{

++i;

j = 0;

}

public void build_cell(String value)

{

m_model.setValueAt(value, i, j++);

}

public Component get_result()

{

return m_table;

}

}



class GridLayout_Builder implements Builder

{


private JPanel m_panel = new JPanel();



public void set_width_and_height(int width, int height)

{

m_panel.setLayout(new GridLayout(height, width));

m_panel.setBackground(Color.white);

}

public void start_row(){}

public void build_cell(String value)

{

m_panel.add(new Label(value));

}

public Component get_result()

{

return m_panel;

}

}



class GridBagLayout_Builder implements Builder

{


private JPanel m_panel = new JPanel();

private GridBagConstraints c = new GridBagConstraints();

private int i = 0, j = 0;



public void set_width_and_height(int width, int height)

{

m_panel.setLayout(new GridBagLayout());

m_panel.setBackground(Color.white);

}

public void start_row()

{

++i;

j = 0;

}

public void build_cell(String value)

{

c.gridx = j++;

c.gridy = i;

m_panel.add(new Label(value), c);

}

public Component get_result()

{

return m_panel;

}

}



public class BuilderDemo

{


public static void main(String[] args)

{

Builder target = null;

try

{

target = (Builder)Class.forName(args[0]).newInstance();

}

catch (Exception ex)

{

ex.printStackTrace();

}

Reader parser = new Reader(target);

parser.construct("BuilderDemo.dat");



JFrame frame = new JFrame("BuilderDemo - " + args[0]);

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

frame.getContentPane().add(target.get_result());

frame.pack();

frame.setVisible(true);

}

}



Example : 2



/* "Product" */

class Pizza {

private String dough = "";

private String sauce = "";

private String topping = "";



public void setDough(String dough) { this.dough = dough; }

public void setSauce(String sauce) { this.sauce = sauce; }

public void setTopping(String topping) { this.topping = topping; }

}



/* "Abstract Builder" */

abstract class PizzaBuilder {

protected Pizza pizza;



public Pizza getPizza() { return pizza; }

public void createNewPizzaProduct() { pizza = new Pizza(); }



public abstract void buildDough();

public abstract void buildSauce();

public abstract void buildTopping();

}



/* "ConcreteBuilder" */

class HawaiianPizzaBuilder extends PizzaBuilder {

public void buildDough() { pizza.setDough("cross"); }

public void buildSauce() { pizza.setSauce("mild"); }

public void buildTopping() { pizza.setTopping("ham+pineapple"); }

}



/* "ConcreteBuilder" */

class SpicyPizzaBuilder extends PizzaBuilder {

public void buildDough() { pizza.setDough("pan baked"); }

public void buildSauce() { pizza.setSauce("hot"); }

public void buildTopping() { pizza.setTopping("pepperoni+salami"); }

}



/* "Director" */

class Waiter {

private PizzaBuilder pizzaBuilder;



public void setPizzaBuilder(PizzaBuilder pb) { pizzaBuilder = pb; }

public Pizza getPizza() { return pizzaBuilder.getPizza(); }



public void constructPizza() {

pizzaBuilder.createNewPizzaProduct();

pizzaBuilder.buildDough();

pizzaBuilder.buildSauce();

pizzaBuilder.buildTopping();

}

}



/* A customer ordering a pizza. */

class BuilderExample {

public static void main(String[] args) {

Waiter waiter = new Waiter();

PizzaBuilder hawaiian_pizzabuilder = new HawaiianPizzaBuilder();

PizzaBuilder spicy_pizzabuilder = new SpicyPizzaBuilder();



waiter.setPizzaBuilder( hawaiian_pizzabuilder );

waiter.constructPizza();



Pizza pizza = waiter.getPizza();

}

}





No comments:

Post a Comment