Composing objects without a pattern can make code really messy – code repetition happens a lot and maintenance is terrible. Fortunately, there is the Composite design 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.
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 { ListuiComponents = 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. 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 (""); 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("
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("", lblCustomerName.toString()); Assert.assertEquals("", txtCustomerName.toString()); Assert.assertEquals(" ", txtCustomerProduct.toString()); Assert.assertEquals("", lblCustomerProduct.toString()); } @Test public void javaAPICompositeTest() { Map topWebComponents = new HashMap<>(); topWebComponents.put("Component1", "HTML"); topWebComponents.put("Component2", "InputText"); Map normalWebComponents = new HashMap<>(); normalWebComponents.put("Component3", "LabelText"); Map container = new HashMap<>(); container.putAll(topWebComponents); container.putAll(normalWebComponents); Assert.assertEquals(EXPECTED_MAP_SIZE, container.size()); } }
Summary of actions:
- Created the UIComponent generic class.
- Created add, remove and toString method inside UIComponent.
- Created the objects that will be inside the UIComponent.
- Composed the objects and printed the code.
To practice the Composite pattern you can create another Component class and add it inside the Form and then print out the code to make the test pass. Be sure to use TDD (Test Driven Development).