1. Decorator mode
(1) What is the decorator mode?
The decorator pattern is to dynamically extend the function of an object without using inheritance or changing the original class file. Among the eight principles of the design pattern, the opening and closing principles stipulate the standards of opening to expansion and closing to modification. The focus of decorator mode is not to change the original documents, which is in line with the principle of opening and closing. Why not use inheritance? In fact, it is stipulated by the principle of composite reuse, and combination is more flexible than inheritance.
(2) Life scene example
In fact, the process of dressing is similar to the pattern of decorators. The process of adding new functions to a class is similar to the process of dressing one by one.
type Person interface { wearClothes() }
Then define a basic class, which is the default implementation of the Person interface.
type person struct {} // Decorated person func (p *person) wearClothes() { fmt.Println("Dress") fmt.Println("Wear pants") fmt.Println("Wear shoes") } type student struct {} // The decorator and the decorated implement the same interface func (s *student) wearClothes() { fmt.Println("Dress") // Duplicate code fmt.Println("Wear pants") fmt.Println("Wear shoes") // New operation fmt.Println("Endorsement package") } type adult struct{ // The decorator and the decorated implement the same interface p *person // Class, the decorator saves the reference of the decorator } func (a *adult) wearClothes() { a.p.wearClothes() // New operation fmt.Println("Carrying a handbag") } func main() { fmt.Println("person:") p := &person{} p.wearClothes() fmt.Println("student:") s := student{} s.wearClothes() fmt.Println("adult:") a := adult{} a.wearClothes() }
(3) Characteristics
1) The decorator and the decorated have the same parent class (implements the same interface).
2) The decorator keeps a reference to the decorator.
3) The decorator accepts all the requests from the caller, and these requests will eventually be returned to the decorator.
(4) Application of Java IO stream
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new ByteOutputStream());
In the above code, when creating BufferedOutputStream, the ByteOutputStream reference is passed to BufferedOutputStream. When calling the write() method of BufferedOutputStream, the ByteOutputStream write() method is called first. Here, BufferedOutputStream is the decorator and ByteOutputStream is the decorator.
(5) Application of Go IO flow
// A PipeReader is the read half of a pipe. type PipeReader struct { // PipeReader is a decorator p *pipe // pipe is the decorator } func (r *PipeReader) Read(data []byte)(n int, err error) { return r.p.Read(data) }
2. Observer mode
(1) What is the observer model?
Observer pattern defines a one to many dependency between objects, so that whenever an object's state changes, its dependent objects will be notified and updated automatically.
The observer model is actually a behavioral model, which is widely used in both business development process and open source community. Our common "publish subscribe" mode and listener mode are implemented in the same principle as the observer mode. The observer pattern has two roles: the observed and the observer.
(2) Life scene example
The scene of the teacher's assignment. Observed by: teacher, observed by: student.
The math teacher assigned Xiao Ming homework with 100 math problems.
package main import ( "fmt" ) type teacher struct { s *student } func (t *teacher) arrangeHomework() { fmt.Println("Math teacher assigns 100 math problems") t.s.writingHomework() } type student struct{} func (s *student) writingHomework() { fmt.Println("Xiao Ming does 100 math problems") } func main() { t := &teacher{ s : &student{}, } t.arrangeHomework() }
At this time, Xiaohong came, just met the math teacher and Xiaoming, because Xiaohong's math score is better than Xiaoming, so the math teacher only arranged 50 math questions for Xiaohong. A teacher has more than one student, that is, an observed person has more than one observer. Solution: Abstract students and make them into interfaces.
package main import ( "fmt" ) type teacher struct { students []student } func (t *teacher) arrangeHomework() { fmt.Println("Math teacher assigns homework") for _, s := range t.students { s.writingHomework() } } type student interface{ writingHomework() } type xiaoming struct {} func (ming *xiaoming) writingHomework() { fmt.Println("Xiao Ming does 100 math problems") } type xiaohong struct {} func (hong *xiaohong) writingHomework() { fmt.Println("Xiao Hong does 50 math problems") } func main() { var students []student students = append(students, &xiaoming{}) students = append(students, &xiaohong{}) t := &teacher{ students : students, } t.arrangeHomework() }
After the math teacher assigned the homework, the physics teacher went on the stage again and assigned 50 physics questions to Xiao Ming and Xiao Hong respectively. Multiple observers multiple observed. Solution: abstract the teacher and make it an interface.
package main import ( "fmt" ) type teacher interface { arrangeHomework() } type mathTeacher struct { students []student // The observed keeps all the references of the observer. There is a one to many relationship between the observed and the observer } func (m *mathTeacher) arrangeHomework() { fmt.Println("Math teacher assigns homework") for _, s := range m.students { s.writingMathHomework() } } type physicsTeacher struct { students []student } func (p *physicsTeacher) arrangeHomework() { fmt.Println("Physics teacher assigns homework") for _, s := range p.students { s.writingPhysicsHomework() } } type student interface{ writingMathHomework() writingPhysicsHomework() } type xiaoming struct {} func (ming *xiaoming) writingMathHomework() { fmt.Println("Xiao Ming does 100 math problems") } func (ming *xiaoming) writingPhysicsHomework() { fmt.Println("Xiao Ming does 50 physics problems") } type xiaohong struct {} func (hong *xiaohong) writingMathHomework() { fmt.Println("Xiao Hong does 50 math problems") } func (hong *xiaohong) writingPhysicsHomework() { fmt.Println("Xiao Hong does 50 physics problems") } func main() { var students []student students = append(students, &xiaoming{}) students = append(students, &xiaohong{}) m := &mathTeacher{ students : students, } p := &physicsTeacher{ students : students, } m.arrangeHomework() p.arrangeHomework() }
(3) The application of Java Observer
public interface Observer { void update(Observable o, Object arg); }
3. Interceptor mode
(1) What is interceptor mode?
The essence of interceptor pattern is actually to execute the specified code before or after the execution of a function.
Interceptor mode is actually a behavior mode, mainly to control the behavior of the object. It is essentially the same as the responsibility chain, filter and other modes, and it is an implementation of AOP. Interceptor mode is widely used in many frameworks and open source systems, including permission verification, log printing, etc.
(2) Life scene example
Xiaoming's mother is going to the supermarket at 11:30. She has some housework to deal with before shopping. There's a lot of housework. I have to mop the floor at 9 o'clock in the morning.
package main import ( "context" "fmt" ) type interceptor func(ctx context.Context, h handler) type handler func(ctx context.Context) func main() { h := func(ctx context.Context) { fmt.Println("go to the supermarket...") } inter1 := func(ctx context.Context, h handler) { fmt.Println("clean the floor...") h(ctx) } ctx := context.Background() inter1(ctx, h) }
After half an hour, at 9:30, Xiaoming's mother found that the clothes were piled up and needed to be washed.
package main import ( "context" "fmt" ) type interceptor func(ctx context.Context, h handler) type handler func(ctx context.Context) func main() { h := func(ctx context.Context) { fmt.Println("go to the supermarket...") } inter1 := func(ctx context.Context, h handler) { fmt.Println("clean the floor...") h(ctx) } inter2 := func(ctx context.Context, h handler) { fmt.Println("wash the clothes...") h(ctx) } var ceps []interceptor ceps := append(ceps, inter1, inter2) ctx = context.Background() for _, cep := range ceps { cep(ctx, h) } }
Output results:
clean the floor... go to the supermarket... wash the clothes... go to the supermarket...
In order to make the handler function execute only once, instead of calling an interceptor to execute the handler function once, a method is needed to string all the interceptors above, but also to ensure that the handler executes only once, so the following invoker method is added.
package main import ( "context" "fmt" ) type interceptor func(ctx context.Context, h handler) type handler func(ctx context.Context) type invoker func(ctx context.Context, ceps []interceptor, h handler) func getInvoker(ctx context.Context, ceps []interceptor, cur int, ivk invoker) invoker { if cur == len(ceps) - 1 { return ivk } return func(ctx context.Context, ceps []interceptor, h handler) { ceps[cur+1](ctx, h, getInvoker(ctx, ceps, cur+1, ivk)) } } func main() { h := func(ctx context.Context) { fmt.Println("go to the supermarket...") } ivk := func(ctx context.Context, ceps []interceptor, h handler) { h(ctx) } var ceps []interceptor inter1 := func(ctx context.Context, h handler, ivk invoker) { fmt.Println("clean the floor...") ivk(ctx, ceps, h) } inter2 := func(ctx context.Context, h handler, ivk invoker) { fmt.Println("wash the clothes...") ivk(ctx, ceps, h) } ceps := append(ceps, inter1, inter2) ctx = context.Background() ceps[0](ctx, h, getInvoker(ctx, ceps, 0, ivk) }
Output results:
clean the floor... wash the clothes... go to the supermarket...
Half an hour later, at 10 o'clock, Xiaoming's mother found out that her favorite play was on air today, and she was going to go out after two episodes.
package main import ( "context" "fmt" ) type interceptor func(ctx context.Context, h handler) type handler func(ctx context.Context) type invoker func(ctx context.Context, ceps []interceptor, h handler) func getInvoker(ctx context.Context, ceps []interceptor, cur int, ivk invoker) invoker { if cur == len(ceps) - 1 { return ivk } return func(ctx context.Context, ceps []interceptor, h handler) { ceps[cur+1](ctx, h, getInvoker(ctx, ceps, cur+1, ivk)) } } func main() { h := func(ctx context.Context) { fmt.Println("go to the supermarket...") } ivk := func(ctx context.Context, ceps []interceptor, h handler) { h(ctx) } var ceps []interceptor inter1 := func(ctx context.Context, h handler, ivk invoker) { fmt.Println("clean the floor...") ivk(ctx, ceps, h) } inter2 := func(ctx context.Context, h handler, ivk invoker) { fmt.Println("wash the clothes...") ivk(ctx, ceps, h) } inter3 := func(ctx context.Context, h handler, ivk invoker) { fmt.Println("watch TV...") ivk(ctx, ceps, h) } ceps := append(ceps, inter1, inter2, inter3) ctx = context.Background() ceps[0](ctx, h, getInvoker(ctx, ceps, 0, ivk) }
Output results:
clean the floor... wash the clothes... watch TV... go to the supermarket...
(3) Application of grpc
func chainUnaryClientInterceptors(cc *ClientConn) { ... var chainedInt UnaryClientInterceptor if len(interceptors) == 0 { chainedInt = nil } else if len(interceptors) == 1 { chainedInt = interceptors[0] } else { chainedInt = func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption) error { return interceptors[0](ctx, method, req, reply, cc, getChainUnaryInvoker(interceptors, 0, invoker), opts...) } } ... }