僵卧孤村不自哀,尚思为国戍轮台。夜阑卧听风吹雨,铁马冰河入梦来。
《十一月四日风雨大作》——陆游
听网络课程后的笔记,还不是很理解,小弟先记下,待未来小有理解后再来完善。
在组件构建过程中,某些接口之间直接的依赖常常会带来很多问题、甚至根本无法实现。采用添加一层间接(稳定)接口,来隔离本来互相紧密关联的接口是一种常见的解决方案。
一、静态代理模式
在面向对象系统中,由于一些原因(如对象创建的开销很大,或者某些操作需要安全控制,或者需要进程外的访问等)无法直接去访问这个对象或者说会造成很大麻烦。使用代理模式可以让我们间接的“访问”对象。(以下是静态代理模式,上面说的一些情况需要用到动态代理模式)
先放代码吧(明星与经纪人):
/**
* Subject Interface
*/
interface Subject {
void Signed(String company);
}
public class ProxyDemo {
public static void main(String[] args) {
Star star = new Star("小白脸");
//创建代理对象
Broker broker = new Broker(star);
broker.Signed("叼炸天");
}
}
class Star implements Subject {
private String name;
Star(String name) {
this.name = name;
}
@Override
public void Signed(String company) {
System.out.println(name + "与" + company + "高层见面");
}
}
class Broker implements Subject {
private Subject targetStar;//要代理的目标对象
Broker(Subject targetStar) {
this.targetStar = targetStar;
}
private void before() {
System.out.println("为被代理人与各公司商谈");
}
private void after() {
System.out.println("被代理人已成功签约进新公司");
}
@Override
public void Signed(String company) {
//签约方法
before();
targetStar.Signed("XX公司");
after();
}
}
所谓代理人,实际上是为被代理人去代理某些事物,就像上面代码的经纪人与明星,一些拍广告之类的事务一般都是由经纪人去与对方谈而非明星但这又是出自明星本身的意愿,就像那些想买火车票又不肯排队的人雇人替他们排队买票一样。(然而上WC是没法代理的)
上面代码中的明星与经济人就是这样的一种关系,一个明星退出原公司而想与一家新公司签约,然而这种事不肯能一次谈妥嘛,需要与多家公司去接触,根据各公司开出的条件来进行选择,明星想做的也许仅仅只是在合同书上签字罢了,他在不想自己去跟各公司接触呢,因为他早已出道并小有名气,那么与各公司交谈的工作自然就交给他的经纪人(代理人)去做啦。
首先,我们定义了一个接口,这个接口里的方法就是需要代理人代理的事务,而我们的明星类(被代理)和经纪人类(代理)都要实现这个接口。我们在创建明星对象后,将其传入经纪人类的构造方法表示对此对象开始代理,然后可以看到在经纪人类中对接口方法的实现里其实是调用了被代理人的签约方法,因为就算经纪人跟公司谈好了最后签合同的时候还是要你明星自己来不是么。这样,就完成了一个静态代理模式。
代理模式UML图要点总结
- “增加一层间接层”是软件系统中对许多复杂问题的一种常见解决方法。在面向对象的系统中,直接使用某些对象会带来很多问题,作为间接层proxy对象便是解决这一问题的常用手段。
- 具体代理模式的实现方法、实现粒度都相差很大,有些可能对单个对象做细粒度的控制,如copy-on-write技术,有些可能对组件模块提供抽象代理层,在架构层次对对象进行代理
- 代理模式并不一定要求保持接口完整的一致性,只要能够实现间接控制,有时候损失一些透明性还是可以接受的。
另外推荐一下别人的关于动态代理模式的博客——动态代理模式
二、适配器
说起来这个词对于Android编程来说还是挺熟悉的,ListView之类都得用嘛。就如生活中常见的电源适配器一样 ,为了让不相兼容的类能够一起工作,我们需要利用接口,将一个类的接口转换为客户希望的另一个接口。
/**
* 电源型号A
*/
interface PowerSource_A {
public void insert();
}
/**
* 电源型号B
*/
interface PowerSource_B {
public void connect();
}
/**
* A型
*/
class Power_A implements PowerSource_A {
@Override
public void insert() {
System.out.println("已连接A型电源");
}
}
/**
* B型
*/
class Power_B implements PowerSource_B {
@Override
public void connect() {
System.out.println("已连接B型电源");
}
}
class PowerSource_A_Adapter implements PowerSource_A {
private PowerSource_B psb;
public PowerSource_A_Adapter(PowerSource_B psb) {
this.psb = psb;
}
@Override
public void insert() {
psb.connect();
}
}
public class AdapterDemo {
public static void main(String[] args) {
PowerSource_A psa = new Power_A();
PowerSource_B psb = new Power_B();
PowerSource_A_Adapter psaa = new PowerSource_A_Adapter(psb);
openA(psa);
openA(psaa);
}
public static void openA(PowerSource_A pa) {
pa.insert();
}
}
如同你从欧洲或是日本买来的电器或是电子产品,由于跟我们的家用标准电压不同(又或是插头的类型不是我们这边样式),我们都需要一个中间的“适配器”才能让这些玩意正常使用。
上面的代码中创建了两个不同的电源类型的接口并分别实现了两个电源类,分别实现的两个用来接入的方法实际上可以当成两种插头。然后看Demo类里定义了一个openA()方法,它需要传入A型电源的对象,那么现在我只创建了这个openA方法而没有openB,这就像是我们家里的插口面板,正好是A型的,没家中没有B型的插孔。于是本着人道主义,我们也应该让B型电源插入插孔嘛。(你说为什么电源还要插入插孔,电脑的电源呗!)
于是就要创建我们的主角适配器类PowerSource_A_Adapter ,它也继承A型号接口,但传入的参数是B型电源的对象,并在实现A的接入方法中实际调用B的接入方法,这样就完成了适配。
要点总结
- Adapter模式主要应用于“希望复用一些现存的类,但是接口又与复用环境要求不一致的情况”,在遗留代码复用、类库迁移等方面非常有用。
- 对象适配器采用“对象组合”方式,更符合松耦合精神。
网友评论