DESIGN PATTERNS SAGA #14: REAL WORLD SITUATIONS WITH VISITOR

Sometimes we need to implement different methods for a POJO with different business requirements. We could certainly create several methods inside the POJO but we would not be utilizing the main benefits of powerful code – cohesion and low coupling. When we want to encapsulate different business requirements inside a cohesive class we can use the Visitor Pattern!

Basically, when using Visitor Pattern we encapsulate the business requirements inside the Visitor classes making our code very flexible.

visitor_diagram.PNG

Get the Design_Patterns_Saga_GitHub_Source_Code

Let’s see a bad example of code if we aren’t using the Visitor Pattern.

1 – Generic Interface: It’s basically the interface that declares the main method to calculate shipping.

public interface CardDevice {
	public double calculateShipping();
}

2 – Business POJOs: Now we have all the POJOs implementing the Generic interface in order to define the behavior to calculate shipping.

public class Mobile implements CardDevice {
	@Override
	public double calculateShipping() {
		return 6;
	}
}

public class WiredPos implements CardDevice {
	@Override
	public double calculateShipping() {
		return 9;
	}
}

public class WirelessPos implements CardDevice {
	@Override
	public double calculateShipping() {
		return 7;
	}
}

3 – Invoker: Now we can see the problem. We have the code very coupled with the specific calculateShipping method.

public class CardDeviceOrder implements CardDevice {

	private List<CardDevice> cardDevices = new ArrayList<>();

	public CardDeviceOrder() {
		super();
	}

	public void addPart(CardDevice cardDevice) {
		cardDevices.add(cardDevice);
	}

	public List<CardDevice> getCardDevices() {
		return Collections.unmodifiableList(cardDevices);
	}

	public double calculateShipping() {
		double shippingCost = 0;
		for (CardDevice cardDevice : cardDevices) {
			shippingCost += cardDevice.calculateShipping();
		}
		return shippingCost;
	}

}

4 – Unit Test: Let’s see if the calculateShipping method is working:

public class BadExampleVisitorTest {

	public static final double EXPECTED_SHIPPING_COST = 22.0;

	@Test
	public void visitorBadExampleTest() {
		CardDeviceOrder order = new CardDeviceOrder();
		order.addPart(new Mobile());
		order.addPart(new WiredPos());
		order.addPart(new WirelessPos());

		double shippingCost = order.calculateShipping();

		Assert.assertTrue(EXPECTED_SHIPPING_COST == shippingCost);
	}

}

Now let’s see the same example using the Visitor Pattern. Now you will realize that we will have more flexibility in the code.

1 – Visitor interface: We are going to define every business requirement inside this Visitor in order to manipulate these rules in an uncoupled mode. We are going to implement the rules inside the Visitor implementation.

public interface CardDeviceVisitor {
	void visit(WirelessPos wirelessPos);
	void visit(Mobile mobile);
	void visit(WiredPos wiredPos);
	void visit(CardDeviceOrder cardDeviceOrder);
}

2 – Visitor implementations: You will realize that the business requirements are implemented directly in the Visitor classes. They implement the Visitor generic interface and execute what is necessary for the business.

CardDeviceDisplayVisitor: Displays basic device information.
CardDeviceShippingVisitor: Calculates the shipping from each card device.

public class CardDeviceDisplayVisitor implements CardDeviceVisitor {

	@Override
	public void visit(WirelessPos wirelessPos) {
		System.out.println("We have a: " +  wirelessPos);
	}

	@Override
	public void visit(Mobile mobile) {
		System.out.println("We have a: " + mobile);
	}

	@Override
	public void visit(WiredPos wiredPos) {
		System.out.println("We have: " + wiredPos);
	}

	@Override
	public void visit(CardDeviceOrder cardDeviceOrder) {
		System.out.println("We have an: "+ cardDeviceOrder);
	}

}

public class CardDeviceShippingVisitor implements CardDeviceVisitor {

	double shippingValue = 0;

	@Override
	public void visit(WirelessPos wirelessPos) {
		System.out.println("Calculating Wireless Pos shipping.");
		shippingValue += 15;
	}

