Structural
1. Adapter - allows
classes with incompatible interfaces to work together by wrapping its own
interface around that of an already existing class. 2 types:
a. Object Adapter Pattern - The adapter contains an instance of the class it
wraps.
b. Class Adapter Pattern - The adapter is created by implementing or
inheriting both the interface that is expected and the interface that is
pre-existing.
/**
* Adapter design pattern can be implemented
in two ways. One using the
* inheritance method and second using the
composition method.
*
* @author Watsh
*
*/
class CylindricalSocket {
public String supply(String cylinStem1, String cylinStem2) {
String output = "Power
power power...";
return output;
}
}
/**
* Adapter using inheritance. Class Adapter
Pattern.
*
* @author Watsh
*
*/
class RectangularAdapter extends CylindricalSocket {
public String adapt(String rectaStem1, String rectaStem2) {
// some conversion logic
String cylinStem1 = rectaStem1;
String cylinStem2 = rectaStem2;
return supply(cylinStem1, cylinStem2);
}
}
/**
* Adapter using composition. Object Adapter
pattern.
*
*/
class RectangularAdapter2 {
private CylindricalSocket socket;
public String adapt(String rectaStem1, String rectaStem2) {
// some conversion logic
socket = new CylindricalSocket();
String cylinStem1 = rectaStem1;
String cylinStem2 = rectaStem2;
return socket.supply(cylinStem1,
cylinStem2);
}
}
public class AdapterExample {
private static String rectaStem1 = "test";
private static String rectaStem2 = "test";
public static void main(String[] args) {
RectangularAdapter adapter = new RectangularAdapter();
String power = adapter.adapt(rectaStem1, rectaStem2);
System.out.println(power);
}
}
|
Adapter (recognizeable by creational
methods taking an instance of different abstract/interface
type and returning an implementation of own/another abstract/interface type
which decorates/overrides the
given instance)
2. Flyweight - reduces the cost of creating and manipulating a large number
of similar objects.
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
// Instances of CoffeeFlavour
will be the Flyweights
class CoffeeFlavour {
private final String name;
CoffeeFlavour(String newFlavor) {
this.name = newFlavor;
}
@Override
public String toString() {
return name;
}
}
// Menu acts as a factory and
cache for CoffeeFlavour flyweight objects
class Menu {
private Map
CoffeeFlavour lookup(String flavorName) {
if (!flavours.containsKey(flavorName))
flavours.put(flavorName, new CoffeeFlavour(flavorName));
return flavours.get(flavorName);
}
int totalCoffeeFlavoursMade() {
return flavours.size();
}
}
class Order {
private final int tableNumber;
private final CoffeeFlavour flavour;
Order(int tableNumber, CoffeeFlavour flavor) {
this.tableNumber = tableNumber;
this.flavour = flavor;
}
void serve() {
System.out.println("Serving " + flavour + " to table " + tableNumber);
}
}
public class FlyweightExample {
private final List
private final Menu menu = new Menu();
void takeOrder(String flavourName, int table) {
CoffeeFlavour flavour = menu.lookup(flavourName);
Order order = new Order(table, flavour);
orders.add(order);
}
void service() {
for (Order order
: orders)
order.serve();
}
String report() {
return "\ntotal CoffeeFlavour objects
made: "
+ menu.totalCoffeeFlavoursMade();
}
public static void main(String[] args) {
FlyweightExample shop = new FlyweightExample();
shop.takeOrder("Cappuccino", 2);
shop.takeOrder("Frappe", 1);
shop.takeOrder("Espresso", 1);
shop.takeOrder("Frappe", 897);
shop.takeOrder("Cappuccino", 97);
shop.takeOrder("Frappe", 3);
shop.takeOrder("Espresso", 3);
shop.takeOrder("Cappuccino", 3);
shop.takeOrder("Espresso", 96);
shop.takeOrder("Frappe", 552);
shop.takeOrder("Cappuccino", 121);
shop.takeOrder("Espresso", 121);
shop.service();
System.out.println(shop.report());
}
}
|
Flyweight (recognizeable by creational
methods returning a cached instance, a bit the "multiton" idea)
3. Bridge - decouples
an abstraction from its implementation so that the two can vary independently.
The
bridge pattern is useful when both the class as well as what it does vary
often. The class itself can be thought of as the implementation and what the class can do as the abstraction. The bridge pattern
can also be thought of as two layers of abstraction.
/** "Implementor" */
interface Workshop {
abstract public void work();
}
/** "ConcreteImplementor" 1/2 */
class Produce implements Workshop {
@Override
public void work() {
System.out.print("Produced");
}
}
/** "ConcreteImplementor" 2/2 */
class Assemble implements Workshop {
@Override
public void work() {
System.out.println(" Assembled.");
}
}
/**
* abstraction in bridge
pattern
*
*/
abstract class Vehicle {
protected Workshop workShop1;
protected Workshop workShop2;
protected Vehicle(Workshop workShop1,
Workshop workShop2) {
this.workShop1 = workShop1;
this.workShop2 = workShop2;
}
abstract public void manufacture();
}
/** "Refined Abstraction" */
class Car extends Vehicle {
public Car(Workshop workShop1,
Workshop workShop2) {
super(workShop1, workShop2);
}
@Override
public void manufacture() {
System.out.print("Car ");
workShop1.work();
workShop2.work();
}
}
/** "Refined Abstraction" */
class Bike extends Vehicle {
public Bike(Workshop workShop1,
Workshop workShop2) {
super(workShop1, workShop2);
}
@Override
public void manufacture() {
System.out.print("Bike ");
workShop1.work();
workShop2.work();
}
}
/** "Client" */
public class BridgeExample {
public static void main(String[] args) {
Vehicle
vehicle1 = new Car(new Produce(), new Assemble());
vehicle1.manufacture();
Vehicle
vehicle2 = new Bike(new Produce(), new Assemble());
vehicle2.manufacture();
}
}
|
Bridge (recognizeable by creational
methods taking an instance of different abstract/interface
type and returning an implementation of own abstract/interface type which delegates/uses the given
instance)
·
None comes to mind yet. A fictive example would be
new
LinkedHashMap(LinkedHashSet, List)
which
returns an unmodifiable linked map which doesn't clone the items, but uses them.
The java.util.Collections#newSetFromMap()
and singletonXXX()
methods
however comes close.
4. Composite - composes zero-or-more similar objects so that they can be
manipulated as one object.
File system can be modeled with composite
pattern as shown below. Directory can be a composite of Files. Files could be
either RegularFile (leafs) or Directories (composites).
import java.util.ArrayList;
import java.util.List;
/** Component */
interface Group {
public void assemble();
}
/** Leaf */
class Block implements Group {
public void assemble() {
System.out.println("Block");
}
}
/** Composite */
class Structure implements Group {
// Collection of child groups.
private List
public void assemble() {
for (Group group : groups) {
group.assemble();
}
}
// Adds the group to the structure.
public void add(Group group) {
groups.add(group);
}
// Removes the group from the structure.
public void remove(Group group) {
groups.remove(group);
}
}
public class CompositeExample {
public static void main(String[] args) {
//Initialize three blocks
Block block1 = new Block();
Block block2 = new Block();
Block block3 = new Block();
//Initialize three structure
Structure
structure = new Structure();
Structure
structure1 = new Structure();
Structure
structure2 = new Structure();
//Composes the groups
structure1.add(block1);
structure1.add(block2);
structure2.add(block3);
structure.add(structure1);
structure.add(structure2);
structure.assemble();
}
}
|
Composite (recognizeable by behavioral
methods taking an instance of same abstract/interface
type into a tree structure)
5. Decorator - dynamically adds/overrides behavior in an existing method of
an object.
interface Icecream {
public String makeIcecream();
}
class SimpleIcecream implements Icecream {
@Override
public String makeIcecream() {
return "Base
Icecream";
}
}
abstract class IcecreamDecorator implements Icecream {
protected Icecream specialIcecream;
public IcecreamDecorator(Icecream
specialIcecream) {
this.specialIcecream = specialIcecream;
}
public String makeIcecream() {
return specialIcecream.makeIcecream();
}
}
class NuttyDecorator extends IcecreamDecorator {
public NuttyDecorator(Icecream
specialIcecream) {
super(specialIcecream);
}
public String makeIcecream() {
return specialIcecream.makeIcecream() + addNuts();
}
private String addNuts() {
return "
+ cruncy nuts";
}
}
class HoneyDecorator extends IcecreamDecorator {
public HoneyDecorator(Icecream specialIcecream) {
super(specialIcecream);
}
public String makeIcecream() {
return specialIcecream.makeIcecream() + addHoney();
}
private String addHoney() {
return " + sweet honey";
}
}
public class DecoratorExample {
public static void main(String args[]) {
Icecream icecream = new HoneyDecorator(new NuttyDecorator(new SimpleIcecream()));
System.out.println(icecream.makeIcecream());
}
}
|
Decorator (recognizeable by creational
methods taking an instance of same abstract/interface
type which adds additional behaviour)
·
All subclasses of
java.io.InputStream
, OutputStream
, Reader
and Writer
have a constructor taking
an instance of same type.
6. Façade - provides
a simplified interface to a large body of code.
Facade (recognizeable by behavioral
methods which internally uses instances of different independent abstract/interface types)
·
javax.faces.context.FacesContext
, it
internally uses among others the abstract/interface types LifeCycle
, ViewHandler
, NavigationHandler
and
many more without that the enduser has to worry about it (which are however
overrideable by injection).
·
javax.faces.context.ExternalContext
, which
internally uses ServletContext
,HttpSession
, HttpServletRequest
, HttpServletResponse
, etc.
7. Proxy - provides
a placeholder for another object to control access, reduce cost, and reduce
complexity.
import
java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface Animal {
public void getSound();
}
class Lion implements Animal {
public void getSound() {
System.out.println("Roar");
}
}
/**
* InvocationHandler is
the interface implemented by the invocation handler of a
* proxy instance. Each
proxy instance has an associated invocation handler.
* When a method is
invoked on a proxy instance, the method invocation is
* encoded and
dispatched to the invoke method of its invocation handler.
*/
class AnimalInvocationHandler implements InvocationHandler {
public AnimalInvocationHandler(Object
realSubject) {
this.realSubject = realSubject;
}
public Object invoke(Object proxy,
Method m, Object[] args) {
Object result
= null;
try {
/*
* Execute the getSound() method of
realSubject.
*/
result
= m.invoke(realSubject, args);
} catch (Exception ex) {
ex.printStackTrace();
}
return result;
}
private Object realSubject = null;
}
/**
* Proxy provides static
methods for creating dynamic proxy classes and
* instances, and it is
also the superclass of all dynamic proxy classes created
* by those methods.
*
* @author Watsh
*
*/
public class ProxyExample {
public static void main(String[] args) {
Animal
realSubject = new Lion();
ClassLoader classLoader =
realSubject.getClass().getClassLoader();
/*
* interfaces below will be Animal
*/
Class[]
interfaces = realSubject.getClass().getInterfaces();
AnimalInvocationHandler
invocationHandler = new AnimalInvocationHandler(
realSubject);
/*
* Creates a dynamic proxy for Lion's realSubject
instance for the
* interface methods that Lion implements.
*/
Animal proxy
= (Animal) Proxy.newProxyInstance(classLoader, interfaces,
invocationHandler);
/*
* Calling an interface method on proxy
instance will call invoke() of
* realSubject instance's invocation Handler.
*/
proxy.getSound();
}
}
|
No comments:
Post a Comment