Write out 23 design patterns in one breath

Creative Patterns

1. Factory Model

An interface for creating objects is defined, but subclasses determine which class to instantiate. The factory method defers instantiation of the class bar to subclasses.

// Product Category
public interface IProduct {
}
public class ProductA1 implements IProduct{}
public class ProductA2 implements IProduct{}
public class ProductB1 implements IProduct{}
public class ProductB2 implements IProduct{}
// Product Enumeration Category
public enum ProductEnum {
    A1, A2, B1, B2
}
// Factory Category
public interface IFactory {
    IProduct create(ProductEnum productEnum); // Type parameters are added here just to better demonstrate the factory approach
}
// Factory A (factory subclass - does it look like a simple factory, hey hey)?
public class FactoryA implements IFactory {
    public IProduct create(ProductEnum productEnum) {
        if(ProductEnum.A1.equals(productEnum)) {
            return new ProductA1();
        } else if(ProductEnum.A2.equals(productEnum)) {
            return new ProductA2();
        }
        return null;
    }
}
// Factory B
public class FactoryB implements IFactory {
    ....
}
// Client Call
// Create Product A1
IFactory factoryA = new FactoryA();
factoryA.create(ProductEnum.A1);
// Create Product B2
IFactory factoryB = new FactoryB();
factoryB.create(ProductEnum.B2)

The difference between a simple factory and a factory approach is that a simple factory handles everything in one place, whereas a factory approach creates a framework for subclasses to decide how to implement it.

// Simple Factory
public class SimpleFactory implements IFactory {
    public IProduct create(ProductEnum productEnum) {
        switch(productEnum) {
            case A1:
                return new ProductA1();
            case A2:
                return new ProductA2();
            case B1:
                return new ProductB1();
            case B2:
                return new ProductB2();
        }
        return null;
    }
}

2. Abstract Factory

Provides an interface for creating families of related or dependent objects without specifying specific classes.

// Product family A
public interface IProductA {
}
public class ProductA1 implements IProductA{} // Product A No.1
public class ProductA2 implements IProductA{} // Product 2 A
// Product B of Product Family
public interface IProductB {
}
public class ProductB1 implements IProductB{} // Product No.1 B
public class ProductB2 implements IProductB{} // Product 2 B
// Factory Class - Note: If you need to add Category C products, you must change the interface.
public interface IFactory {
    IProductA createProductA();
    IProductB createProductB();
}
// Factory 1: Specific factories use "factory method" to achieve
public class Factory1 implements IFactory {
    public IProduct createProductA() {
        return new ProductA1;
    }
    
    public IProduct createProductB() {
        return new ProductB1;
    }
}
// Factory 2
public class Factory2 implements IFactory {
    ....
}
// Shop
public class Store {
    private IFactory factory;
    
    public Store(IFactory factory) {
        this.factory = factory;
    }
} 
// Client Call
// Create Product No.1
Store store1 = new Store(new Factory1());
store1.createProductA();
store1.createProductB();
// Create Product 2
Store store2 = new Store(new Factory2());
store2.createProductA();
store2.createProductB();

3. Builder Model

Also known as Generator Mode, it encapsulates the construction process of a product and allows step-by-step construction.

StringBuilder

4. Singleton Model

Ensure that there is only one instance of a class and provide a global access point

// Lazy Man: Double Check Lock
public class Singleton {
    private volatile static Singleton instance;
    
    private Singleton() {}
    
    public Singleton getInstance() {
        if(instance == null) { // Single Checked
            synchronized(Singleton.class) {
                if(instance == null) { // Double Checked
                    return new Singleton();
                }
            }
        }
        return instance;
    }
}

volatile

Some people think that the reason for using volatile is visibility, which ensures that threads don't have instance s locally, and read them in main memory every time. But it's not right. The main reason for using volatile is another feature: disallow instruction reordering optimization. That is to say, there will be a memory barrier (on the generated assembly code) after the assignment operation of volatile variables, and the read operation will not be reordered before the memory barrier.

// Static inner class
public class Singleton {
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    
    private Singleton (){}  
    
    public static final Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}
// Hungry Chinese Style
public class Singleton{
    //Class is initialized when loaded
    private static final Singleton instance = new Singleton();
 
    private Singleton(){}
 
    public static Singleton getInstance(){
        return instance;
    }
}

5. Prototype Model

When the process of creating instances of a given class is expensive or complex, the prototype pattern is used.

Object.clone()

Structural model

1. Adapter mode

Convert the interface of a class into another interface that the customer expects. The adapter allows incompatible classes to cooperate.

/** Object adapter -- more commonly used**/
public interface ITarget {
    void request();
}
// Adapted class
public class Adaptee {
    public void specificRequest(){}
}
// Adaptation classes: a composite approach
public class Adapter implements ITarget {
    private Adaptee adaptee;
    