	@Override
	public void visit(Mobile mobile) {
		System.out.println("Calculating Mobile shipping");
		shippingValue += 3;
	}

	@Override
	public void visit(WiredPos wiredPos) {
		System.out.println("Calculating Wired Pos shipping.");
		shippingValue += 9;
	}

	@Override
	public void visit(CardDeviceOrder order) {
		System.out.println("If they bought more than 3 things "
				+ "there will be a discount in the shipping.");
		List<CardDevice> cardDevices = order.getCardDevices();
		if (cardDevices.size() > 3) {
			shippingValue -= 5;
		}
		System.out.println("Shipping amount is: " + shippingValue);
	}

	public double getShippingValue() {
		return shippingValue;
	}
}

3 – Business POJOs: We can clearly see that the POJOs are already different. They are not invoking the method to execute the specific business requirements. Now we invoke a method from a generic visitor. It decouples the code and the business requirement is manipulated inside the Visitor. Check it out:

public class Mobile implements CardDevice {
	@Override
	public void accept(CardDeviceVisitor visitor) {
		visitor.visit(this);
	}
}

public class WiredPos implements CardDevice {
	@Override
	public void accept(CardDeviceVisitor visitor) {
		visitor.visit(this);
	}
}

public class WirelessPos implements CardDevice {
	@Override
	public void accept(CardDeviceVisitor visitor) {
		visitor.visit(this);
	}
}

4 – Orchestrator, Invoker: The CardDeviceOrder class is responsible for orchestrating the cardDevices classes and invoking every method from the Visitors according to the specific Visitor class implementation.

public class CardDeviceOrder implements CardDevice {

	private List<CardDevice> cardDevices = new ArrayList<>();

	public CardDeviceOrder() {
		super();
	}

	public void addPart(CardDevice cardDevice) {
		cardDevices.add(cardDevice);
	}

	public List<CardDevice> getCardDevices() {
		return Collections.unmodifiableList(cardDevices);
	}

	@Override
	public void accept(CardDeviceVisitor visitor) {
		for (CardDevice cardDevice : cardDevices) {
			cardDevice.accept(visitor);
		}
		visitor.visit(this);
	}
}

5 – Unit Tests: Let’s test our Visitors! You will see the business methods being invoked through the Visitor classes.

First, add all the devices to the list of CardDeviceOrder.
Second, instantiate both visitors and pass them into the accept method from CardDeviceOrder.
Third, confirm the calculated shipping value is correct.

public class GoodExampleVisitorTest {

	public static final double EXPECTED_SHIPPING_COST = 27.0;

	@Test
	public void visitorGoodExampleTest() {
		CardDeviceOrder order = new CardDeviceOrder();
		order.addPart(new WirelessPos());
		order.addPart(new Mobile());
		order.addPart(new WiredPos());

		CardDeviceShippingVisitor shipping
			= new CardDeviceShippingVisitor();

		order.accept(shipping);
		order.accept(new CardDeviceDisplayVisitor());

		Assert.assertTrue(EXPECTED_SHIPPING_COST ==
				shipping.getShippingValue());
	}

}

Summary of actions:

  1. Created the Visitor generic interface.
  2. Implemented the Visitor classes with the necessary business requirements.
  3. Created the business POJOs delegating responsibilities to the Visitor classes.
  4. Created the orchestrator class responsible to manipulate the invocations.

To practice the Visitor Pattern you can create another Visitor class with another specific business requirement, for example calculating the rent from every card device. Create another test method to make sure it works. Be sure to use TDD (Test Driven Development).

DESIGN PATTERNS SAGA #14: REAL WORLD SITUATIONS WITH VISITOR

DESIGN PATTERNS SAGA #13: REAL WORLD SITUATIONS WITH MEMENTO

What if we need to keep many different states of an object? We could keep the information inside objects with a lot of setters and make the code a big mess. This would indeed work but it would be awful to change anything in the future. As we developers know, software that does not change is dead.

When we need to manipulate different states of an object we can use the Memento Pattern.

memento_diagram.PNG

Get the Design_Patterns_Saga_GitHub_Source_Code

