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

DESIGN PATTERNS SAGA #7: REAL PROJECT SITUATIONS WITH COMMAND

Creating multiple methods inside only one class can bring many problems like inflexibility and difficulty maintaining code. I am sure all of us have already seen a class full of methods and different responsibilities, right? Classes like these are very difficult to understand, take a lot of time to find out what is happening in all methods and are difficult to test. Fortunately, there is a way to solve this problem – we can use the Command Pattern! The Command Pattern encapsulates its behavior in a separate class that is executed by the Invoker class.

command_diagram.PNG

Get the Design_Patterns_Saga_GitHub_Source_Code

A very common example of the Command Pattern is the Thread class that receives the Runnable interface with the command inside the run method.

public class ThreadCommandExample {

	public static void main(String[] args) {
		TaskCommand command = new TaskCommand(10, 12); 

		Thread invoker = new Thread(command);
		invoker.start();
	}

	static class TaskCommand implements Runnable {

		int num1;
		int num2;

		TaskCommand(int num1, int num2) {
			this.num1 = num1;
			this.num2 = num2;
		}

		@Override
		public void run() { //execute method
			System.out.println(num1 * num2); //receiver
		}
	}
}

1 – Command interface: This is the base of the Pattern, without this generic interface nothing happens. No secrets here, just an interface that has the execute method, nothing more.

public interface Command {

	public void execute();

}

2 – Discount class: this class contains the information to be processed. It’s a simple POJO.

public class Discount {

	private boolean eligible;
	private boolean discountApplied;

	public Discount(boolean eligible) {
		this.eligible = eligible;
	}

	public void applyDiscount() {
		discountApplied = true;
		System.out.println("Discount applied!");
	}

	// Getters and setters omitted

}

3 – Commands: These actions must be executed inside the Invoker. The ApplyDiscountCommand has the business requirements in order to apply the discount. The ApplyAllDiscountsCommand is responsible for encapsulating the ApplyDiscountCommand in order to apply all the discounts once. So, this Command receives a Discount list and invokes the applyDiscount method.

public class ApplyDiscountCommand implements Command {

	private Discount discount;

	public ApplyDiscountCommand(Discount discount) {
		this.discount = discount;
	}

	@Override
	public void execute() {
		if (discount.isEligible()) {
			discount.applyDiscount();
		}
	}

}

public class ApplyAllDiscountsCommand implements Command {

	private List<Discount> discounts;

	public ApplyAllDiscountsCommand(List<Discount> discounts) {
		this.discounts = discounts;
	}

	@Override
	public void execute() {
		discounts.forEach(e -> new ApplyDiscountCommand(e).execute());
	}

}

4 – CommandInvoker: As the name says, it’s going to invoke the Command. We are receiving the Command in the Constructor to be executed in the execute method.

public class CommandInvoker {

	private Command command;

	public CommandInvoker(Command command) {
		this.command = command;
	}

	public void execute() {
		this.command.execute();
	}

}

5 – Unit Tests: All is set to execute Unit Tests. To execute the Command, create the Discount list. Then use the CommandInvoker passing the Command in the Constructor. In the end, execute the Command!

Now, ensure the discounts were applied correctly!

public class CommandTest {

	@Test
	public void applyCommandTest() {
		List<Discount> discounts = mockDiscounts();

		ApplyAllDiscountsCommand command =
                   new ApplyAllDiscountsCommand(discounts);

		CommandInvoker invoker = new CommandInvoker(command);
		invoker.execute();

		Assert.assertTrue(discounts.get(0).isDiscountApplied());
		Assert.assertFalse(discounts.get(1).isDiscountApplied());
		Assert.assertTrue(discounts.get(2).isDiscountApplied());
		Assert.assertFalse(discounts.get(3).isDiscountApplied());
	}

	private List<Discount> mockDiscounts() {
		return Arrays.asList(new Discount(true),
                     new Discount(false), new Discount(true),
                     new Discount(false));
	}

}

Summary of actions:

1 – Created the Command interface.
2 – Created the request object (POJO).
3 – Created the Commands implementing the Command interface.
4 – Implemented the execute method in each Command.
5 – Created the CommandInvoker class to execute the commands.
6 – Invoked the Command through the InvokerCommand class.

To practice the Command Pattern you can create another Discount Command, for example, GoalDiscountCommand that is responsible for applying a Discount depending on the price the client paid for the product and create another test method to make sure it works! Try to use TDD (Test Driven Development). Start the development from the test. Remember, the only way to master the Design Patterns is by practicing them! So, I strongly recommend you clone the project and implement your new Discount Command!

DESIGN PATTERNS SAGA #7: REAL PROJECT SITUATIONS WITH COMMAND

DESIGN PATTERNS SAGA #6: REAL PROJECT SITUATIONS WITH CHAIN OF RESPONSIBILITY

The problem: For complex business logic with lots of conditions, we could certainly use one giant class with lots of ifs. But by doing this,  the class would be highly coupled and with no cohesion. Consider using the Chain of Responsibility Pattern when you have business logic to implement that is dependent on several conditions.

Pros: Using this Pattern you can encapsulate your logic and you can easily create new features using different conditions.

Cons: If the chain gets too large, we can have performance problems. Be careful when using this Pattern.

The diagram:

chain_of_responsibility_diagram.png

The source code:
https://github.com/rafadelnero/design-patterns-saga.git

1 – The Handler class:  This class controls all the flows of the Chain of Responsibility Pattern.

public void setSucessor(Handler successor) – It sets the next chain to handle the next condition. We must create a chain based on all the classes that extend Handler.

public abstract Response handleRequest(Request request) – All the chain classes must implement this method. All the business logic will be executed inside this method.

 public abstract class Handler {
	protected Handler successor;

	public void setSucessor(Handler successor) {
		this.successor = successor;
	}

	public abstract Response handleRequest(Request request);

}

2 – The DiscountHandler class: In this class, we will configure the Chain of the classes that can handle the logic.  Basically, we will instantiate all the classes and set them in the setSuccessor(Handler handler) method. Remember that this Chain must be executed in the correct order as the following:

public class DiscountHandler {

	public Response applyDiscount(Request request) {
		Handler noDiscount = new NoDiscount();
		Handler basic = new BasicDiscount();
		Handler moderate = new ModerateDiscount();
		Handler vip = new VipDiscount();

		noDiscount.setSucessor(basic);
		basic.setSucessor(moderate);
		moderate.setSucessor(vip);

		return noDiscount.handleRequest(request);
	}

}

3 – The Chain: Now we have the classes that will create the chain. We must instantiate them from the root. In the case of this chain, they must be executed in the correct order.

All the classes must extend Handler to be part of the Chain.

When extending the Handler class we must implement the handleRequest abstract method. For example, if the request corresponds to the BasicDiscount condition, the logic from this class will be executed or else the next object in the Chain will handle this logic and so forth.

The classes from the chain are:

public class NoDiscount extends Handler {
	
	@Override
	public Response handleRequest(Request request) {
		if (request.getCustomerSalesAmount()
                  .compareTo(BasicDiscount.minimalValue) < 0) {
			return new Response(DiscountType.NO_DISCOUNT);
		} else {
			return successor.handleRequest(request);
		}
	}

}

public class BasicDiscount extends Handler {

	public static BigDecimal minimalValue = new BigDecimal("10000");

	@Override
	public Response handleRequest(Request request) {
		if (request.getCustomerSalesAmount()
                  .compareTo(ModerateDiscount.minimalValue) < 0) {
			System.out.println("Execute some business logic here");

			return new Response(DiscountType.BASIC);
		} else {
			return successor.handleRequest(request);
		}
	}

}

public class ModerateDiscount extends Handler {
	
	public static BigDecimal minimalValue = new BigDecimal("50000");

	@Override
	public Response handleRequest(Request request) {
		if (request.getCustomerSalesAmount()
                  .compareTo(VipDiscount.minimalValue) < 0) {
			return new Response(DiscountType.MODERATE);
		} else {
			return successor.handleRequest(request);
		}
	}

}

public class VipDiscount extends Handler {

	public static BigDecimal minimalValue = new BigDecimal("100000");

	@Override
	public Response handleRequest(Request request) {
		if (request.getCustomerSalesAmount()
                   .compareTo(minimalValue) >= 0) {
			System.out.println("Execute some business logic here");
			
			return new Response(DiscountType.VIP);
		}
		
		throw new IllegalArgumentException("Invalid argument.");
	}

}

4 – The Unitary Tests: Finally we will see the Pattern working! We are going to test if the request value corresponds to the correct condition. The test will be done for all the Chain classes.

 public class ChainOfResponsibilityTest {

	@Test
	public void verifyIfBasicDiscountWasAppliedTest() {
		Response response = new DiscountHandler()
                 .applyDiscount(new Request(new BigDecimal("20000")));

		Assert.assertEquals(response
                 .getDiscountType(), DiscountType.BASIC);
	}

	@Test
	public void verifyIfModerateDiscountWasAppliedTest() {
		Response response = new DiscountHandler()
                 .applyDiscount(new Request(new BigDecimal("50000")));

		Assert.assertEquals(response
                 .getDiscountType(), DiscountType.MODERATE);
	}

	@Test
	public void verifyIfVipDiscountWasAppliedTest() {
		Response response = new DiscountHandler()
                 .applyDiscount(new Request(new BigDecimal("100000")));

		Assert.assertEquals(response
                 .getDiscountType(), DiscountType.VIP);
	}

	@Test
	public void verifyIfNoDiscountWasAppliedTest() {
		Response response = new DiscountHandler()
                  .applyDiscount(new Request(new BigDecimal("5000")));

		Assert.assertEquals(response
                .getDiscountType(), DiscountType.NO_DISCOUNT);
	}

}

Summary of actions:

1 – Created the Handler class.
2 –  Extended the Handler class by the Chain classes.
3 – Implemented the handleRequest(Request request) method in the Chain classes.
4 – Created the Chain using the setSuccessor(Handler handler) method.
5 – Invoked the handleRequest method from the Chain root.

To practice the Chain of Responsibility Pattern you can create another DiscountType, for example, the SpecialDiscount and create another test method to make sure it works! Try to use TDD (Test Driven Development). Start the development from the test. Remember to use the IDE’s shortcuts for faster coding!

DESIGN PATTERNS SAGA #6: REAL PROJECT SITUATIONS WITH CHAIN OF RESPONSIBILITY