Design Pattern Cheatsheet
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&lt;Account&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&lt;Account&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 MethodWHEN: 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.