1 – Memento: Nothing special here, it is just the object information we encapsulated in order to keep only part of the Customer information.

 public class CustomerMemento {

	private String name;
	private String phone;

	public CustomerMemento(String name, String phone) {
		this.name = name;
		this.phone = phone;
	}

        // Getters and setters, hashcode and equals omitted.

}

2 – Caretaker: it encapsulates the Memento object history in a Stack.

Methods:
save: it saves the state of the CustomerMemento object, pushing it to the top of the Stack.

revert: it reverts the state of the CustomerMemento to the last saved state.

public class Caretaker {

	private Stack<CustomerMemento> customerHistory = new Stack<>();

	public void save(Customer customer) {
		customerHistory.push(customer.save());
	}

	public void revert(Customer customer) {
		customer.revert(customerHistory.pop());
	}

}

3 – Originator: This is the main Customer object, the Originator of the information. There are two important methods here:

save: sets the name and phone from the Customer in the CustomerMemento object and returns the CustomerMemento.

revert: it reverts the Customer with the CustomerMemento information.

 public class Customer implements Serializable {

	private static final long serialVersionUID = 574314732983681621L;
	private String name;
	private String address;
	private String phone;

	public Customer(String name, String address, String phone) {
		this.name = name;
		this.address = address;
		this.phone = phone;
	}

        public CustomerMemento save() {
		return new CustomerMemento(name, phone);
	}

	public void revert(CustomerMemento customer) {
		this.name = customer.getName();
		this.phone = customer.getPhone();
	}

        // Getters and setters, hashcode and equals omitted.
}

Summary of actions:

  1. Created the Memento class with the necessary information.
  2. Created the Caretaker class.
  3. Declared the Stack to keep the Memento information into the Caretaker.
  4. Created the Originator class with all the Customer’s information.
  5. Created the save method that returns the CustomerMemento object.
  6. Created the revert method to put CustomerMemento information into Customer.

To practice the Memento Pattern you can create other CaretakerOriginator and Memento classes and make it work. Create another test method to make sure it works. Be sure to use TDD (Test Driven Development).

DESIGN PATTERNS SAGA #13: REAL WORLD SITUATIONS WITH MEMENTO

DESIGN PATTERNS SAGA #12: REAL PROJECT SITUATIONS WITH MEDIATOR

What should be done when we need to use a functionality that may be repeated in several classes? Should we repeat the code in all the classes? If the behavior of this functionality changes, you will be obligated to change it in the entire project and test everything. This would not be cool. Fortunately, there is a solution for this problem – we can use the Mediator Pattern!

Using the Mediator Pattern can encapsulate this behavior inside just one class allowing us to reuse this method in every necessary class. Doing this will result in cohesive code with low coupling.

mediator_diagram.PNG

Get the Design_Patterns_Saga_GitHub_Source_Code

1 – Mediator: This class will be responsible to mediate the necessary behavior of calculating the Discount. This pattern is pretty straightforward, it simply mediates functionality.

public class DiscountCalculatorMediator {

	private BigDecimal productValue;

	private BigDecimal DISCOUNT_RATE = new BigDecimal("0.10");

	public DiscountCalculatorMediator(BigDecimal productValue) {
		this.productValue = productValue;
	}

	public BigDecimal calculate() {
		return productValue.
                  subtract(productValue.multiply(DISCOUNT_RATE));
	}

}

2 – Product POJO: We are going to mediate the calculateDiscount method to the Mediator class, and therefore we are not going to have the same code in different parts of the system. We will have this code only in the Mediator.

 public class Product {

	private BigDecimal productValue;

	public Product(BigDecimal productValue) {
		this.productValue = productValue;
	}

	public BigDecimal calculateDiscount() {
		return new DiscountCalculatorMediator
                  (productValue).calculate();
	}

}

Summary of actions:

  1. Created the Mediator class.
  2. Created the calculateDiscount behavior inside the Mediator.
  3. Reused the Mediator in the POJO.

To practice the Mediator Pattern you can create another Mediator class where you encapsulate a specific behavior and just reuse it in a method. Create another test method to make sure it works. Be sure to use TDD (Test Driven Development).

