What pain points are design patterns designed to address?
It is a series of routines to solve specific problems, is the summary of predecessors’ code design experience, has a certain universality, can be used repeatedly. The goal is to improve code reusability, readability, and maintainability. The essence of design patterns is the practical application of object-oriented design principles, the full understanding of class encapsulation, inheritance and polymorphism, as well as the association and combination relationships of classes. Don’t reinvent the wheel.
What is object-oriented programming
Object-oriented programming
Is a programming paradigm or style. It takes class or object as the basic unit of organizing code, and takes encapsulation, abstraction, inheritance and polymorphism as the cornerstone of code design and implementation.Object oriented programming language
It is a programming language that supports the syntax mechanism of class or object, and has ready-made syntax mechanism, and can conveniently realize the four features of object-oriented programming.Object-oriented development
Including object-oriented analysis OOA, object-oriented design OOD, object-oriented programming OOP.
10 Design Principles
1. Single Responsibility principle SRP
Implementation class should be responsible for A single responsibility: If A code block (function class module) is responsible for multiple functions, then when A function needs to change the code, it may lead to B function problems, so A code block should be responsible for only one responsibility.
2. Open-close principle OCP
Open to extensions, close to modifications: Modifying old code to implement new functionality can cause bugs in old modules, so we should implement new functionality by developing new blocks of code.
3. Richter replacement principle LSP
Don’t break the inheritance system: a child class in a program should be able to replace its parent class wherever it appears and keep expectations unchanged. So a subclass tries not to change the expected behavior of a superclass method.
4. Interface isolation principle ISP
When designing an interface, simplify the simplicity: when class A needs only some of the methods in interface B, because implementing the interface needs to implement all of its methods, class A has some unnecessary code. At this point, interface B should be split, separating the methods that class A does and does not need.
5. Rely on the inversion principle
Programming for interfaces: Abstractions should not depend on details, details should depend on abstractions. The core of programming is interface oriented. We should rely on abstract interfaces, not concrete interface implementation classes or concrete objects.
Note: SOLID above is also known as the five Design Principles
6. The Principle of Least knowledge (Demeter principle)LOD
Reduced coupling: A class or object should have minimal knowledge of other objects. Communicate only with direct (coupled) friends.
7. Combination/aggregation reuse principle CRP
Composition more than inheritance: When possible, implement new functionality by composing existing objects (borrowing their capabilities) rather than using inheritance to acquire those capabilities.
8. Don’t repeat yourself DRY
Functional semantic duplicates should be merged, code execution duplicates should be deleted, and code logic duplicates but semantic differences should be preserved.
9. Keep it simple KISS
As much as possible, use simple and readable code to implement functionality, rather than logically complex, difficult to implement, and less readable.
10. Don’t overdesign logic you don’t need right now
Don’t over-optimize, don’t over-reserve extension points, and don’t design code that your colleagues don’t understand.
How do you evaluate the quality of your code?
- Readability, scalability, maintainability, reusability, testability…
- High cohesion and low coupling.
War is no HeHeZhiGong good healer tyger, big more if the wisest man often seems stupid, really good code and not by how much technology and exotic curiosity-a solution looking bad, but to look to the best of the world after the busy, a few pen realized functions at the same time without any traces of individual style, comply with the code specification, programming ideas, design patterns, the code.
How to form long-term memories?
Try to connect the dots
- Form a pyramid structure memory from top to bottom.
- Compiling key words and formulae memory.
Get carried away
- Extract the essence of the branches of knowledge to strengthen memory, to remove the essence.
Learning without thought is labor lost; thought without learning is perilous
- Deep thinking makes other people’s knowledge truly your own.
Learning and time to practice, not also said
- The first learning is only a short-term memory in the mind,
It takes a lot of review and reinforcement to form a long-term memory.
- The first learning is only a short-term memory in the mind,
Matters needing attention
Knowledge is dead, but code is alive. Don’t hardwire design pattern implementations into living business logic. It is our goal to be able to use what we have learned, but if we write code that is not understood by others in the group, it will affect the maintainability and development efficiency of the project. So we can use it sparingly, but we have to master the idea. Grasp the design pattern firmly, take to interview, interview others, the group to share or can frighten the crowd.
23 Design patterns shorthand
- Shorthand: 5, 7, 11, 23 are all odd numbers
- 5 types of creation
- 7 types of structure
- 11 behavioral patterns
- Create a type:
Work order to build the prototype
- Abstract factory, factory, singleton, builder, prototype
- Structured:
Bridge agent decorates adapter, enjoy element combination into facade
- Bridges, agents, decorators, adapters, savors, compositions, facades
- Behavior type:
Observe the status of template iterations, command mediations interpret chains of responsibility, and access policy memos
- Observer, template, iteration, status, command, mediator, interpreter, chain of responsibility, visitor, policy, memo
Creative design patterns
Encapsulates the object creation process and decouples the creation and use of objects
The singleton pattern
Application scenarios
Handles resource access conflicts and creates globally unique classes.
The solution
- Lazy: create when you need it (scenarios: not necessarily needed, expensive to create, lazy loading, need to start the system quickly).
- Han type: created when the system is started (scenario: must be used, temporary creation affects response speed).
- Multiple examples: A fixed number of the same instances of the same class.
The factory pattern
Application scenarios
Use to create a subclass object that inherits from the same parent class and implements the same interface. A given type parameter creates a concrete object.
The solution
enum HelloType {
A,
B
}
interface Hello {
sayHello()
}
class A implements Hello {
sayHello() {
console.log('A'); }}class B implements Hello {
sayHello() {
console.log('B'); }}class HelloFactory {
static list = new Map<HelloType, Hello>([
[HelloType.A, new A()],
[HelloType.B, new B()]
])
static getHello(type: HelloType) {
return HelloFactory.list.get(type)}}// test
HelloFactory.getHello(HelloType.A).sayHello()
HelloFactory.getHello(HelloType.B).sayHello()
Copy the code
Abstract Factory pattern
Application scenarios
A subclass object that inherits from the same parent class and implements the same interface creates a concrete object from a given number of type parameters.
The solution
enum Type {
A,
B
}
enum Occupation {
TEACHER,
STUDENT
}
interface Hello {
sayHello()
}
class TA implements Hello {
sayHello() {
console.log('Teacher A say hello')}}class TB implements Hello {
sayHello() {
console.log('Teacher B say hello')}}class SA implements Hello {
sayHello() {
console.log('Student A say hello')}}class SB implements Hello {
sayHello() {
console.log('Student B say hello')}}class AFactory {
static list = new Map<Occupation, Hello>([
[Occupation.TEACHER, new TA()],
[Occupation.STUDENT, new SA()]
])
static getHello(occupation: Occupation) {
return AFactory.list.get(occupation)
}
}
class BFactory {
static list = new Map<Occupation, Hello>([
[Occupation.TEACHER, new TB()],
[Occupation.STUDENT, new SB()]
])
static getHello(occupation: Occupation) {
return BFactory.list.get(occupation)
}
}
class HelloFactory {
static list = new Map<Type, AFactory | BFactory>([
[Type.A, AFactory],
[Type.B, BFactory]
])
static getType(type: Type) {
return HelloFactory.list.get(type)}}// test
HelloFactory.getType(Type.A).getHello(Occupation.TEACHER).sayHello()
HelloFactory.getType(Type.A).getHello(Occupation.STUDENT).sayHello()
HelloFactory.getType(Type.B).getHello(Occupation.TEACHER).sayHello()
HelloFactory.getType(Type.B).getHello(Occupation.STUDENT).sayHello()
Copy the code
Builder mode
Application scenarios
- There are many required parameters to verify at creation time.
- Parameter evaluation is sequential and interdependent at creation time.
- There are many steps to creating an object, all of which have to be successful.
The solution
class Programmer {
age: number
username: string
color: string
area: string
constructor(p) {
this.age = p.age
this.username = p.username
this.color = p.color
this.area = p.area
}
toString() {
console.log(this)}}class Builder {
age: number
username: string
color: string
area: string
build() {
if (this.age && this.username && this.color && this.area) {
return new Programmer(this)}else {
throw new Error('Lack of information')}}setAge(age: number) {
if (age > 18 && age < 36) {
this.age = age
return this
} else {
throw new Error('Not the right age')}}setUsername(username: string) {
if(username ! = ='Ming') {
this.username = username
return this
} else {
throw new Error('Xiao Ming is not suitable')}}setColor(color: string) {
if(color ! = ='yellow') {
this.color = color
return this
} else {
throw new Error('Yellow is not appropriate')}}setArea(area: string) {
this.area = area
return this}}// test
const p = new Builder()
.setAge(20)
.setUsername('little red')
.setColor('red')
.setArea('hz')
.build()
.toString()
Copy the code
The prototype pattern
Application scenarios
- The prototype pattern is based on the existing object clone data, rather than modifying the prototype chain!
- Objects are expensive to create, and different instances of the same class have the same property values. Saving resources by means of prototype cloning.
- Immutable objects are implemented by shallow cloning.
- Mutable objects are implemented by deep cloning, which consumes a lot of resources.
- Different versions of the same object can be compared with the same shallow clone and the changed deep clone, and then the new version replaces the old version.
Structural design mode
Some classical structures of classes or objects are summarized. These classical structures can solve the problems of specific application scenarios and decouple the structure and use of classes or objects
The bridge model
Application scenarios
- Decouple abstraction and implementation so that they can change independently.
- A class has multiple dimensions that vary independently, and we can extend them independently by combining them.
- Very similar to the combination over inheritance principle.
The solution
enum MsgLevel {
ERROR,
WARN,
}
enum MsgType {
EMAIL,
PHONE
}
interface MsgContent {
content()
}
class ErrorMsg implements MsgContent {
content() {
return 'ERROR'}}class WarnMsg implements MsgContent {
content() {
return 'WARN'}}interface MsgSender {
send()
}
class PhoneSend implements MsgSender {
msgContent: MsgContent
constructor(msgContent: MsgContent) {
this.msgContent = msgContent
}
send() {
console.log(`phone send The ${this.msgContent.content()}`)}}class EmailSend implements MsgSender {
msgContent: MsgContent
constructor(msgContent: MsgContent) {
this.msgContent = msgContent
}
send() {
console.log(`email send The ${this.msgContent.content()}`)}}// Test ();
new PhoneSend(new WarnMsg()).send()
new PhoneSend(new ErrorMsg()).send()
new EmailSend(new WarnMsg()).send()
new EmailSend(new ErrorMsg()).send()
Copy the code
The proxy pattern
Application scenarios
- Add nonfunctional requirements to the original class in order to decouple the code from the original business.
- Development of non-functional requirements for business systems: monitoring, statistics, authentication, traffic limiting, logging, caching.
The solution
- Implementation by inheritance (not recommended)
class User{
login(){
console.log('user login... ')}}class UserProxy extends User{
login() {
console.log('login before')
super.login()
console.log('login after')}}Copy the code
- Implemented through an interface (recommended)
interface Login {
login()
}
class User implements Login {
login() {
console.log('user login... ')}}class UserProxy implements Login {
user = new User()
login() {
console.log('login before')
this.user.login()
console.log('login after')}}Copy the code
Decorator pattern
Application scenarios
- Decorator classes are enhancements to the original functionality.
- The decorator class and the original class inherit from the same parent class, so we can nest multiple decorator classes into the original class.
- Mainly to solve the problem of inheritance relationship is too complex, through combination to replace inheritance.
- You can use more than one decorator by nesting the original class.
The solution
- Through AOP
Function.prototype.before = function (beforeFn) {
return (. arg) = >{ beforeFn(... arg);return this(...arg);
}
};
Function.prototype.after = function (afterFn) {
return (. arg) = > {
const result = this(... arg); afterFn(... arg);returnresult; }};function ImportEvent1 {
console.log('Say important things three times.')}function ImportEvent2 {
console.log('Important things say two three times.')}function ImportEvent3 {
console.log('Say the important things three times.')}// test
ImportEvent2.before(ImportEvent1).after(ImportEvent3)()
Copy the code
Adapter mode
Application scenarios
- The adapter pattern is used to remedy design flaws and make incompatible interfaces compatible.
- Encapsulate a flawed interface design.
- Unify the interface design of multiple classes.
- Replace dependent external systems.
- Compatible with older versions of interfaces.
- Suitable for data in different formats.
The solution
- The original interface method is few, class adapter and object adapter can be.
- If the original class has many methods and little difference from the target interface, use a class adapter to reduce the amount of code.
- If the original class has many methods and is very different from the target interface, composition is preferable to inheritance with object adapters.
// Target interface format
interface ITarget {
f1()
f2()
f3()
}
// The original class is not compatible with the target interface
class Origin {
fa(){}fb(){}f3(){}}// Use an adapter for compatibility
class Adaptor implements ITarget {
origin = new Origin()
f1() {
this.origin.fa()
}
f2() {
this.origin.fb()
}
f3() {
this.origin.f3()
}
}
Copy the code
The flyweight pattern
Application scenarios
- Shared units. Reuse objects to save memory, provided that the share object is immutable (it does not change after initialization).
The solution
- For example, the online chess game has 1000 rooms, each room has 1 board, the board’s current state (the position of the pieces) is different, but the size, color, name of the pieces are the same and fixed, can be designed to enjoy yuan.
Portfolio model
Application scenarios
Organize a set of objects into a tree structure to represent a partial-whole hierarchy. The composite pattern allows the client to unify the processing logic (recursive traversal) for individual and composite objects.
The solution
abstract class FileSystemNode {
path: string
abstract getFilesCount()
abstract getFilesSize()
}
class FileNode extends FileSystemNode {
constructor(path) {
super(a);this.path = path
}
getFilesCount() {
return 1
}
getFilesSize() {
return require(this.path).length
}
}
class Directory extends FileSystemNode {
subNodes = []
constructor(path) {
super(a);this.path = path
}
getFilesCount() {
return this.subNodes.reduce(item= > item.getCount(), 0)}getFilesSize() {
return this.subNodes.reduce(item= > item.getSize(), 0)}}Copy the code
Facade (appearance) mode
Application scenarios
- Combine multiple back-end interface requests into one (redundant interface) to improve response speed and solve performance problems.
- By encapsulating fine-grained interfaces, you can provide high-level interfaces that combine fine-grained interfaces to improve the ease-of-use of interfaces.
Behavioral design patterns
Summarizes some classic ways of class or object interaction to decouple the classes or objects associated with this behavior
Observer mode
Application scenarios
- Decouple observer from observed.
- The publish/subscribe pattern has a publish/subscribe scheduling center (middleman), the Observer pattern does not!
The solution
// The target object
class Subject {
observerList: Observer[]
constructor() {
this.observerList = [];
}
addObserver(observer) {
this.observerList.push(observer);
}
notify() {
this.observerList.forEach((observer) = >{ observer.update(); }); }}/ / observer
class Observer {
cb: Function
constructor(cb: Function) {
if (typeof cb === "function") {
this.cb = cb;
} else {
throw new Error("The Observer constructor must pass the function type!"); }}update() {
this.cb(); }}// test
const observerCallback = function () {
console.log("I've been informed.");
};
const observer = new Observer(observerCallback);
const subject = new Subject();
subject.addObserver(observer);
subject.notify();
Copy the code
Template pattern
Application scenarios
- Define an algorithm (business logic) skeleton in a method and defer some steps to subclasses. The template method pattern allows subclasses to redefine certain steps in an algorithm without changing the overall structure of the algorithm.
- Reuse extensions.
The solution
abstract class Drinks {
firstStep() {
console.log('Boil water')}abstract secondStep()
thirdStep() {
console.log('Pour into glass')}abstract fourthStep()
drink() {
this.firstStep()
this.secondStep()
this.thirdStep()
this.fourthStep()
}
}
class Tea extends Drinks {
secondStep() {
console.log('Soaking tea')}fourthStep() {
console.log('Add lemon')}}class Coffee extends Drinks {
secondStep() {
console.log('Brew coffee')}fourthStep() {
console.log('sugar')}}// test
const tea = new Tea()
tea.drink()
const coffee = new Coffee()
coffee.drink()
Copy the code
The strategy pattern
Application scenarios
- Define a family of algorithms, encapsulate each algorithm separately, and make them interchangeable.
- Avoid lengthy if-else or switch branch decisions.
The solution
enum StrategyType {
S,
A,
B
}
const strategyFn = {
'S': function (salary: number) {
return salary * 4
},
'A': function (salary: number) {
return salary * 3
},
'B': function (salary: number) {
return salary * 2}}const calculateBonus = function (level: StrategyType, salary: number) {
return strategyFn[level](salary)
}
calculateBonus(StrategyType.A, 10000) / / 30000
Copy the code
Chain of Responsibility model
Application scenarios
- Multiple processors, ABC, process the same request in turn, forming a chain. When one processor can process the request, it will not be passed on to subsequent processors.
- Filter interceptor processor.
The solution
const order500 = function (orderType, pay, stock) {
if (orderType === 1 && pay === true) {
console.log("500 yuan deposit, 100 yuan coupon.");
return true;
} else {
return false; }};const order200 = function (orderType, pay, stock) {
if (orderType === 2 && pay === true) {
console.log("200 yuan deposit, 50 yuan coupon.");
return true;
} else {
return false; }};const orderCommon = function (orderType, pay, stock) {
if ((orderType === 3| |! pay) && stock >0) {
console.log("Regular purchase, no coupons.");
return true;
} else {
console.log("There's not enough stock to buy.");
return false; }};class chain {
fn: Function
nextFn: Function
constructor(fn: Function) {
this.fn = fn;
this.nextFn = null;
}
setNext(nextFn) {
this.nextFn = nextFn
}
init(.arguments) {
const result = this.fn(... arguments);if(! result &&this.nextFn) {
this.nextFn.init(... arguments); }}}const order500New = new chain(order500);
const order200New = new chain(order200);
const orderCommonNew = new chain(orderCommon);
order500New.setNext(order200New);
order200New.setNext(orderCommonNew);
order500New.init(3.true.500); // Regular purchase, no coupon
Copy the code
The state pattern
Application scenarios
- Each state inside a thing is encapsulated as a class, and a change in the internal state produces a different behavior.
The solution
class weakLight {
light: Light
constructor(light: Light) {
this.light = light
}
press() {
console.log('Turn on the bright light')
this.light.setState(this.light.strongLight)
}
}
class strongLight {
light: Light
constructor(light: Light) {
this.light = light
}
press() {
console.log('off')
this.light.setState(this.light.offLight)
}
}
class offLight {
light: Light
constructor(light: Light) {
this.light = light
}
press() {
console.log('Turn on low light')
this.light.setState(this.light.weakLight)
}
}
class Light {
weakLight: weakLight
strongLight: strongLight
offLight: offLight
currentState: offLight | weakLight | strongLight // Current status: off by default
constructor() {
this.weakLight = new weakLight(this)
this.strongLight = new strongLight(this)
this.offLight = new offLight(this)
this.currentState = this.offLight
}
press() {
this.currentState.press()
}
setState(state) {
this.currentState = state
}
}
// test
const light = new Light()
light.press()
light.press()
light.press()
light.press()
light.press()
light.press()
Copy the code
Iterator pattern
Application scenarios
- Iterating over collection objects.
Visitor pattern
Application scenarios
- Allows one or more operations to be applied to a set of objects, decoupling the operations from the objects themselves.
Memorandum Mode
Application scenarios
- Without violating encapsulation, capture the internal state of an object and save that state outside of the object so that the object can later be restored to its previous state.
The solution
class Programmer {
age: number
username: string
color: string
area: string
constructor(p) {
this.age = p.age
this.username = p.username
this.color = p.color
this.area = p.area
}
// Create a snapshot
createSnapshot() {
return {
age: this.age,
username: this.username,
color: this.color,
area: this.area
}
}
// Restore the object state using the snapshot
restoreSnapshot(snapshot: Programmer) {
this.age = snapshot.age
this.username = snapshot.username
this.color = snapshot.color
this.area = snapshot.area
}
}
Copy the code
Command mode
Application scenarios
- The main functions and application scenarios of the command mode are to control the execution of commands, such as asynchronous, delayed, queued, undoing, storing, and logging commands. Decouple the initiator and the executor of the command.
The solution
interface Command {
execute()
}
class closeDoorCommand implements Command {
execute() {
console.log('close door'); }}class openPcCommand implements Command {
execute() {
console.log('open pc'); }}class openQQCommand implements Command {
execute() {
console.log('login qq'); }}class CommandManager {
commandList: Command[] = []
addCommand(command: Command) {
this.commandList.push(command)
}
execute() {
this.commandList.forEach(command= > {
command.execute()
})
}
}
//test
const commandManager = new CommandManager();
commandManager.addCommand(new closeDoorCommand());
commandManager.addCommand(new openPcCommand());
commandManager.addCommand(new openQQCommand());
commandManager.execute();
Copy the code
Interpreter mode
Application scenarios
Given a language, define its grammatical representation, and define an interpreter that uses the identity to interpret sentences in the language.
The mediation patterns
Application scenarios
- The mediation pattern is designed much like the middle tier, by introducing the mediation as a middle tier to transform the interactions (dependencies) between a set of objects into a one-to-many (star-shaped) relationship. Originally, an object needs to interact with n objects, but now it only needs to interact with one intermediary object, which minimizes the interaction between objects, reduces the code complexity, and improves the readability and maintainability of the code.
Don't forget to like, follow and comment on my quality