读书笔记:headfirst 设计模式 ,大话设计模式 ,及http://www.cnblogs.com/V1haoge博客
适配器模式
现实中的适配器
以前的手机USB数据线连接手机大多是老式的梯形microUSB接口,
而现在连接大多数手机都采用了速度更快typeC椭圆形接口,
以前的旧数据线还想继续使用怎么办呢?
配一个microUSB转typeC的转接头就可以解决问题,
这个转接头就是现实生活中的一种适配器
适配器模式模式
概念: 将一个类的接口转换成客户希望的另外一个接口,Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作.
结构图
类适配器与对象适配器
类适配器
原理:通过继承来实现适配器功能
当我们要访问的接口A中没有我们想要的方法 ,却在另一个接口B中发现了合适的方法,我们又不能改变访问接口A,在这种情况下,我们可以定义一个适配器p来进行中转,这个适配器p要实现我们访问的接口A,这样我们就能继续访问当前接口A中的方法(虽然它目前不是我们的菜),然后再继承接口B的实现类BB,这样我们可以在适配器P中访问接口B的方法了,这时我们在适配器P中的接口A方法中直接引用BB中的合适方法,这样就完成了一个简单的类适配器。
对象适配器
原理:通过组合来实现适配器功能
当我们要访问的接口A中没有我们想要的方法 ,却在另一个接口B中发现了合适的方法,我们又不能改变访问接口A,在这种情况下,我们可以定义一个适配器p来进行中转,这个适配器p要实现我们访问的接口A,这样我们就能继续访问当前接口A中的方法(虽然它目前不是我们的菜),然后在适配器P中定义私有变量C(对象)(B接口指向变量名),再定义一个带参数的构造器用来为对象C赋值,再在A接口的方法实现中使用对象C调用其来源于B接口的方法
代码案例
public class AdapterTest {
public static void main(String[] args) {
IMicroUSB classAdapter = new ClassAdapter();
System.out.print("使用类适配器效果:");
classAdapter.isMicroUSB();
IMicroUSB objAdapter = new ObjectAdapter(new TypeC());
System.out.print("使用对象适配器效果:");
objAdapter.isMicroUSB();
}
}
结果:
使用类适配器效果:新式typeC口
使用对象适配器效果:新式typeC口
// 描述microUSB接口
public interface IMicroUSB {
void isMicroUSB();
}
// 描述typeC接口
public interface ITypeC {
void isTypeC();
}
// TypeC接口实现
public class TypeC implements ITypeC {
@Override
public void isTypeC() {
System.out.println("新式typeC口");
}
}
// 类适配器
public class ClassAdapter extends TypeC implements IMicroUSB {
@Override
public void isMicroUSB() {
isTypeC();
}
}
// 对象适配器
public class ObjectAdapter implements IMicroUSB {
private ITypeC typeC;
ObjectAdapter(ITypeC typeC){
this.typeC=typeC;
}
@Override
public void isMicroUSB() {
typeC.isTypeC();
}
}
-
类适配器与对象适配器使用场景一致,用于连接二者,将不匹配变的匹配
-
使用适配器一般属于无奈之举,在双方都不太容易修改的时候再使用适配器
-
类适配器与对象适配器区别:
其实就是从两个角度来描述一类问题,要访问的方法不在合适的接口内,一个从接口出发(被访问,类适配),一个从访问出发(主动访问,对象适配)
接口适配器
原理:通过抽象类来实现适配,这种适配稍别于上面所述的适配。
当存在这样一个接口,其中定义了N多的方法,而我们现在却只想使用其中的一个到几个方法,如果我们直接实现接口,那么我们要对所有的方法进行实现,哪怕我们仅仅是对不需要的方法进行置空(只写一对大括号,不做具体方法实现)也会导致这个类变得臃肿,调用也不方便,这时我们可以使用一个抽象类作为中间件,即适配器,用这个抽象类实现接口,而在抽象类中所有的方法都进行置空,那么我们在创建抽象类的继承类,而且重写我们需要使用的那几个方法即可。
代码案例
// 目标接口
public interface A {
void a();
void b();
void c();
void d();
void e();
}
// 接口适配器 将来需要实现用到的 a()与d() 未被空实现
public abstract class AbstractAdapter implements A {
@Override
public void b() {
}
@Override
public void c() {
}
@Override
public void e() {
}
}
// 实现类
public class AImpl extends AbstractAdapter {
@Override
public void a() {
System.out.println("实现a方法被调用");
}
@Override
public void d() {
System.out.println("实现d方法被调用");
}
}
// 测试
public class AbstractAdapterTest {
public static void main(String[] args) {
A a = new AImpl();
a.a();
a.d();
}
}
接口适配器使用场景:
想要使用接口中的某个或某些方法,但是接口中有太多方法,我们要使用时必须实现接口并实现其中的所有方法,可以使用抽象类来实现接口,并不对方法进行实现(仅置空),然后我们再继承这个抽象类来通过重写想用的方法的方式来实现。这个抽象类就是适配器
外观模式
迪米特(最少知识)原则
最少知识原则:如果两个类不必彼此直接通信,那么这两份类就不应当发生直接的相互作用,如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用
- 只和你的密友谈话
- 这个原则希望我们在设计中不要让太多的类耦合在一起
- 优势会减少未来的维护成本,但可能会导致复杂度增加,降低运行时性能
外观模式
概念: 为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一字系统更加容易使用(屏蔽系统复杂性)
- 可以为一个子系统实现一个以上的外观
- 实现一个外观需要将子系统组合进外观中,然后将工作委托给子系统执行
结构图 :
代码案例
//子系统方法1
public class SubMethod1 {
public void method1(){
System.out.println("子系统中类1的方法1");
}
}
//子系统方法2
public class SubMethod2 {
public void method2(){
System.out.println("子系统中类2的方法1");
}
}
//子系统方法3
public class SubMethod3 {
public void method3(){
System.out.println("子系统中类3的方法1");
}
}
// 外观类
public class Facader {
private SubMethod1 sm1 = new SubMethod1();
private SubMethod2 sm2 = new SubMethod2();
private SubMethod3 sm3 = new SubMethod3();
public void facMethod1(){
sm1.method1();
sm2.method2();
}
public void facMethod2(){
sm2.method2();
sm3.method3();
sm1.method1();
}
}
// 测试类
public class Clienter {
public static void main(String[] args) {
Facader face = new Facader();
face.facMethod1();
face.facMethod2();
}
}
何时使用外观模式
- 当需要简化并统一一个很大的接口或者一群复杂的接口时,使用外观模式
- 设计初阶阶段,有意识的将不同的两个层分离,层与层之间建立外观(三层架构 例:MVC三层架构)
- 开发阶阶段,子系统不断重构演变变得复杂,增加外观减少依赖
- 维护遗留旧系统时,可以为旧系统建立外观与新系统交互
装饰者,适配器,外观模式的意图
适配器将一个对象包装起来以改变其接口,装饰者将一个对象包装起来以增加新的行为和责任,而外观将一群对象组合起来以简化其接口.
模式 | 意图 |
---|---|
装饰者 | 不改变接口,但加入行为,责任 |
适配器 | 将一个接口转成另外一个接口 |
外观 | 让接口更简单 |