DESIGN PATTERNS SAGA #12: REAL PROJECT SITUATIONS WITH MEDIATOR

DESIGN PATTERNS SAGA #11: REAL PROJECT SITUATIONS WITH ITERATOR

Most of the time we use the Iterator from the Collections API. It’s very important to know how to implement a customized Iterator in order to understand what is happening behind the scenes. If we read Java source code, for example, we are going to find out that the elements from ArrayList are actually manipulated in a normal array. If we read Java source code we can greatly improve our programming skills. I strongly recommend that you make this a habit.

Iterator diagram:

iterator_diagram.PNG

Get the Design_Patterns_Saga_GitHub_Source_Code

1 – Iterator: This is the main class where we are able to iterate the objects. The first detail to be aware of is that we are implementing the Iterable interface using String as a generic type.

We are declaring an array of products and the index.

In the constructor, we are initializing the array of products with the size of 10 and assigning zero to the index.

In the add method, we are adding a new element inside the array. We simply check if the index is the same size as the initial size of the array, in this case, 10. If so, we are going to make the array 5 times larger than before. Following this process, we copy the old array information to the largerArray. Finally, we add the product to the final array.

In the iterator method, we are using an anonymous inner class that implements Iterator. If we are using the Iterator interface we must implement all of its methods.

Again, we are declaring the index to control the iteration.

Let’s see what happens on the Iterator methods:

hasNext: verifies if there is another element to be iterated. If the index from the iterator is lower than the size of the array and the element is different from null it means we have the next element.

next: gets the next element from the products. It simply refers to the next element from the array and returns it.

remove: it copies the old array without the removed position, returning the removed element.

public class ProductRepository implements Iterable<String> {

	private String[] products;
	private int index;

	public ProductRepository() {
		products = new String[10];
		index = 0;
	}

	public void add(String product) {
		if (index == products.length) {
			String[] largerProducts = new String[
                            products.length + 5];
			System.arraycopy(products, 0, largerProducts,
                            0, products.length);
			products = largerProducts;
			largerProducts = null;
		}

		products[index++] = product;
	}

	@Override
	public Iterator<String> iterator() {
		return new Iterator<String>() {

			private int currentIndex = 0;

			@Override
			public boolean hasNext() {
				return currentIndex < products.length
                                  && products[currentIndex] != null;
			}

			@Override
			public String next() {
				return products[currentIndex++];
			}

			@Override
			public void remove() {
				System.arraycopy(products, currentIndex + 1,
                                  products, currentIndex, products.length - 1);
			}

		};
	}
}

2 – Unit Test: Now we are going to test the Iterator.

customIteratorTest: we add some elements, we get the iterator, remove two elements and check if the remaining element is “Tablet”.

javaAPIIteratorTest: we are just using the iterator from the Java Collections API, removing all the elements and checking if the Set Collection is empty.

 public class IteratorTest {

	@Test
	public void customIteratorTest() {
		ProductRepository repo = new ProductRepository();

		repo.add("Guitar");
		repo.add("Notebook");
		repo.add("Tablet");

		Iterator<String> productIterator = repo.iterator();
		productIterator.remove();
		productIterator.remove();

		Assert.assertEquals(productIterator.next(), "Tablet");
	}

	@Test
	public void javaAPIIteratorTest() {
		Set<String> simpsons = new HashSet<>();

		simpsons.add("Homer");
		simpsons.add("Bart");
		simpsons.add("Margie");

		Iterator<String> simpsonsItr = simpsons.iterator();

		while (simpsonsItr.hasNext()) {
			String name = simpsonsItr.next();
			System.out.println(name);
			simpsonsItr.remove();
		}

		Assert.assertEquals(0, simpsons.size());
	}

}

Summary of actions:

  1. Created the Iterator class.
  2. Encapsulated an array inside the Iterator class.
  3. Implemented the Iterable interface in the Iterator class.
  4. Overrode the iterator method.
  5. Created an anonymous inner class of the Iterator type.
  6. Overrode all the methods from the Iterator interface.

