代理模式(Proxy Pattern) :为某个对象提供一个代理,并由这个代理对象控制对原对象的访问。
定义解读:使用代理模式以后,客户端直接访问代理,代理在客户端和目标对象之间起到中介的作用。
在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为“代理”的第三者来实现间接引用。
因为代理对象可以在客户端和目标对象之间起到中介的作用,因此可以通过代理对象去掉客户不能看到 的内容和服务或者添加客户需要的额外服务。
根据业务的不同,代理也可以有不同的类型:
下面来看一下代理模式的成员和类图。
代理模式算上客户端一共有四个成员:
其实我也不太清楚代理模式里面为什么会是Subject和RealSubject这个叫法。
下面通过类图来看一下各个成员之间的关系:
代理模式类图
从类图中可以看出,工厂类提供一个静态方法:通过传入的字符串来制造其所对应的产品。
在这里举一个买房者通过买房中介买房的例子。
现在一般我们买房子不直接接触房东,而是先接触中介,买房的相关合同和一些事宜可以先和中介进行沟通。
在本例中,我们在这里让买房者直接支付费用给中介,然后中介收取一部分的中介费, 再将剩余的钱交给房东。
中介作为房东的代理,与买房者直接接触。而且中介还需要在真正交易前做其他的事情(收取中介费,帮买房者check房源的真实性等等),因此该场景比较适合使用代理模式。
根据上面的代理模式的成员:
首先我们定义一下房东和代理需要实现的接口PaymentInterface
(在类图里面是继承某个共同对象,我个人比较习惯用接口来做)。
//================== PaymentInterface.h ==================
@protocol PaymentInterface <NSObject>
- (void)getPayment:(double)money;
@end
这个接口声明了中介和房东都需要实现的方法getPayment:
接着我们声明代理类HouseProxy
:
//================== HouseProxy.h ==================
@interface HouseProxy : NSObject<PaymentInterface>
@end
//================== HouseProxy.m ==================
const float agentFeeRatio = 0.35;
@interface HouseProxy()
@property (nonatomic, copy) HouseOwner *houseOwner;
@end
@implementation HouseProxy
- (void)getPayment:(double)money{
double agentFee = agentFeeRatio * money;
NSLog(@"Proxy get payment : %.2lf",agentFee);
[self.houseOwner getPayment:(money - agentFee)];
}
- (HouseOwner *)houseOwner{
if (!_houseOwner) {
_houseOwner = [[HouseOwner alloc] init];
}
return _houseOwner;
}
@end
在HouseProxy
里面,持有了房东,也就是被代理者的实例。然后在的getPayment:
方法里,调用了房东实例的getPayment:
方法。而且我们可以看到,在调用房东实例的getPayment:
方法,代理先拿到了中介费(中介费比率agentFeeRatio
定义为0.35,即中介费的比例占35%)。
这里面除了房东实例的getPayment:
方法之外的一些操作就是代理存在的意义:它可以在真正被代理对象做事情之前,之后做一些其他额外的事情。比如类似AOP编程一样,定义类似的before***Method
或是after**Method
方法等等。
最后我们看一下房东是如何实现getPayment:
方法的:
//================== HouseOwner.h ==================
@interface HouseOwner : NSObject<PaymentInterface>
@end
//================== HouseOwner.m ==================
@implementation HouseOwner
- (void)getPayment:(double)money{
NSLog(@"House owner get payment : %.2lf",money);
}
@end
房东类HouseOwner
按照自己的方式实现了getPayment:
方法。
很多时候被代理者(委托者)可以完全按照自己的方式去做事情,而把一些额外的事情交给代理来做,这样可以保持原有类的功能的纯粹性,符合开闭原则。
下面我们看一下客户端的使用以及打印出来的结果:
客户端代码:
//================== client.m ==================
HouseProxy *proxy = [[HouseProxy alloc] init];
[proxy getPayment:100];
上面的客户端支付给了中介100元。
下面我们看一下打印结果:
Proxy get payment : 35.00
House owner get payment : 65.00
和预想的一样,中介费收取了35%的中介费,剩下的交给了房东。
代理模式代码示例类图
从UML类图中我们可以看出,在这里没有使用抽象主题对象,而是用一个接口来分别让中介和房东实现。
JDKDynamicAopProxy
是对JDK的动态代理进行了封装