DESIGN PATTERNS SAGA #16: REAL WORLD SITUATIONS WITH COMPOSITE

Composing objects without a pattern can make code really messy – code repetition happens a lot and maintenance is terrible. Fortunately, there is the Composite pattern that solves this problem completely. Using the Composite pattern we can encapsulate the code in just one place and compose the object more easily.

There are some frameworks that use the Composite pattern, such as JSF. It’s possible to reference all HTML components through the component’s classes from JSF.

This is a simple example of Web components being composed by the Composite pattern.

composite_diagram.PNG

Get the Design_Patterns_Saga_GitHub_Source_Code

1 – UIComponent generic class: Every web component will inherit UIComponent, so we will be using polymorphism with this abstract class.

There are some necessary methods to enable the composition of objects.

add(UIComponent uiComponent): Adds any component.
remove(UIComponent uiComponent): Removes any component.
toString(): Prints the components code when they are composed.

public abstract class UIComponent {

	List<UIComponent> uiComponents = new ArrayList<>();

	public UIComponent add(UIComponent uiComponent) {
		throw new UnsupportedOperationException
			("Feature not implemented at this level");
	}

	public UIComponent remove(UIComponent uiComponent) {
		throw new UnsupportedOperationException
			("Feature not implemented at this level");
	}

	public abstract String toString();

}

2 – Components classes: These are the classes that will extend UIComponent. Basically, they are the actual components.

Form: It is a component that will be composed by others. As we developers know, the form tag is vastly used in every web application, so we are going to add components inside the Form component.
InputText: We are going add InputText instances in the Form component.
LabelText: We are going to add LabelText instances in the Form component.

public class Form extends UIComponent {

	String name;

	public Form(String name) {
		this.name = name;
	}

	@Override
	public UIComponent add(UIComponent uiComponent) {
		uiComponents.add(uiComponent);

		return uiComponent;
	}

	@Override
	public UIComponent remove(UIComponent uiComponent) {
		uiComponents.remove(uiComponent);

		return uiComponent;
	}

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder
               ("
<form name='").append(name).append("'>");
		uiComponents.forEach(
                   e -> builder.append("\n").append(e));
		builder.append("\n</form>

");
		return builder.toString();
	}
}

public class InputText extends UIComponent {

	String name;
	String value;

	public InputText(String name, String value) {
		this.name = name;
		this.value = value;
	}

	@Override
	public String toString() {
		return new StringBuilder("<inputText name='")                 .append(name).append("' value='").append(value)                 .append("'/>").toString();
	}
}

public class LabelText extends UIComponent {

	String value;

	public LabelText(String value) {
		this.value = value;
	}

	@Override
	public String toString() {
		return new StringBuilder("<label value='")                                                                           .append(value).append("'/>").toString();
	}
}

3 – Unit Tests: Let’s use the Composite pattern now. We will instantiate and add the other components in the Form class. Then we will confirm if the Components code is correct with the assertion. There is another test with a composition of Maps showing this pattern with Java API.

public class CompositeTest {

	public static final int EXPECTED_MAP_SIZE = 3;

	@Test
	public void compositeTest() {
		Form mainForm = new Form("frmCustomer");

		LabelText lblCustomerName = new LabelText("Customer name:");
		InputText txtCustomerName = new InputText("txtCustomerName",
				"Juggy");

		LabelText lblCustomerProduct = new LabelText("Product:");
		InputText txtCustomerProduct = 
                 new InputText("txtCustomerProduct", "Alienware laptop");

		mainForm.add(lblCustomerName);
		mainForm.add(txtCustomerName);
		mainForm.add(lblCustomerProduct);
		mainForm.add(txtCustomerProduct);

		Assert.assertEquals("<label value='Customer name:'/>",
				lblCustomerName.toString());
		Assert.assertEquals("<inputText name='txtCustomerName'" 				+ " value='Juggy'/>",
				txtCustomerName.toString());
		Assert.assertEquals("<inputText name='txtCustomerProduct'" 				+ " value='Alienware laptop'/>",
				txtCustomerProduct.toString());
		Assert.assertEquals("<label value='Product:'/>",
				lblCustomerProduct.toString());
	}

	@Test
	public void javaAPICompositeTest() {
		Map<String, String> topWebComponents = new HashMap<>();

		topWebComponents.put("Component1", "HTML");
		topWebComponents.put("Component2", "InputText");

		Map<String, String> normalWebComponents = new HashMap<>();

		normalWebComponents.put("Component3", "LabelText");

		Map<String, String> container = new HashMap<>();

		container.putAll(topWebComponents);
		container.putAll(normalWebComponents);

		Assert.assertEquals(EXPECTED_MAP_SIZE, container.size());
	}

}

Summary of actions:

  1. Created the UIComponent generic class.
  2. Created add, remove and toString method inside UIComponent.
  3. Created the objects that will be inside the UIComponent.
  4. Composed the objects and printed the code.

To practice the Composite pattern you can create another Component class and add inside the Form and then print out the code to make the test pass. Be sure to use TDD (Test Driven Development).

DESIGN PATTERNS SAGA #16: REAL WORLD SITUATIONS WITH COMPOSITE

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s