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