大家好,这里是每周都陪你进步的网管~
其实也不是每周啦,上周阳了~实在是进步不动了......这周咱们继续之前搁置了一段时间的设计模式系列。
上一次咱们分享的是职责链模式,在文章最后提到了一下装饰器模式,两者虽然结构上类似但在用途上还是有区别的,而装饰器模式本身算是代理模式的一个特殊应用,所以这篇文章我们就先来学习一下代理模式的构成和用法,后面再来学习装饰器。
老看我文章的同学可能发现了,虽然教材上是把设计模式分成了建造型、结构型、行为型三大块展开的,但是我没有按照这个大纲来铺开内容,而是更注重延续性一点,力求尽量能做到由一种模式引出关联的另外一种模式。所以在学完"流程开发的三个利器"—模版、策略和职责链三个行为型模式后,我们先把其他行为型的模式放一放,先来学习两个结构型的模式—代理和装饰器。
什么是代理模式
代理模式是一种结构型设计模式。其中代理控制着对于原对象的访问,并允许在将请求提交给原对象的前后进行一些处理,从而增强原对象的逻辑处理。
上面的代理者我们一般叫做代理对象或者直接叫做代理--Proxy,进行逻辑处理的原对象通常被称作服务对象,代理要跟服务对象实现相同的接口,才能让客户端傻傻分不清自己使用的到底是代理还是真正的服务对象,这样一来代理就能在客户端察觉不到的情况下对服务对象的处理逻辑进行增强。
什么叫对处理逻辑进行增强?或者换一种说法,叫对核心功能添加增强功能?举个例子来说,处理客户端查询用户订单信息的APIHandler就是核心处理逻辑,增强逻辑就是我们需要在查询订单信息之前,验证请求是否是有效用户、记录请求的参数和返回的响应数据等等。
看了上面代理模式的解释,你可能还是觉得有点宽泛,下面咱们写一个简单的代码示例,这个过程中你差不多就会发现:“诶,原来这就是代理模式啊,我之前写代码的时候早就用过了~!”下面我们一起开下这个例子吧。
代理模式使用演示
假设有一个代表小汽车的Car类型
typeCarstruct{}
小汽车要的主要行为就是可以让人驾驶,所以Car需要实现一个代表驾驶行为的接口(interface)Vehicle,该接口只有一个方法Drive。
"本文使用的完整可运行源码
typeVehicleinterface{
Drive
}
typeCarstruct{}
func(c*Car)Drive{
fmt.Println("Carisbeingdriven")
}
Car的结构体指针通过实现Drive方法实现了Vehicle接口。
现在我们只要实例化一个Car的实例,在实例上面调用Drive方法就能让车开起来,不过如果我们的驾驶员现在还是个未成年,那么在地球的大部分国家都是不允许开车的,如果在开车时要加一个驾驶员的年龄限制,我们该怎么办呢?
给Car结构体加一个Age字段显然是不合理的,因为我们要表示的驾驶员的年龄而不是车的车龄。同理驾驶员年龄的判断我们也不应该加在Car实现的Drive方法里,这样会导致每个实现Vehicle接口的类型都要在自己的Drive方法里加上类似的判断。
这个时候通常的做法是,加一个表示驾驶员的类型Driver。
typeDriverstruct{
Ageint
}
然后再来一个包装Driver和Vehicle类型的包装类型。
"本文使用的完整可运行源码
typeCarProxystruct{
vehicleVehicle
driver*Driver
}
funcNewCarProxy(driver*Driver)*CarProxy{
return&CarProxy{&Car{},driver}
}
这样的话我们接可以通过,用包装类型代理vehicle属性的Drive行为时,给它加上驾驶员的年龄限制。
func(c*CarProxy)Drive{
ifc.driver.Age>=16{
c.vehicle.Drive
}else{
fmt.Println("Drivertooyoung!")
}
}
我相信这个编程技巧大家在平时开发中都用过,这个其实就是代理模式。
现在我们通过代理模式给Car类型的Drive行为扩充了检查驾驶员的行为,下面我们执行一下程序试试效果。
"本文使用的完整可运行源码
funcmain{
car:=NewCarProxy(&Driver{12})
car.Drive//输出Drivertooyoung!
car2:=NewCarProxy(&Driver{22})
car2.Drive//输出Carisbeingdriven
}
正如执行后的结果所示,我们不必为服务对象--Car类型添加任何属性和方法。相反,我们只是在其上面的代理层把客户端Drive方法的调用委托(英文术语叫delegate)给了其vehicle属性的Drive方法,并在之前添加了年龄检查行为,从而达到我们想要的效果。
看完例子后,相信大家都理解了写代码时怎么使用代理模式,下面我们从代码走出来,再更清晰的描述下代理模式它的整体结构。
看清代理模式
根据上面一开始的描述和后面的代码例子,我们总结出来,参与代理模式的一共有四种角色:客户端、服务接口、服务类和代理类,他们之间的关系用UML类图表示如下:

代理模式--UML 类图
上面UML类图一共有四个角色,这四个角色在代理模式中的职责分别是。
服务接口(ServiceInterface)声明了服务类要实现的接口。服务类的业务处理逻辑就是实现在这里定义的接口方法中,代理类也必须遵循该接口才能伪装成服务对象。
服务(Service)类,就是上面说的,提供实际业务逻辑的原对象。
代理(Proxy)类包含一个服务对象作为成员变量。代理完成其任务(例如延迟初始化、记录日志、访问控制和缓存等)后面会将请求传递给服务对象。通常情况下,代理会对其服务对象的整个生命周期进行管理,来增强服务对象,这样与核心业务逻辑不相关的增强逻辑就可以由代理来实现。
客户端(Client)通过统一接口与服务或代理进行交互,所以可在一切需要服务对象的代码中使用服务对象的代理,客户端完全不会感知到。
代理模式延伸
在代理模式中,通过让代理类实现跟服务类相同的接口,从而把代理类伪装成了服务类,客户端请求代理时,代理再把请求委派给其持有的真实服务类,在委派的过程中我们就可以添加增强逻辑。
如果我们把代理类当成服务对象再给代理类加个代理,代理的代理再加代理,那么就变成了另外一种设计模式--装饰器模式啦,其实装饰器模式本身就是代理模式的一个特殊应用,关于装饰器的内容,我们放到后面进行学习。

哈哈哈 子弹越打越多了
哈哈哈 子弹越打越多了
《刺客信条: 黑旗 记忆重置》回应DLC比本体贵: DLC非必须
《刺客信条:黑旗 记忆重置》的首发表现可以说不错,Steam玩家峰值直逼10万。然而Steam的玩家评价却“褒贬不一”。除了游戏宣发问题外,还有一个原因就是总价超越本体的大量DLC,让玩家觉得育碧“吃相难看”。《刺客…当臭猫扶贫神秘嘉豪号主!
原神: 7.0奥黛塔定位反转! 并非木偶专属, 冰神设计成最大悬念
各位旅行者,今天咱们来聊聊原神7.0版本里一个让人意外的角色调整——奥黛塔的定位变化。千万没想到,原本大家都以为奥黛塔是木偶的专属队友,结果根据她的机制和定位来看,这是一位同时拥有星超导和星扩散的双机制…哪辆车能不被28吨钢卷压成废铁? 哪辆车能不被28吨钢卷压成废铁?
原神: 新超导木偶队顶配配装攻略! 告别刮痧与充能卡顿
很多玩家都有这样的困惑,同样是木偶新超导队伍,部分玩家用四星队友就能打出高额满配伤害,而自己配齐五星阵容,却始终面临伤害刮痧、充能卡顿的问题。其实核心差距并非角色配置,而是多数玩家没有掌握版本更新后,…