作为一个编程菜鸟,过去在学习设计模式的时候,老师给推荐了一本《大话设计模式》。阅读以后受益匪浅,可惜当初没有坚持看完。
最近有时间了,又重新捡起来学习了一遍,整理了一下笔记,由于本人能力有限,欢迎大家批评指正。
1.适配器模式 Adapeter Pattern
- 将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
- 在系统的数据和行为都正确,但接口不符时,我们应该考虑用适配器,目的是使控制范围之外的一个原有对象与某个接口匹配。适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况。或者说当两个类所做的事情相同或相似,但是具有不同的接口时,而双方都不太容易修改的时候使用。
2.分类
(1)对象适配器模式
适配器容纳一个它包裹的类的实例。以下主要讲解关于对象适配器模式的运用。
(2)类适配器模式
这种适配器模式下,适配器继承自己实现的类(一般多重继承)。
(3)缺省适配器模式
缺省适配器模式是一种特殊的适配器模式,但这个适配器是由一个抽象类实现的,并且在抽象类中要实现目标接口中所规定的所有方法,但很多方法都是空方法。此时此类无须实例化。而只充当适配器的角色,为其子类提供了一个共同的接口,但其子类又可以选择其需要的方法进行重写。
3.uml类图
4.组成
a.Target
这是客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口。
b.Adaptee
需要适配的类。
c.Adapter
通过在内部包装一个Adaptee对象,把源接口转换成目标接口。
5.实例
(1)需求
火箭队教练布置战术,姚明初来乍到听不懂英文,需要翻译进行适配。
(2)代码
a.战术接口
package com.longinus.ap;
public interface Tactics {
public abstract void Attack();
public abstract void Defense();
}
b.各个球员类
package com.longinus.ap;
public class Forwards implements Tactics{
private String name;
public Forwards(String name) {
// TODO Auto-generated constructor stub
setName(name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void Attack() {
// TODO Auto-generated method stub
System.out.println(getName() + "挡拆、错位、突破、跳投");
}
@Override
public void Defense() {
// TODO Auto-generated method stub
System.out.println(getName() + "协防");
}
}
package com.longinus.ap;
public class Guards implements Tactics{
private String name;
public Guards(String name) {
// TODO Auto-generated constructor stub
setName(name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void Attack() {
// TODO Auto-generated method stub
System.out.println(getName() + "底角三分");
}
@Override
public void Defense() {
// TODO Auto-generated method stub
System.out.println(getName() + "锁死对方球星");
}
}
package com.longinus.ap;
public class ForeignCenter {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ForeignCenter(String name) {
// TODO Auto-generated constructor stub
setName(name);
}
public void 进攻(String str){
System.out.println(str);
}
public void 防守(String str){
System.out.println(str);
}
}
c.翻译适配器类
package com.longinus.ap;
public class Translator implements Tactics {
private ForeignCenter fc;
public Translator(String name) {
// TODO Auto-generated constructor stub
fc = new ForeignCenter(name);
}
@Override
public void Attack() {
// TODO Auto-generated method stub
fc.进攻(fc.getName() + "挡拆策应,底线卡位,要球勾手,转身跳投");
}
@Override
public void Defense() {
// TODO Auto-generated method stub
fc.防守(fc.getName() + "保护篮筐、封盖投篮");
}
}
d.测试类
package com.longinus.ap;
public class Test {
public static void main(String[] args) {
Forwards f = new Forwards("T-Mac");
Guards g = new Guards("Battier");
Translator c = new Translator("YaoMing");
System.out.println("教练布置战术");
f.Attack();
f.Defense();
g.Attack();
g.Defense();
c.Attack();
c.Defense();
}
}
e.输出结果
教练布置战术
T-Mac挡拆、错位、突破、跳投
T-Mac协防
Battier底角三分
Battier锁死对方球星
YaoMing挡拆策应,底线卡位,要球勾手,转身跳投
YaoMing保护篮筐、封盖投篮
6.补充
(1)如何做都一个类不被实例化或者不被轻易实例化?
- 把一个类定义为抽象类
- 把一个类的构造方法设置为private类型,禁用new获取对象的实例,只能通过特殊的方法来实现,此时在一个系统中对此类的使用就能得到合理的控制(如:单例模式、多例模式、简单工厂方法等模式)
- 对于两个独立的系统,要满足OCP原则,则适配器模式会有一定的局限性
(2)如果能事先预防接口不同的问题,不匹配问题就不会发生。在有小的接口不统一问题发生时,及时重构,问题不至于扩大。只有碰到无法改变原有设计和代码的情况时,才考虑适配。
7.装饰者模式与适配器模式的区别
(1)关于新职责
适配器也可以在转换时增加新的职责,但主要目的不在此。装饰者模式主要是给被装饰者增加新职责的。
(2)关于原接口
适配器模式是用新接口来调用原接口,原接口对新系统是不可见或者说不可用的。装饰者模式原封不动的使用原接口,系统对装饰的对象也通过原接口来完成使用。(增加新接口的装饰者模式可以认为是其变种-“半透明”装饰者)
(3)关于其包裹的对象
适配器是知道被适配器者的详细情况的(就是那个类或那个接口)。装饰者只知道其接口是什么,至于其具体类型(是基类还是其他派生类)只有在运行期间才知道。