专栏首页魏晓蕾的专栏装饰者模式、观察者模式、拦截器模式及其Go语言实现

装饰者模式、观察者模式、拦截器模式及其Go语言实现

1、装饰者模式

(1)什么是装饰者模式? 装饰者模式是在不使用继承和不改变原类文件的情况下,动态地扩展一个对象的功能。在设计模式的八大原则中,开闭原则规定了对扩展开放,对修改关闭的标准。装饰者模式的重点是不改变原类文件,这其实是符合开闭原则的。为什么不使用继承呢?这其实是合成复用原则规定的,组合比继承更加地灵活。 (2)生活场景实例 穿衣服的过程其实跟装饰者模式比较相似,往一个类上增加一个个新功能的过程就类似我们一件件穿衣服的过程。

type Person interface {
	wearClothes()
}

然后定义一个基础类,是Person接口的默认实现。

type person struct {}					// 被装饰者
func (p *person) wearClothes() {
	fmt.Println("穿衣服")
	fmt.Println("穿裤子")
	fmt.Println("穿鞋子")
}

type student struct {}					// 装饰者,和被装饰者实现了相同的接口
func (s *student) wearClothes() {
	fmt.Println("穿衣服")				// 重复代码
	fmt.Println("穿裤子")
	fmt.Println("穿鞋子")
	// 新增操作
	fmt.Println("背书包")
}

type adult struct{						// 装饰者,和被装饰者实现了相同的接口
	p *person							// 类的组合,装饰者保存了被装饰者的引用
}
func (a *adult) wearClothes() {
	a.p.wearClothes()
	// 新增操作
	fmt.Println("拎一个手提包")
}

func main() {
	fmt.Println("person:")
	p := &person{}
	p.wearClothes()

	fmt.Println("student:")
	s := student{}
	s.wearClothes()

	fmt.Println("adult:")
	a := adult{}
	a.wearClothes()
}

(3)特征 1)装饰者和被装饰者具有相同的父类(实现了相同的接口)。 2)装饰者保存了一个被装饰者的引用。 3)装饰者接受所有调用端的请求,并且这些请求最终都会返回给被装饰者。 (4)Java IO流中的应用

BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new ByteOutputStream());

在上面这段代码中,在创建BufferedOutputStream时,通过将ByteOutputStream的引用传给BufferedOutputStream,在调用BufferedOutputStream的write()方法时,先调用了ByteOutputStream的write()方法。这里,BufferedOutputStream是装饰者,ByteOutputStream是被装饰者。 (5)Go IO流中的应用

// A PipeReader is the read half of a pipe.
type PipeReader struct {			// PipeReader是装饰者
	p *pipe							// pipe是被装饰者
}
func (r *PipeReader) Read(data []byte)(n int, err error) {
	return r.p.Read(data)
}

2、观察者模式

(1)什么是观察者模式? 观察者模式定义了对象间一种一对多的依赖关系,使得每当一个对象状态发生变化时,其相关依赖对象都会得到通知并自动更新。 观察者模式其实是一种行为型模式,无论是业务开发过程还是开源社区中都广泛被应用。我们常见的“发布-订阅”模式、监听器模式的实现,原理其实和观察者模式是一致的。观察者模式具有两个角色:被观察者和观察者。 (2)生活场景实例 老师布置作业的场景。被观察者:老师,观察者:学生。 数学老师给小明布置了家庭作业,内容是100道数学题。

package main
import (
	"fmt"
)

type teacher struct {
	s *student
}
func (t *teacher) arrangeHomework() {
	fmt.Println("数学老师布置100道数学题")
	t.s.writingHomework()
}

type student struct{}
func (s *student) writingHomework() {
	fmt.Println("小明做100道数学题")
}

func main() {
	t := &teacher{
		s : &student{},
	}
	t.arrangeHomework()
}

此时小红来了,刚好碰到了数学老师和小明,因为小红数学成绩比小明好,所以数学老师只布置了50道数学题给小红。一个老师多个学生,即一个被观察者多个观察者。解决方法:抽象student,将student做成接口。

package main
import (
	"fmt"
)

type teacher struct {
	students []student
}
func (t *teacher) arrangeHomework() {
	fmt.Println("数学老师布置作业")
	for _, s := range t.students {
		s.writingHomework()
	}
}

type student interface{
	writingHomework()
}
type xiaoming struct {}
func (ming *xiaoming) writingHomework() {
	fmt.Println("小明做100道数学题")
}
type xiaohong struct {}
func (hong *xiaohong) writingHomework() {
	fmt.Println("小红做50道数学题")
}

func main() {
	var students []student
	students = append(students, &xiaoming{})
	students = append(students, &xiaohong{})
	t := &teacher{
		students : students,
	}
	t.arrangeHomework()
}

