Design Pattern Cheatsheet

Kuroun Seung
8 min readJan 29, 2018

--

YAGNI vs Martin’s OCP and Meyer’s OCP

YAGNI is talking about up front software feature. Martin’s OCP is about code ripple effect. Meyer’s OCP is about API backward compatibility.

Framework Design: Dependency Injection, Inversion of Control and AOP

DI is to inject instantiate object from DI container into a method. Decoupling in the implementations. DI is the technique to wire object together in the flexible way and makes code clean. It applies Open-Closed principle.

  • For example, in Spring framework, configuration xml need to have bean class to have getter and setter method
  • Example of configuration service class into spring application:

application.java

ApplicationContext context = new ClassPathXmlApplicationContext(“springconfig.xml”);
IPersonService personService = (IPersonService) context.getBean(“personService”);

springconfig.xml

<bean id=”personService” class=”PersonService”>
<property name=”personDao” ref=”personDao” />
</bean>
<bean id=”personDao” class=”PersonDAO” />

The principle of Hollywood of Framework design “We call you, don’t call us.” is inversion of control principle.

Crosscutting concern

The problem when we call same function everywhere in the code. To avoid it we should follow good programming practice like DRY.

AOP concept

A programming paradigm to write code more modularity by separation of crosscutting concerns. Advantage is to avoid code scattering and tangling.

  • Joinpoint is somewhere in the classes and methods
  • Pointcut is a collection of one or more Joinpoint
  • Advice is the implementation of Crosscutting concern. Advice type:
  • Before
  • After returning
  • After throwing
  • After
  • Around
  • Aspect is what Crosscutting Concern do I execute (=Advice) at which location in the code (=Pointcut)?
  • Weaving is the way to the place the advice code with the target code at corresponding Pointcuts

For example, in Spring framework, it uses Dynamic proxy pattern to implement AOP

serviceCounter.java

@Aspect

@After(“execution(* PersonService.findPerson(…))”)
public void log(JoinPoint joinPoint){
joinPoint.getSignature().getName();
}

springconfig.xml

<aop:aspectj-autoproxy />
<bean id=”serviceCounter” class=”ServiceCounter” />

In Rails framework, filter methods are purely applied AOP.

Framework Design: White box and Black box

White box framework is easy to develop but difficult to use and in the contrary of Black box framework. BB framework has more composition and delegation while WB has more inheritance.

Design Pattern GOF

Behavioral Command

WHAT: Receiver, Invoker, Abstract Command, Concrete Command

WHEN: Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable or redoable operations.

Behavioral Chain of Responsibility

WHAT: Client, Handler, Concrete Handler

WHEN: Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.

ADVANTAGE: Reduce coupling. Add flexibility to assign object.

DISADVANTAGE: No guarantee for receipt because request has no specific receiver.

Structural Composite

WHAT: Component, Composite, Leaf

WHEN: Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.

Behavioral Iterator

WHAT: Aggregate, Concrete Aggregate, Iterator, Concrete Iterator

WHEN: Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.

Behavioral Mediator

WHAT: Mediator, Concrete Mediator, Colleague, Concrete Mediator

WHEN: Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.

Behavioral Observer

WHAT: Subject, Concrete Subject, Observer, Concrete Observer

WHEN: Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

ADVANTAGE: Abstract loose coupling between subject and observer, Broadcast communication.

DISADVANTAGE: Unexpected updated

ISSUE: Observing has more than one subject. Who trigger the update?

Behavioral State

WHAT: State Context, Context, Concrete Context

WHEN: Allow an object to alter its behavior when its internal state changes. The object will appear to change its class. No if-else at all.

Behavioral Strategy

WHAT: Context, Strategy, Concrete Strategy

WHEN: Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

ADVANTAGE: Reduce conditional statements. A choice of implementation.

DISADVANTAGE: Client must be aware of different strategies. Increase number of objects

Structural Facade

WHAT: Client, Facade, Sub-systems

WHEN: Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher level interface that makes the subsystem easier to use.

Behavioral Template Method

WHAT: Abstract Class (Template Class), Concrete Class (Primitive Class)

WHEN: Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure. Heavily reuse code.

Structural Proxy & Dynamic Proxy

WHAT: Subject, Real Subject, Proxy Subject

WHEN: Provide a surrogate or placeholder for another object to control access to it. To add functionalities to object.

TimingProxy.java

package proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TimingProxy implements InvocationHandler {

private Object target;
public TimingProxy(Object target){
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result;
try {
long start = System.nanoTime();
result = method.invoke(target, args);
long end = System.nanoTime();
long exeTime = end - start;
System.out.println("Execute time for " + target.getClass().getName() + " calling " + method.getName() + " is " + exeTime + "(ns)");
} catch (InvocationTargetException e) {
throw e.getTargetException();
} catch (Exception e) {
throw new RuntimeException("unexpected invocation exception: " + e.getMessage());
}
return result;
}
}

AccountService.java

