代理模式
Proxy模式又叫做代理模式,是构造型的设计模式之一,它可以为其他对象提供一种代理(Proxy)以控制对这个对象的访问。
所谓代理,是指具有与代理元(被代理的对象)具有相同的接口的类,客户端必须通过代理与被代理的目标类交互,而代理一般在交互的过程中(交互前后),进行某些特别的处理。
用一个日常可见的案例来理解“代理”的概念,如下图:
这里以一个进口水果作为一个主题任务,这是一个抽象的任务。具体的购物主题包括tw进口、flb进口,这些都是可以自己去完成的,
package main
import "log"
type fruit struct {
name string //水果名
tax bool //有没有税
}
type fruitImport interface {
buy(f *fruit)
}
type twImport struct {
}
func (*twImport) buy(f *fruit) {
log.Println("buy-tw-",f.name)
}
type flbImport struct {
}
func (*flbImport) buy(f *fruit) {
log.Println("buy-flb-",f.name)
}
type fruitProxy struct {
fruit fruitImport
}
func (fp *fruitProxy) buy(f *fruit) {
log.Println("代理商去买-",f.name)
fp.fruit.buy(f)
if f.tax {
log.Println("交税-",f.name)
}
}
func newFruitProxy(f fruitImport) fruitImport {
return &fruitProxy{fruit:f}
}
func main() {
//正常调用
banana := &fruit{name:"香蕉",tax:true}
apple := &fruit{name:"苹果",tax:false}
new(twImport).buy(banana)
new(flbImport).buy(apple)
//代理购买
proxy := newFruitProxy(&twImport{})
proxy.buy(banana)
proxy.buy(apple)
}
输出
2024/06/14 09:57:15 buy-tw- 香蕉
2024/06/14 09:57:15 buy-flb- 苹果
2024/06/14 09:57:15 代理商去买- 香蕉
2024/06/14 09:57:15 buy-tw- 香蕉
2024/06/14 09:57:15 交税- 香蕉
2024/06/14 09:57:15 代理商去买- 苹果
2024/06/14 09:57:15 buy-tw- 苹果
代理模式的优缺点
优点:
(1) 能够协调调用者和被调用者,在一定程度上降低了系统的耦合度。
(2) 客户端可以针对抽象主题角色进行编程,增加和更换代理类无须修改源代码,符合开闭原则,系统具有较好的灵活性和可扩展性。
缺点:
(1) 代理实现较为复杂。
适用场景
为其他对象提供一种代理以控制对这个对象的访问。
装饰模式
装饰模式(Decorator Pattern):动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式比生成子类实现更为灵活。装饰模式是一种对象结构型模式。
package main
import "fmt"
// ---------- 抽象层 ----------
//抽象的构件
type Phone interface {
Show() //构件的功能
}
//装饰器基础类(该类本应该为interface,但是Golang interface语法不可以有成员属性)
type Decorator struct {
phone Phone
}
func (d *Decorator) Show() {}
// ----------- 实现层 -----------
// 具体的构件
type HuaWei struct {}
func (hw *HuaWei) Show() {
fmt.Println("秀出了HuaWei手机")
}
type XiaoMi struct{}
func (xm *XiaoMi) Show() {
fmt.Println("秀出了XiaoMi手机")
}
// 具体的装饰器类
type MoDecorator struct {
Decorator //继承基础装饰器类(主要继承Phone成员属性)
}
func (md *MoDecorator) Show() {
md.phone.Show() //调用被装饰构件的原方法
fmt.Println("贴膜的手机") //装饰额外的方法
}
func NewMoDecorator(phone Phone) Phone {
return &MoDecorator{Decorator{phone}}
}
type KeDecorator struct {
Decorator //继承基础装饰器类(主要继承Phone成员属性)
}
func (kd *KeDecorator) Show() {
kd.phone.Show()
fmt.Println("手机壳的手机") //装饰额外的方法
}
func NewKeDecorator(phone Phone) Phone {
return &KeDecorator{Decorator{phone}}
}
// ------------ 业务逻辑层 ---------
func main() {
var huawei Phone
huawei = new(HuaWei)
huawei.Show() //调用原构件方法
fmt.Println("---------")
//用贴膜装饰器装饰,得到新功能构件
var moHuawei Phone
moHuawei = NewMoDecorator(huawei) //通过HueWei ---> MoHuaWei
moHuawei.Show() //调用装饰后新构件的方法
fmt.Println("---------")
var keHuawei Phone
keHuawei = NewKeDecorator(huawei) //通过HueWei ---> KeHuaWei
keHuawei.Show()
fmt.Println("---------")
var keMoHuaWei Phone
keMoHuaWei = NewMoDecorator(keHuawei) //通过KeHuaWei ---> KeMoHuaWei
keMoHuaWei.Show()
}
秀出了HuaWei手机
---------
秀出了HuaWei手机
贴膜的手机
---------
秀出了HuaWei手机
手机壳的手机
---------
秀出了HuaWei手机
手机壳的手机
贴膜的手机
装饰模式的优缺点
优点:
(1) 对于扩展一个对象的功能,装饰模式比继承更加灵活性,不会导致类的个数急剧增加。
(2) 可以通过一种动态的方式来扩展一个对象的功能,从而实现不同的行为。
(3) 可以对一个对象进行多次装饰。
(4) 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,原有类库代码无须改变,符合“开闭原则”。
缺点:
(1) 使用装饰模式进行系统设计时将产生很多小对象,大量小对象的产生势必会占用更多的系统资源,影响程序的性能。
(2) 装饰模式提供了一种比继承更加灵活机动的解决方案,但同时也意味着比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为繁琐。
适用场景
(1) 动态、透明的方式给单个对象添加职责。
(2) 当不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展和维护时可以使用装饰模式。
装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。换句话说,用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。
与代理的区别:感觉代理是处理额外逻辑,装饰是添加额外逻辑