    public Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }
    
    public void show() {
        adaptee.specificRequest();
    }
}
// Client Call
ITarget target = new ITarget(new Adaptee());
target.request();
/** Class adapter**/
public interface ITarget {
    void request();
}
// Adapted class
public class Adaptee {
    public void specificRequest(){}
}
// Adaptation classes: in an inherited manner
public class Adapter extends Adaptee implements ITarget {
    public Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }
    
    public void show() {
        specificRequest();
    }
}
// Client Call
ITarget target = new ITarget();
target.request();

2. Bridging mode

I don't know how to change your realization, but also your abstraction.

Scenarios for handling changes in two or more dimensions

3. Combination model

Allow you to combine objects into a tree structure to represent the "whole/part" hierarchy. Composition enables customers to handle individual objects and object combinations in a consistent manner.

It's actually about maintaining a set of Item s in a specific class
Although the combination mode violates the single principle, it is more valuable.

4. Decoration Mode

Dynamic attachment of responsibility to the object. To extend functionality, decorators offer more flexible alternatives than inheritance.

// Abstract component class
public abstract class AbsComponent {
    public abstract void perform();
}
// assembly
public class Component extends AbsComponent {
    public void perform() {
        ....
    }
}
// Abstract Decorators (Defining this class just for better separation)
public abstract class AbsDecorator extends AbsComponent {
}
// Decorator A
public class DecoratorA extends AbsDecorator {
    AbsComponent component; // Reference to component classes for decoration
    
    public DecoratorA(AbsComponent component) {
        this.component = component;
    }
    
    public void perform() {
       ....
    }
}
// Decorator A
public class DecoratorB extends AbsDecorator {
    ....
}
// Client Call
Component component = new Component();
DecoratorA decoratorA = new DecoratorA(component);
DecoratorB decoratorB = new DecoratorB(decoratorA);
decoratorB.perform();

Actual Scenario: InputStream File InputStream...

5. Appearance pattern

Provide a unified interface for accessing a group of interfaces of subsystems. Appearance defines a high-level interface to make subsystems easier to use.

6. Hedonic Model

If you want an instance of a class to be used to provide many "virtual instances", use the Flyweight pattern.

Application scenario: caching

7. Agency Model

Provide a surrogate or placeholder for another object to control access to that object.

/** Static proxy**/
// Proxy class
public interface ISubject {
    void request();
}
public class RealSubject implements ISubject {
    public void request() {}
}
// proxy class
public class Proxy implements ISubject {
    private ISubject subject;
    
    public Proxy(ISubject subject) {
        this.subject = subject;
    }
    
    public void request() {
        subject.request();
    }
}
// Client Call
ISubject subject = new RealSubject();
Proxy proxy = new Proxy(subject);
proxy.request();
/** JDK Dynamic proxy: The proxy class must implement interfaces**/
public class MyInvocationHandler implements InvocationHandler {
    private Object target;
   
    public DyProxy(Object target) {
        this.target = target;
    }
    
    @override
    public Object invoke(Object proxy, Method method, Object[] args) throw Throwable {
        // Preconditions can be added here
        Object result = method.invoke(target, args);
        // Postconditions can be added here
        return result;
    }
    
    // Get the proxy object
    public Object getProxy() {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        Class<?>[] infs = target.getClass().getInterfaces();
        return Proxy.newProxyInstance(loader, infs, this);
    }
}
// Client Call
ISubject subject = new RealSubject();
MyInvocationHandler handler = new MyInvocationHandler(serive);
ISubject subjectProxy = (ISubject)handler.getProxy();
subjectProxy.request();

JDK dynamic proxy can only generate proxy for classes that implement interfaces, not for classes.
CGLIB implements proxy for classes. It mainly generates a subclass for a specified class and overrides its methods (also known as weaving).

Behavioral patterns

1. Responsibility Chain Model

When you want more than one object to have the opportunity to process a request, you use the responsibility chain model.

FilterChain
Game: Drum and Flower

2. Command mode

Encapsulate "requests" as objects to parameterize other objects using different requests, queues, or logs. Command mode also supports undo operations.

// command
public interface ICommand {
    void exec();
    void undo();
}
// Recipient
public class Receiver {
    public void action() {
        ....
    }
}
// Command Implementation Class
public class CommandImpl {
    private Receiver receiver;
    
    public CommandImpl() {
        this.receiver = receiver;
    }

    public void exec() {
        receiver.action();
    }
    ...
}
// caller
public class Invoker {
    private ICommand command;
    
    public void setCommand(ICommand command) {
        this.command = command;
    }
    
    public void exec() {
        command.exec();
    }
}
// Client Call
ICommand command = new CommandImpl(new Receiver());
Invoker invoker = new Invoker();
invoker.setCommand(command);
invoker.exec();

Application scenarios: queue requests, log requests

3. Interpreter mode

Create interpreters for languages

Never used, never wanted to use.

4. Iterator pattern

Provides a method to access each element of an aggregated object sequentially without exposing its internal representation.

Iterator

5. Intermediary Model

Complex communication and control between centralized objects.

Chestnut: CPU scheduling

6. MOU Model

Let the object return to its previous state (e.g. undo).