package bank.service;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.Collection;
import bank.dao.AccountDAO;
import bank.dao.IAccountDAO;
import bank.domain.Account;
import bank.domain.Customer;
import proxy.LogProxy;
import proxy.TimingProxy;
public class AccountService implements IAccountService {
private IAccountDAO accountDAO;
public AccountService(){
// Proxy.newProxyInstance is dynamically create class on the fly and accept 3 arguments:
// loader class
// list of interface to implement
// proxy object and will call invoke method internally
// good video explain about this https://www.youtube.com/watch?v=dEwgHewfvyk

ClassLoader cl = AccountService.class.getClassLoader();
IAccountDAO accountDAO = new AccountDAO();
IAccountDAO logProxyDAO = (IAccountDAO) Proxy.newProxyInstance(cl,new Class[]{IAccountDAO.class},new LogProxy(accountDAO));

IAccountDAO timingAccountDAO = (IAccountDAO) Proxy.newProxyInstance(cl, new Class[]{IAccountDAO.class}, (InvocationHandler) new TimingProxy(logProxyDAO));
this.accountDAO = timingAccountDAO;
}
public Account createAccount(long accountNumber, String customerName) {
Account account = new Account(accountNumber);
Customer customer = new Customer(customerName);
account.setCustomer(customer);
accountDAO.saveAccount(account);
return account;
}
public void deposit(long accountNumber, double amount) {
Account account = accountDAO.loadAccount(accountNumber);
account.deposit(amount);
accountDAO.updateAccount(account);
}
public Account getAccount(long accountNumber) {
Account account = accountDAO.loadAccount(accountNumber);
return account;
}
public Collection&amp;lt;Account&amp;gt; getAllAccounts() {
return accountDAO.getAccounts();
}
public void withdraw(long accountNumber, double amount) {
Account account = accountDAO.loadAccount(accountNumber);
account.withdraw(amount);
accountDAO.updateAccount(account);
}
public void transferFunds(long fromAccountNumber, long toAccountNumber, double amount, String description) {
Account fromAccount = accountDAO.loadAccount(fromAccountNumber);
Account toAccount = accountDAO.loadAccount(toAccountNumber);
fromAccount.transferFunds(toAccount, amount, description);
accountDAO.updateAccount(fromAccount);
accountDAO.updateAccount(toAccount);
}
}

Application.java

package bank;import java.lang.reflect.Proxy;
import java.util.Collection;
import bank.domain.Account;
import bank.domain.AccountEntry;
import bank.domain.Customer;
import bank.service.AccountService;
import bank.service.IAccountService;
import proxy.TimingProxy;
public class Application {
public static void main(String[] args) {

ClassLoader cl = Application.class.getClassLoader();
IAccountService as = new AccountService();
IAccountService timingAccountService = (IAccountService) Proxy.newProxyInstance(cl, new Class[]{IAccountService.class}, new TimingProxy(as));
IAccountService accountService = timingAccountService;
// create 2 accounts;
accountService.createAccount(1263862, “Frank Brown”);
accountService.createAccount(4253892, “John Doe”);
//use account 1;
accountService.deposit(1263862, 240);
accountService.deposit(1263862, 529);
accountService.withdraw(1263862, 230);
//use account 2;
accountService.deposit(4253892, 12450);
accountService.transferFunds(4253892, 1263862, 100, “payment of invoice 10232”);
// show balances

Collection&amp;lt;Account&amp;gt; accountlist = accountService.getAllAccounts();
Customer customer = null;
for (Account account : accountlist) {
customer = account.getCustomer();
System.out.println(“Statement for Account: “ + account.getAccountnumber());
System.out.println(“Account Holder: “ + customer.getName());
System.out.println(“-Date — — — — — — — — — — — — -”
+ “-Description — — — — — — — — — “
+ “-Amount — — — — — — -”);
for (AccountEntry entry : account.getEntryList()) {
System.out.printf(“%30s%30s%20.2f\n”, entry.getDate()
.toString(), entry.getDescription(), entry.getAmount());
}
System.out.println(“ — — — — — — — — — — — — — — — — — — — — “
+ “ — — — — — — — — — — — — — — — — — — — — “);
System.out.printf(“%30s%30s%20.2f\n\n”, “”, “Current Balance:”,
account.getBalance());
}
}
}

Structural Bridge

WHAT: Abstraction, Refined Abstraction, Implementor, Concrete Implementor

WHEN: Decouple an abstraction from its implementation so that the two can vary independently. (Bridge between two strategy pattern but it is more structural)

Creational Factory Method

WHAT: ( Virtual Constructor)
Product, Concrete Product, Creator, Concrete Creator.
Parameterized Factory Method & Non-parameterized Factory Method

WHEN: Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

Miscellaneous

PUSH & POP IN OBSERVER: Push promotes robust system. Data is pushed from subject to observers. Observer cannot decide. Observer who own data know the data. Observer who does not own data, does not know data. Better in frequency update. Less reusable. Data is pull by an observer when they need it. It is more flexible. Observer can decide when and what data to query. Better when it is not so often update. Less efficient. Push has more broadcasting characteristic than pull.

STRATEGY & COR: We use strategy, we still have if-else condition to pick up a concrete strategy. Likewise, COR still has if-else condition are chunked into each handler. If the code does not change too often and has small amount of contexts, we should use strategy. Otherwise, use COR instead.

--

--