To practice the Iterator Pattern you can create another Repository and iterate over the elements from it. Create another test method to make sure it works. Be sure to use TDD (Test Driven Development).

DESIGN PATTERNS SAGA #11: REAL PROJECT SITUATIONS WITH ITERATOR

DESIGN PATTERNS SAGA #10: REAL PROJECT SITUATIONS WITH FACADE

Sometimes we have to deal with complex methods where there are many processes and it’s necessary to orchestrate them every time and repeat the code many times.

There are other situations in which the methods are very confusing and difficult to understand. How can a new developer discover what method to invoke if everything is confusing? He would probably ask a more experienced developer to explain what is happening. Even developers that already know the project might be confused and would spend a good amount of time trying to understand what is happening.

Fortunately, there is a solution for this problem! We can use the Facade Pattern and make it easy to understand! Several actions can be encapsulated and orchestrated inside a Facade!

Important points about the Facade Pattern:

  • Simplifies readability.
  • Avoids code repetition.
  • Simplifies legacy code processes to use.
  • Encapsulates actions in one simplified method.

facade_diagram.PNG

Get the Design_Patterns_Saga_GitHub_Source_Code

1 – Classes to be orchestrated: The main reason to use the Facade Pattern is to orchestrate some confusing/complex tasks so they become easier to understand. In this example, we want to execute the logic to apply a discount. It’s not so simple because there are many processes to accomplish it. Now we are going to see all the necessary classes to execute all the logic.

public class CustomerService {

	private Map<Long, Customer> customers = new HashMap<>();

	public CustomerService() {
		customers.put(1L, new Customer("Vader", "Imperial City",
                  new Date()));
	}

	public Customer findProductBy(long idCustomer) {
		return customers.get(idCustomer);
	}

}

public class ProductService {

	private Map<Long, Product> products = new HashMap<>();

	public ProductService() {
		products.put(1L, new Product(1L, "POS", new BigDecimal("100")));
	}

	public Product findProductBy(long idProduct) {
		return products.get(idProduct);
	}

}

public class DiscountService {

	public void applyDiscount(Customer customer, Product product) {
		System.out.println("Applying discount for customer: "
                                 + customer.getName() +
				  " product: " + product.getName());
	}

}

public class Product {
	private long idProduct;
	private String name;
	private BigDecimal price;

	public Product(long idProduct, String name, BigDecimal price) {
		this.idProduct = idProduct;
		this.name = name;
		this.price = price;
	}

        // Getters and setters omitted

}

public class Customer {

	private String name;
	private String address;
	private Date birthDate;

	public Customer(String name, String address, Date birthDate) {
		this.name = name;
		this.address = address;
		this.birthDate = birthDate;
	}

       // Getters and setters omitted
}

public class ApplyDiscountRequest {

	private long idCustomer;
	private long idProduct;
	private long idDiscount;

	public ApplyDiscountRequest(long idCustomer, long idProduct,
                long idDiscount) {
		this.idCustomer = idCustomer;
		this.idProduct = idProduct;
		this.idDiscount = idDiscount;
	}

       // Getters and setters omitted
}

2 – Facade: Now let’s organize the code! In order to apply a discount, there are many actions to be executed, that’s why we are going to use the Facade Pattern to orchestrate all these processes.


public class DiscountFacade {

	private CustomerService customerService = new CustomerService();
	private ProductService productService = new ProductService();
	private DiscountService discountService = new DiscountService();

	public boolean applyDiscount(ApplyDiscountRequest discountRequest) {
		Customer customer = customerService
                   .findProductBy(discountRequest.getIdCustomer());

		Product product = productService
                   .findProductBy(discountRequest.getIdProduct());

		discountService.applyDiscount(customer, product);
		boolean isDiscountApplied = true;

		return isDiscountApplied;
	}

}

3 – Unit Test: Let’s test if our Facade is working! It’s very simple now, we just have to invoke the Facade!

public class FacadeTest {

	private DiscountFacade facade;

	@Before
	public void init() {
		facade = new DiscountFacade();
	}