To put it bluntly, it's to maintain a state internally.

7. Observer model

One-to-many dependencies between objects are defined so that when an object changes its state, all its dependencies are notified and updated automatically.

// Observer interface
public interface IObserver {
    void update();
}
// Observer A
public class ObserverA implements IObserver {
    ....
}
// Observer B
public class ObserverB implements IObserver {
    ....
}
// Theme Interface
public interface ISubject {
    void register(IObserver observer);
    void remove(IObserver observer);
    void notifyObservers();
}
// Topic Implementation Class
public class SubjectImpl implements ISubject {
    private List<IObserver> observers;
    
    public SubjectImpl() {
        observers = new ArrayList<>();
    }
    
    public void register(IObserver observer) {
        observers.add(observer);
    }
    
    public void remove(IObserver observer) {
        observers.remove(observer);
    }
    
    public void notifyObservers() {
        for(IObserver observer : observers) {
            observer.update();
        }
    }
}
// Client Call
ISubject subject = new SubjectImpl();
subject.register(new ObserverA());
subject.register(new ObserverB());
subject.notifyObservers();
/** JDK Built-in observer mode: Notification by inheriting Observable**/
// Observer
public interface Observer {
    void update(Observable o, Object arg);
}
// theme
public class Observable {
    private boolean changed = false;
    private Vector obs;
    
    public synchronized void addObserver(Observer o) {
        ....
        obs.addElement(o);
    }
    
    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }
    
    public void notifyObservers(Object arg) {
        ....
        ((Observer)arrLocal[i]).update(this, arg);
        ....
    }
    
    // An additional setChange method is added to ensure that notifications are not made until critical conditions are reached.
    // Notify as long as the change is made.
    protected synchronized void setChanged() {
        changed = true;
    }
}

8. Strategic Patterns

The algorithmic family is defined and encapsulated separately so that they can be replaced with each other. This pattern makes the algorithmic changes independent of the customers who use the algorithmic.

// Policy interface
public interface IStrage {
    void opreate();
}
// Specific strategy classes
public class StrageA implements IStrage {
    ....
}
public class StrageB implements IStrage {
    ....
}
// context
public class Context {
    private IStrage strage;
    public void setStrage(IStrage strage) {
        this.strage = strage;
    }
    public void opreate() {
        strage.opreate();
    }
}
// Client Call
IStrage strage = new StrageA();
Context ctx = new Context();
ctx.setStrage(strage);
ctx.opreate();

Application scenario: session strategy

9. State mode

Allowing an object to change its behavior when its internal state changes, the object appears to modify its class.

// State interface
public interface IState {
    void handle();
}
// Specific state class A
public class StateA implements IState {
    private Context ctx;
    
    public StateA(Context ctx) {
        this.ctx = ctx;
    }
    
    public void handle() {
        // It can be handled directly.
        ....
        // It can also transfer state.
        ctx.setState(ctx.getStateB());
    }
}
// Specific State Class B
public class StateB implements IState {
    ....
}
// context
public class Context {
    private IState stateA;
    private IState stateB;
    
    private IState state = stateA; // Default state A
    
    public Context() {
        stateA = new StateA(this);
        stateB = new StateB(this);
    }
    
    public void setState(IState state) {
        this.state = state;
    }
    
    public void setStateA(IState stateA) {
        this.stateA = stateA;
    }
    
    public void setStateB(IState stateB) {
        this.stateB = stateB;
    }
    
    public void opreate() {
        state.handle();
    }
    ...
}
// Client Call
Context ctx = new Context();
ctx.opreate(); // State A - > Transition to State B
ctx.opreate(); // State B

Is it similar to the strategy model?
In state mode, we encapsulate a group of actions in state objects, and the actions of context can be delegated to one of those objects at any time. Over time, the current state changes in the set of state objects. But contexts'customers don't know much about state objects, or even are completely unaware of them.
In policy mode, customers are usually destined to specify which policy object Context will combine. Now, although policy patterns make us more resilient and able to change policies at runtime, there is usually only one most appropriate policy object for a context object.

10. Template Method

Define the skeleton of an algorithm in a method and defer some steps to subclasses. Template method enables subclasses to redefine some steps in the algorithm without changing the structure of the algorithm.

Actually, it's using abstract classes.

11. Visitor Model

The simplest expression is the setter/getter method.

Design Principles

  1. Packaging changes
  2. For interface programming, not for implementation programming
  3. Multi-use combination, less inheritance
  4. Efforts to design loosely coupled interaction objects
  5. Open-Close Principle: Classes should be open to extensions and closed to modifications
  6. Dependency Inversion Principle: Dependence on abstraction, not on concrete classes
  7. Minimum Knowledge Principle: Reducing Interaction between Objects
  8. Hollywood Principle: Let low-level components be hooked into computing without high-level components relying on low-level components
  9. Single Principle: A class should have only one cause for change

Keywords: Java JDK Programming Session less

Added by bbauder on Mon, 10 Jun 2019 01:54:14 +0300