数学老师布置完作业后,物理老师又上场了,给小明和小红分别布置了50道物理题。多个观察者多个被观察者。解决方法:抽象teacher,将teacher做成接口。

package main
import (
	"fmt"
)

type teacher interface {
	arrangeHomework()
}
type mathTeacher struct {
	students []student					// 被观察者保存所有观察者的引用,被观察者与观察者之间是一对多的关系
}
func (m *mathTeacher) arrangeHomework() {
	fmt.Println("数学老师布置作业")
	for _, s := range m.students {
		s.writingMathHomework()
	}
}
type physicsTeacher struct {
	students []student
}
func (p *physicsTeacher) arrangeHomework() {
	fmt.Println("物理老师布置作业")
	for _, s := range p.students {
		s.writingPhysicsHomework()
	}
}

type student interface{
	writingMathHomework()
	writingPhysicsHomework()
}
type xiaoming struct {}
func (ming *xiaoming) writingMathHomework() {
	fmt.Println("小明做100道数学题")
}
func (ming *xiaoming) writingPhysicsHomework() {
	fmt.Println("小明做50道物理题")
}
type xiaohong struct {}
func (hong *xiaohong) writingMathHomework() {
	fmt.Println("小红做50道数学题")
}
func (hong *xiaohong) writingPhysicsHomework() {
	fmt.Println("小红做50道物理题")
}

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)Java Observer中的应用

public interface Observer {
	void update(Observable o, Object arg);
}

3、拦截器模式

(1)什么是拦截器模式? 拦截器模式的本质,其实是在一个函数执行之前或者之后去执行指定的代码。 拦截器模式其实是一种行为型模式,主要是去控制的对象的行为,它和责任链、过滤器等模式本质是一样的,是AOP思想的一个实现。拦截器模式在很多框架和开源系统中都有广泛应用,包括权限校验、日志打印等。 (2)生活场景实例 小明的妈妈准备11点半去超市购物,在购物之前有一些家务事要先处理。家务事比较多,上午9点钟的时候要拖地。

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)
}

过了半个小时,9点半的时候小明妈妈发现衣服堆积了,还需要把衣服洗了。

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)
	}
}

输出结果:

clean the floor...
go to the supermarket...
wash the clothes...
go to the supermarket...

为了使得handler函数最后只执行一次,而不是调用一个拦截器执行一次handler函数,就需要一个方法把上面所有的拦截器串起来,但又要保证handler只执行一次,所以添加如下invoker方法。

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)
}

输出结果:

clean the floor...
wash the clothes...
go to the supermarket...

又过了半个小时,10点钟的时候小明妈妈发现自己最喜欢的一部剧今天开播,准备追两集剧再出门。

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)
}

输出结果:

clean the floor...
wash the clothes...
watch TV...
go to the supermarket...

(3)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...)
		}
	}
	...
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 【Scala】Scala的集合类型与数组操作

    Scala有一个非常通用丰富强大可组合的集合库;集合是高阶的,并拥有一大套操作方法。Scala的所有的集合类都可以在包 scala.collection 包中找...

    魏晓蕾
  • 如何在IDEA中集成Tomcat【提供三种方法】

    魏晓蕾
  • JeecgBoot项目上线发布的几种方式

    部署方案采用nginx+tomcat部署方案 后端服务通过JAR方式运行 前端项目build后的静态资源,部署到nginx中

    魏晓蕾
  • golang context实战

    来自官方文档: https://blog.golang.org/context: Incoming requests to a server should cr...

    王磊-AI基础
  • Netty 之入门应用

    系列文章:http://www.jianshu.com/p/594441fb9c9e

    Yano_nankai
  • 深入理解ES6之——JS类的相关知识

    类声明以class关键字开始,其后是类的名称;剩余部分的语法看起来像对象字面量中的方法简写,并且在方法之间不需要使用逗号。

    寻找石头鱼
  • 学习 sentry 源码整体架构,打造属于自己的前端异常监控SDK

    这是学习源码整体架构第四篇。整体架构这词语好像有点大,姑且就算是源码整体结构吧,主要就是学习是代码整体结构,不深究其他不是主线的具体函数的实现。文章学习的是打包...

    若川
  • Netty|02 easy example

    1、博主使用的IDE是idea,很强大的开发工具 2、为了精简篇幅,博主把代码中需要导入包那一块去除了,大家在操作的时候需要手动导包 3、入门案例的每一行代码都...

    微笑的小小刀
  • 第200天:js---常用string原型扩展

    半指温柔乐
  • 【 Flutter Unit 解牛篇 】代码折叠展开面板,怎么没有线?

    FlutterUnit是【张风捷特烈】长期维护的一个Flutter集录、指南的开源App 如果你还未食用,可参见总汇集: 【 FlutterUnit 食用...

    张风捷特烈

扫码关注云+社区

领取腾讯云代金券