	@Test
	public void applyDiscountTest() {
		boolean isDiscountApplied = facade.applyDiscount(
                 new ApplyDiscountRequest(1L, 1L, 1L));

		Assert.assertTrue(isDiscountApplied);
	}

}

Summary of actions:

  1. Created the classes to be orchestrated.
  2. Created the Facade class.
  3. Orchestrated all the necessary logic in the Facade.
  4. Encapsulated all the actions to apply a discount to a customer.

To practice the Facade Pattern you can create other actions to be orchestrated and simplified. For example, changing the customer’s plan or anything you want to practice the Pattern. Create another test method to make sure it works. Be sure to use TDD (Test Driven Development).

DESIGN PATTERNS SAGA #10: REAL PROJECT SITUATIONS WITH FACADE

DESIGN PATTERNS SAGA #9: REAL PROJECT SITUATIONS WITH ADAPTER

When we can’t modify a legacy code and have to adapt the new code, it’s a good idea to use the Adapter Pattern. We can use this Pattern when we are using a legacy Service which we can’t change and the response is a really big object with unintelligible names. In the new Service, we don’t want to receive all the big object information from the legacy Service and we want to avoid using the big object directly. To do this, we can use the generic interface to create the Adapter and adapt the legacy POJO in the way we want.

adapter_diagram.PNG

Get the Design_Patterns_Saga_GitHub_Source_Code

1 – Adapter interface: In order to get any class adapted to what we need, first we must create a class or an interface that is generic so we can use polymorphism.

public interface Customer {

	public String getId();
	public String getFirstName();
	public String getLastName();
	public String getEmail();

}

2 – POJOs we want to adapt: These are the POJOs we must adapt to the Service process.

public class CustomerFromLegacyCode {

	private String cn;
	private String surname;
	private String givenName;
	private String mail;

        // Constructor, getters and setters omitted.

}

public class CustomerCSV {

	private int id;
	private String firstname;
	private String lastname;
	private String emailAddress;

	public CustomerCSV(String values) {
		StringTokenizer tokenizer = new StringTokenizer(values, ",");
		if (tokenizer.hasMoreElements()) {
			id = Integer.parseInt(tokenizer.nextToken());
		}
		if (tokenizer.hasMoreElements()) {
			firstname = tokenizer.nextToken();
		}
		if (tokenizer.hasMoreElements()) {
			lastname = tokenizer.nextToken();
		}
		if (tokenizer.hasMoreElements()) {
			emailAddress = tokenizer.nextToken();
		}
	}

        // Getters and setters omitted.

}

3 – Adapted classes: These classes are the Adapters! Basically, we implement the Adapter interface and encapsulate the POJO that must be adapted in the constructor of the Adapter. Then, we just override the Adapter interface methods and delegate the method invocation to the class to be adapted to get the correct value, and it’s done! We can use the adapters now!

public class CustomerAdapterCSV implements Customer {

	private CustomerCSV instance;

	public CustomerAdapterCSV(CustomerCSV instance) {
		this.instance = instance;
	}

	@Override
	public String getId() {
		return instance.getId() + "";
	}

	@Override
	public String getFirstName() {
		return instance.getFirstname();
	}

	@Override
	public String getLastName() {
		return instance.getLastname();
	}

	@Override
	public String getEmail() {
		return instance.getEmailAddress();
	}

}

public class CustomerAdapterFromLegacyCode implements Customer {

	private CustomerFromLegacyCode instance;

	public CustomerAdapterFromLegacyCode(
                  CustomerFromLegacyCode instance) {
		this.instance = instance;
	}

	@Override
	public String getId() {
		return instance.getCn();
	}

	@Override
	public String getFirstName() {
		return instance.getGivenName();
	}

	@Override
	public String getLastName() {
		return instance.getSurname();
	}

	@Override
	public String getEmail() {
		return instance.getMail();
	}

	public String toString() {
		return "ID: " + instance.getCn();
	}

}

public class CustomerDB implements Customer {

	private String id;
	private String firstName;
	private String lastName;
	private String email;

	public CustomerDB(String id, String firstName,
                           String lastName, String email) {
		this.id = id;
		this.firstName = firstName;
		this.lastName = lastName;
		this.email = email;
	}

