模式名称 | 介绍 |
代理模式 | 为其他对象提供一种代理以控制对这个对象的访问。 |
装饰器模式 | 动态的给一个对象添加一些额外的职责。就增加功能来说,此模式比生成子类更为灵活。 |
适配器模式 | 将一个类的接口转换成客户希望的另外一个接口。使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 |
外观模式 | 为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。 |
代理模式
代理模式可以为其他对象提供一种代理,以控制这个对象的访问。
所谓代理,是指具有与被代理对象相同的接口类,客户端必须通过代理与被代理的目标进行交互,代理在这个交互的过程中,进行某些额外的处理。
比如说,一个普通的汉堡,直接吃则吃到的是普通的汉堡。但是如果利用代理,将这个普通的汉堡撒上辣椒粉,就可以得到一个香辣鸡腿堡。最后也吃到了一个香辣鸡腿堡。
package main import "fmt" type Goods struct { Kind string //商品种类 Fact bool //商品真伪 } // =========== 抽象层 =========== //抽象的购物主题Subject type Shopping interface { Buy(goods *Goods) //某任务 } // =========== 实现层 =========== //具体的购物主题, 实现了shopping, 去韩国购物 type KoreaShopping struct {} func (ks *KoreaShopping) Buy(goods *Goods) { fmt.Println("去韩国进行了购物, 买了 ", goods.Kind) } //具体的购物主题, 实现了shopping, 去美国购物 type AmericanShopping struct {} func (as *AmericanShopping) Buy(goods *Goods) { fmt.Println("去美国进行了购物, 买了 ", goods.Kind) } //具体的购物主题, 实现了shopping, 去非洲购物 type AfrikaShopping struct {} func (as *AfrikaShopping) Buy(goods *Goods) { fmt.Println("去非洲进行了购物, 买了 ", goods.Kind) } //海外的代理 type OverseasProxy struct { shopping Shopping //代理某个主题,这里是抽象类型 } func (op *OverseasProxy) Buy(goods *Goods) { // 1. 先验货 if (op.distinguish(goods) == true) { //2. 进行购买 op.shopping.Buy(goods) //调用原被代理的具体主题任务 //3 海关安检 op.check(goods) } } //创建一个代理,并且配置关联被代理的主题 func NewProxy(shopping Shopping) Shopping { return &OverseasProxy{shopping} } //验货流程 func (op *OverseasProxy) distinguish(goods *Goods) bool { fmt.Println("对[", goods.Kind,"]进行了辨别真伪.") if (goods.Fact == false) { fmt.Println("发现假货",goods.Kind,", 不应该购买。") } return goods.Fact } //安检流程 func (op *OverseasProxy) check(goods *Goods) { fmt.Println("对[",goods.Kind,"] 进行了海关检查, 成功的带回祖国") } func main() { g1 := Goods{ Kind: "韩国面膜", Fact: true, } g2 := Goods{ Kind: "CET4证书", Fact: false, } //如果不使用代理来完成从韩国购买任务 var shopping Shopping shopping = new(KoreaShopping) //具体的购买主题 //1-先验货 if g1.Fact == true { fmt.Println("对[", g1.Kind,"]进行了辨别真伪.") //2-去韩国购买 shopping.Buy(&g1) //3-海关安检 fmt.Println("对[",g1.Kind,"] 进行了海关检查, 成功的带回祖国") } fmt.Println("---------------以下是 使用 代理模式-------") var overseasProxy Shopping overseasProxy = NewProxy(shopping) overseasProxy.Buy(&g1) overseasProxy.Buy(&g2) }
代理模式的核心要点为代理和原对象实现相同接口,但扩大了原对象的职能。
装饰器模式
装饰器模式和代理模式很相似,都是给原有的类添加额外的功能。然而区别在于,装饰器模式的特点在于可以不断“迭代“。
如下图所示,一个手机可以贴膜变成带膜的手机,也可以装壳变成带壳的手机,带壳的手机也可以贴膜变成带壳膜的手机。
Aceld的例子不是很直观,这里贴一个其他代码实现https://www.jianshu.com/p/f70f8b154be4
// 计算器 type Calculate interface { Cal() int} //创建一个基准struct,后面的装饰器给这个基准struct加功能 type OriCalculate struct { num int} func NewOriCalculate(num int)*OriCalculate{ return &OriCalculate{num:num} } func (o *OriCalculate)Cal() int{ return o.num } //创建基于基准struct的乘法 type MutCalculate struct{ Calculate num int} func NewMutCalculate(C Calculate,num int) *MutCalculate { return &MutCalculate{Calculate:C,num:num} } func (m *MutCalculate) Cal() int { return m.num*m.Calculate.Cal() } //创建基于基准struct的加法 type AddCalculate struct { Calculate num int} func NewAddCalculate(C Calculate,num int) *AddCalculate { return &AddCalculate{Calculate:C,num:num} } func (a *AddCalculate)Cal()int { return a.num+a.Calculate.Cal() } // 使用 func ExampleDecorator() { //o:=NewOriCalculate(1) //定义的时候,接口实现的是指针接收,所以用指针初始化 o:=&OriCalculate{1} m:=MutCalculate{o,2} a:=AddCalculate{o,3} log.Printf("Oricalculate is %d \n MutCalculate is %d\n AddCalculate is %d \n",o.Cal(),m.Cal(),a.Cal()) // Output: The output of // this example. }
通俗来讲,装饰器模式和代理模式最大的区别就在于,装饰器模式拥有一个迭代的特性,原类和装饰器都实现了同一个接口,那么每次这个“类”,被“装饰”后,他还是他自己(此处指拥有原本的方法),但是他成为了一个新的自己。在实际业务中,可以根据具体的业务需求来随意装饰。比代理模式更加灵活。
适配器模式
适配器模式较为简单,共有三个角色:适配目标,适配器,适配者
如下图所示,HttpClient利用成员HttpRequest.DoHttp()发送请求,除此外HttpClient包含了url,host,port,body等参数信息。
Http和RPC Adapter实现了HttpRequest接口。
默认情况下,当HttpClient调用DoHttp()方法时,将调用初始化时传入的Http对象中的Do()方法。当使用适配器时,将调用适配器RPC Adapter中的Do()方法。适配器将根据HttpClient中的url,host,port,body等参数信息来构造RPC请求并发。
package main import "fmt" type HttpRequest interface { Do() } type HttpClient struct { H HttpRequest Url stringHost string} func (h *HttpClient) DoReq() { h.H.Do() } func NewHttpClient(req HttpRequest) *HttpClient { return &HttpClient{H: req} } // 实现HttpRequest就恶口 type Http struct{} func (h *Http) Do() { fmt.Println("发送http请求") } type RPCAdapter struct { R *RPC } func (a *RPCAdapter) Do() { // 做一些rpc适配 // 发送rpc请求s a.R.DoRPC() } func NewRPCAdapter(r *RPC) HttpRequest { return &RPCAdapter{R: r} } type RPC struct { } func (r *RPC) DoRPC() { fmt.Println("发送rpc请求") } func main() { // -----不使用适配器----- http := NewHttpClient(&Http{}) http.DoReq() // -----使用适配器----- // 初始化一个rpc请求 rpc := &RPC{} // 将rpc请求装入适配器 adapter := NewRPCAdapter(rpc) // 初始化一个http请求 http = NewHttpClient(adapter) // 发送http请求,实际上被转化成了rpc请求 http.DoReq() }
适配器模式将目标类和适配者解耦,通过一个适配器类重现现有的适配者类。灵活性和拓展性较好,可以很方便的更换适配器,符合开闭原则。
外观模式
外观模式较为常见也较为简单。比如现在有ABCD四个对象的不同方法,当某个业务需要同时调用AB对象中的方法时,则需要分别调用两次。而如果此时使用外观模式,在AB对象外新加一层“外观”,当调用外观的方法时就自动调用了AB对象中的方法。
此处不再举出代码案例。