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