	public String getId() {
		return id;
	}

	public String getFirstName() {
		return firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public String getEmail() {
		return email;
	}

	public String toString() {
		return "ID: " + id + ", First name: " + firstName + ", Last
                           name: " + lastName + ", Email: " + email;
	}

}

4 – Using the Adapter: Now that we have the Adapters ready, we can encapsulate the POJOs we want in each Adapter. We put information into the objects and also put the POJO to be adapted inside the Adapter. Then we can add in the Customer List since it is from the Customer type.

public class CustomerService {

	public List<Customer> getEmployeeList() {
		List<Customer> customers = new ArrayList<>();

		Customer customerFromDB = new CustomerDB("1234",
                         "Robert", "C. Martin","bobMartin@cleancode.com");

		customers.add(customerFromDB);

		CustomerFromLegacyCode customerFromLegacyCode =
				new CustomerFromLegacyCode(
                     "777", "Java", "Juggy", "juggy@java.com");

		customers.add(new
                     CustomerAdapterFromLegacyCode(customerFromLegacyCode));

		CustomerCSV customerCSV = new
                   CustomerCSV("567,James,Gosling,jamesGosling@java.com");

		customers.add(new CustomerAdapterCSV(customerCSV));

		return customers;
	}

}

Bonus example

1 – Adapter interface:

public interface Product {

	String getName();

	BigDecimal getPrice();

	String productType();

	void processProduct();

	boolean isProcessed();

}

2 – Legacy Service:

public class LegacyProductService {

	public LegacyProductPOJO findLegacyProduct() {
		return new LegacyProductPOJO().buildDefault();
	}

}

3 – Legacy Product POJO:

public class LegacyProductPOJO {

	private String a01;
	private String a02;
	private String a03;
	private String a04;
	private String a05;
	private String a06;
	private String a07;
	private String a08;
	private String a09;
	private String a10;
	private String a11;
	private String a12;
	private String a13;
	private String a14;
	private String a15;
	private String a16;
	private String a17;
	private String a18;
	private String a19;
	private String a20;

     // Getters and setters omitted

}

4 – Adapter class:

public class LegacyProductAdapter implements Product {

	private boolean processed;
	private LegacyProductPOJO legacyProductPOJO;

	public LegacyProductAdapter(LegacyProductPOJO legacyProductPOJO) {
		this.legacyProductPOJO = legacyProductPOJO;
	}

	@Override
	public String getName() {
		return legacyProductPOJO.getA01();
	}

	@Override
	public BigDecimal getPrice() {
		return new BigDecimal(legacyProductPOJO.getA02());
	}

	@Override
	public String productType() {
		return legacyProductPOJO.getA03();
	}

	@Override
	public void processProduct() {
		System.out.println("Processing product..");
		processed = true;
	}

	@Override
	public boolean isProcessed() {
		return processed;
	}

}

5 – New Service to be invoked:

public class ProductService {

	public void processProduct(Product product) {
		product.processProduct();
	}

}

6 – The Unit Test:

public class BonusAdapterTest {

