Definitions
* Construct a complex object from simple objects step by step.
Where to use & benefits
- Make a complex object by specifying only its type and content. The built object is shielded from the details of its construction.
- Want to decouple the process of building a complex object from the parts that make up the object.
- Isolate code for construction and representation.
- Give you finer control over the construction process.
- Related patterns include
- Abstract Factory, which focuses on the layer over the factory pattern (may be simple or complex), whereas a builder pattern focuses on building a complex object from other simple objects.
- Composite, which is often used to build a complex object
Example
To build a house, we will take several steps:
- build foundation,
- build frame,
- build exterior,
- 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 Listfeatures = 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
01This implementation is arguably preferable. Each table class encapsulates a different layout.
02class 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
03The 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.
05class 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