这是我看Head first设计模式书籍之后想要总结的知识点,一方面是对自己学习的东西总结和提炼加强自己的理解和记忆,另一方面是给大家简化这本书,方便大家快速了解各种设计模式。
我想提醒大家的是,设计模式只是前人总结的一些经验套路,实际上还是要在开发项目中慢慢体会,不可成为设计模式的中毒患者,强行照搬设计模式的一些规则。
我们现在要包装某些对象:让它们的接口看起来不像是自己而像是别的东西。可以将类的接口转换成想要的接口,
以便实现不同的接口。我们还将探讨另外一个模式,将对象包装起来以简化其接口。
这次我们将要讲解的设计模式是适配器模式和外观模式。
我们举个栗子:
假设你缺少鸭子对象,想用一些火鸡对象来冒充,显而易见,因为火鸡的接口不同,所以我们不能
公然用。那么就借助适配器。
直接看代码部分:
鸭子接口
public interface Duck{
public void quack(); //呱呱叫
public void fly(); //飞行能力
}
鸭子具体实现类 绿头鸭
public class MallardDuck implements Duck{
public void quack(){
System.out.println("Quack");
}
public void fly(){
System.out.println("I'm flying");
}
}
火鸡接口
public interface Turkey{
public void gobble(); //咯咯叫
public void fly(); //火鸡飞不远
}
火鸡具体实现类
public class WildTurkey implements Turkey{
public void gobble(){
System.out.println("Gobble gobble");
}
public void fly(){
System.out.println("I'm flying a short distance");
}
}
适配器类实现想要转换成的类型接口,也就是你的客户所期望看到的接口,也就是鸭子接口
//如果想要用火鸡对象来适配鸭子接口则需一个适配器
public class TurkeyAdapter implements Duck{
Turkey turkey;
//得到要适配对象的引用,即火鸡对象的引用
public TurkeyAdpater(Turkey turkey){
this.turkey = turkey;
}
public void quack(){
turkey.gobble();
}
public void fly(){
for(int i = 0; i < 5; i++){
turkey.fly(); //要让鸭子的飞行和火鸡的飞行能够对应,因此连续调用5次火鸡飞行方法
}
}
}
测试类
//测试适配器
public class DuckTestDrive{
public static void main(String args[]){
MallardDuck duck = new MallardDuck();
WildTurkey turkey = new WildTurkey();
Duck turkeyAdpater = new TurkeyAdapter(turkey);
System.out.println("The Turkey says...");
turkey.gobble();
turkey.fly();
System.out.println("\nThe Duck says...");
testDuck(duck);
System.out.println("\nThe TurkeyAdpater says...");
testDuck(turkeAdapter);
}
static void testDuck(Duck duck){
duck.quack();
duck.fly();
}
}
/*
测试结果:
The Turkey says...
Gobble gobble
I'm flying a short distance
The Duck says...
Quack
I'm flying
The turkeyAdpater says...
Gobble gobble
I'm flying a short distance
I'm flying a short distance
I'm flying a short distance
I'm flying a short distance
I'm flying a short distance
*/
客户使用适配器的过程如下:
1.客户(也就是测试程序,使用Duck接口方法)通过目标接口调用适配器的方法对适配器发出请求
2.适配器使用被适配者接口把请求转换成被适配者的一个或多个调用接口
3.客户接受到调用的结果,但并未察觉这一切是适配器在起转换作(也就是说客户和被适配者是解耦的,一个不知道另一个)
注: 实现一个适配器所需要进行的工作,的确和目标接口的大小成正比。如果不用适配器,你就必须改写客户端的代码来调用这
个新的接口,将会花许多力气做大量的调查工作和代码改写工作。相比之下,提供一个适配器类,将所有的改变封装在一个类中,是比较好的做法。
下面是我们的适配器模式出场了:
适配器模式: 让一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。
优点: 使用对象组合,以修改的接口包装被适配者,而且被适配者的任何子类,都可以搭配着适配器使用。
这个模式把客户和接口绑定起来,而不是和实现绑定起来的。
对象适配器和类适配器:
前面的是对象适配器,而类适配器需要多继承才能够实现它,可以使用C++来实现代码
对象适配器不仅可以适配某个类,也可以适配该类的任何子类。类适配器可以覆盖被适配者的行为,因为利用了继承的方式。
我们举个栗子:
如果有个家庭影院,你在观赏电影的时候要执行一些任务(将灯光调暗、打开投影仪、将功放设置为环绕立体声等等),也就是说
你需要调用不同对象的方法才能实现。有了外观模式,通过实现一个提供更合理的接口的外观类,你可以将一个复杂的子系统变得容易使用。
我们客户可以通过调用watchMovie()方法来观看电影
让我们直接看代码:
第一部是使用组合让外观能够访问子系统中所有的组件
public class HomeTheaterFacade{
//我们会用到的子系统组件全部都在这里
Amplifier amp;
Tuner tuner;
DvdPlayer dvd;
CdPlayer cd;
Projector projector;
TheaterLights lights;
Screen screen;
PopcornPopper popper;
public HomeTheaterFacade(Amplifier amp,
Tuner tuner,
DvdPlayer dvd,
CdPlayer cd,
Projector projector,
Screen screen,
TheaterLights lights,
PopcornPopper popper){
this.amp = amp;
this.tuner = tuner;
//此处省略
}
public void watchMovie(String movie){
System.out.println("Get ready to watch a movie...");
popper.on();
popper.pop();
lights.dim(10);
screen.down();
projector.on();
//此处省略
dvd.play(movie);
}
public void endMovie(){
System.out.println("Shutting movie theater down...");
popper.off();
lights.on();
screen.up();
//此处省略
}
}
public class HomeTheaterTestDrive{
public static void main(String args[]){
//在这里实例化组件省略
HomeTheaterFacade homeTheater = new HomeTheaterFacade(amp,
tuner, dvd, cd, projector, screen, lights, popper);
homeTheater.watchMovie("Ring Ring Bang!");
homeTheater.endMovie();
}
}
下面是我们的外观模式出场了
外观模式: 提供了一个统一的接口,用来访问子类系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。
外观的意图是要提供一个简单的接口,好让一个子系统更易于使用
让我们看一个新的OO设计原则:
"最少知识原则": 同迪米特法则,可以简单理解成talk only to your immediate friends.也就是只和你的密友谈话。
一个软件实体应当尽可能少的与其他实体发生相互作用。每一个软件单元对其他的单位都只有很少的知识,而且局限于那些与
本单位密切相关的软件单位。初衷在于降低类之间的耦合。
这个原则提供了一些方针: 在该对象的方法内,我们只应该调用属于以下范围的方法:
该对象本身、被当做方法的参数而传递进来的对象、此方法所创建或实例化的任何对象、对象的任何组件
适配器模式和外观模式和装饰者模式的区别:
装饰者模式目的 ----> 不改变接口,但加入责任
适配者模式目的 ----> 将一个接口转成另一个接口
外观 ----> 让接口更简单
当需要使用一个现有的类而其接口并不符合你的需要时,就使用适配器模式。
当需要简化并统一一个很大的接口或者一群复杂的接口时,使用外观模式。
下次我将分享模板方法模式