	@Test
	public void legacyServiceAdapterTest() {
		LegacyProductPOJO legacyProduct =
                    new LegacyProductService().findLegacyProduct();

		Product adapter = new LegacyProductAdapter(legacyProduct);

		ProductService productService = new ProductService();
		productService.processProduct(adapter);

		Assert.assertTrue(adapter.isProcessed());
	}

}

Summary of actions:

1 – Created the Adapter generic interface.
2 – Created the Adapter’s classes to adapt the POJOs.
3 – Implemented the Customer interface in each Adapter class.
4 – Encapsulated the POJO inside each Adapter class.
5 – Created a constructor inside the Adapter class receiving the POJO.
6 – Overrode the Adapter interface methods using the POJO class.
7 – Instantiated the Adapter class and put the POJO inside it.

To practice the Adapter Pattern you can create another class to be adapted, for example, the XMLCustomer and also create the Adapter for this customer! Create another test method to make sure it works! Try to use TDD (Test Driven Development).

DESIGN PATTERNS SAGA #9: REAL PROJECT SITUATIONS WITH ADAPTER

DESIGN PATTERNS SAGA #8: REAL PROJECT SITUATIONS WITH TEMPLATE METHOD

What if we needed to create logs for all user actions on a project? Imagine if we repeated the same code for everything. The log would probably be like:

Header – Always the same
Body – Variable
Bottom – Always the same

Imagine if we implemented these logs without a template (100 log classes implemented separately) and suddenly the client wants to change the log Header and Bottom. We would have to correct all the 100 classes! After correcting all the classes, we would be obligated to test all those logs. Can you imagine how bad it would be?

Fear not! There is a solution for this problem. We can use a powerful Pattern that I really like called Template Method Pattern.

With Template Method, we can encapsulate all these repeated actions and reuse it as much as we want. Now, if something changes on the log Header or the Bottom, even something regarding the creation of the log, we can change it in just one place! Yes, very easy, just in one place! Make your code flexible and powerful and fully master this Pattern!

template_method_diagram.PNG

Get the Design_Patterns_Saga_GitHub_Source_Code

1 – UserActionsLog – This is our Template. We are basically defining the Template for all log generation. You can see that this class is abstract.  Also, we implemented the common methods and declared the generateBodyLog() method as abstract because this is the different content from all logs.

In the generateLog() method we orchestrate the invocations from the methods in order to get the log generation encapsulated.

public abstract class UserActionsLog {

	private String userName;

	private boolean logCreated;

	protected UserActionsLog(String userName) {
		createLogFile();
		this.userName = userName;
	}

	private void generateHeaderLog() {
		System.out.println("Log Date:" +  new Date());
	}

	public abstract void generateBodyLog();

	private void generateBottomLog() {
		System.out.println("Action executed by:" + userName);
	}

	public void generateLog() {
		generateHeaderLog();
		generateBodyLog();
		generateBottomLog();
		verifyLogFile();
	}

	private void createLogFile() {
		System.out.println("Creating log file....");
	}

	private void verifyLogFile() {
		System.out.println("Verifying if the " + 
                     "file and log was created.");
		logCreated = true;
	}

	public boolean isLogCreated() {
		return logCreated;
	}

}

2 – The log implementations: These are the classes we created to pass the user name to the constructor and implement the specific generateBodyLog() method because each log will change only in the body content.

public class CustomerRegisterLog extends UserActionsLog {

	public CustomerRegisterLog(String userName) {
		super(userName);
	}

	@Override
	public void generateBodyLog() {
		System.out.println("Generating Customer body log..");
	}

}

public class CompanyReceiptLog extends UserActionsLog {

	public CompanyReceiptLog(String userName) {
		super(userName);
	}

	@Override
	public void generateBodyLog() {
		System.out.println("Generating Company Receipt body log..");
	}

}

3 – Unit Tests: It’s done. Now we can execute the tests to check if the log is correct.

public class TemplateMethodTest {

	private static final String USER_NAME = "Juggy";

	@Test
	public void executeCustomerLogTest() {
		UserActionsLog customerLog = new CustomerRegisterLog(USER_NAME);  

		customerLog.generateLog();

		Assert.assertTrue(customerLog.isLogCreated());
	}

	@Test
	public void executeCompanyReceiptTest() {
		UserActionsLog companyReceiptLog = new CompanyReceiptLog(USER_NAME);  

		companyReceiptLog.generateLog();

		Assert.assertTrue(companyReceiptLog.isLogCreated());
	}

}

Summary of actions:

1 – Created the Template Method class.
2 – Declared the Template Method class as abstract.
3 – Implemented the common methods in the Template Method class.
4 – Created the specific method as abstract in the Template Method class.
5 – Created the orchestrator method in the Template Method class.
6 – Created the specific class to implement the specific abstract method.
7 – Instantiated the specific class and invoked the orchestrator method.

To practice the Template Method Pattern you can create another type of Log class, for example, the CompanyCostsLog and create another test method to make sure it works! Try to use TDD (Test Driven Development). Start the development from the test.

DESIGN PATTERNS SAGA #8: REAL PROJECT SITUATIONS WITH TEMPLATE METHOD