一、适配器模式概述
适配器模式的定义-意图:将一个类的接口转换成客户希望的另一个接口。适配器模式让那些接口不兼容的类可以一起工作。(对象结构模式->对象适配器/类结构模式->类适配器)
适配器模式包含三个角色:目标(Target)角色、适配者(Adaptee)角色和适配器(Adapter)角色。根据Adapter角色的实现不同,可分为类适配器和对象适配器。
- 类适配器模式(继承也是泛化实现):适配器角色对于被适配角色的适配是通过继承完成的还是通过组合来完成的。通过继承类适配者类(Adaptee Class)实现的,另外类适配器实现客户类所需要的接口。当客户对象调用适配器类方法的时候,适配器内部调用它所继承的适配者的方法。
- 对象适配器模式(组合实现):适配器角色对于被适配角色的适配是通过组合来完成的。包含一个适配器者的引用(reference),与类适配器相同,对象适配器也实现了对客户类需要用接口。当客户对象调用对象适配器的方法的时候,对象适配器调它所包含的适配器者实例的适当方法。
- 适配器模式的优缺点
- 优点:
- 1.将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构;
- 2.增加了类的透明性和复用性,提高了适配者的复用性,同一个适配者类可以在多个不同的体统中复用;
- 3.灵活性和扩展性非常好;
- 4.类适配器模式:置换一些适配者的发放很方便;
- 5.对象适配器模式:可以把多个不同的适配者适配到同一个目标,还可以适配一个适配者的子类。
- 缺点:
- 1.类适配器模式:
- ①一次最多只能适配一个适配者类,不能同时适配多个适配者;
- ②适配者类不能为最终类(不能为final);
- ③目标抽象类只能为接口,不能为类。
- 2.对象适配器模式:在适配器中置换适配者类的某些方法比较麻烦。
- 1.类适配器模式:
- 优点:
- 适用环境:
- 1.系统需要使用一些现有的类,而这些类的接口不符合系统的需要,甚至没有这些类的源代码;
- 2.创建一个可以重复使用的类,用于和一些彼此之间没有太大关联的类,包括一些可能在将来引进的类一起工作。
二、缺省适配器
缺省适配器模式(Defualt Adapter Pattern)的定义:当不需要实现一个接口所提供的所有方法时,可先设计一个抽象类实现该接口,并为借口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可以选择性地覆盖父类的某些方法来实现需求,它适用于不想使用一个接口中的所有方法的情况,又成为单接口适配器模式。
结构:
- 适配者接口(ServiceInterface)
- 缺省适配器类(AbstractServiceClass)
- 具体业务类(ConcreteServiceClass)。
三、双向适配器
双向适配器定义:在对象适配器的使用过程,如果在适配器中同时包含对目标类和适配者类的引用,适配者可以通过对它调用目标类中的方法,目标类也可以通过它调用适配者类中的方法,那么该适配器就是一个双向适配器。
结构:
- 目标(Target)角色
- 适配者(Adaptee)角色
- 适配器(Adapter)角色。
四、代码实现
适配器模式的结构和实现:
- 1.目标(Target)角色:这是客户所期待的接口。因为Java不支持多继承,所以Target必须是接口,不可以是类。
- 2.适配者(Adaptee)角色:需要适配的类。
- 3.适配器(Adapter)角色:把源接口转换成目标接口。
4.1 对象适配器
某公司欲开发一款儿童玩具汽车,为了更好地吸引小朋友的注意力,该玩具汽车在移动过程中伴随着灯光闪烁和声音提示。在该公司以往的产品中已经实现了控制灯光闪烁(例如警灯闪烁)和声音提示(例如警笛音效)的程序,为了重用先前的代码并且使得汽车控制软件具有更好的灵活性和扩展性,现使用适配器模式设计该玩具汽车控制软件。
4.1.1 目标(Target)角色(CarController:汽车控制类,充当目标抽象类)
package Adapter.car;
//汽车控制类,充当目标抽象类
public abstract class CarController {
public void move() {
System.out.println("车开始移动!");
}
public abstract void phonate();//发出声音
public abstract void twinkle();//灯光闪烁
}
4.1.2 适配者(Adaptee)角色(PoliceSound:警笛类,充当适配者;PoliceLamp:警灯类,充当适配者)
package Adapter.car;
//警车声音类,充当适配器
public class PoliceSound {
public void alarmSound() {
System.out.println("发出警车声音!");
}
}
package Adapter.car;
//警车灯类,充当适配者
public class PoliceLamp {
public void alarmLamp() {
System.out.println("呈现警车灯闪烁!");
}
}
4.2.3 适配器(Adapter)角色(PoliceCarAdapter:警车适配器,充当适配器)
package Adapter.car;
//适配器角色
public class PoliceCarAdapter extends CarController{
//定义适配者PoliceSound对象
private PoliceSound sound;
//定义适配者PoliceLamp对象
private PoliceLamp lamp;
public PoliceCarAdapter() {
sound=new PoliceSound();
lamp=new PoliceLamp();
}
@Override
public void phonate() {
// TODO 自动生成的方法存根
sound.alarmSound();
}
@Override
public void twinkle() {
// TODO 自动生成的方法存根
lamp.alarmLamp();
}
}
4.2.4 main方法实现对象适配器模式
package Adapter.car;
public class Test {
public static void main(String[] args) {
// TODO 自动生成的方法存根
CarController cc;//针对抽象目标编程
//cc =new AmbulanceCarAdapter();//救护车适配器
//cc.move();
//cc.twinkle();
//cc.phonate();
System.out.println("----------------");
cc=new PoliceCarAdapter();//警车适配器
cc.move();
cc.twinkle();
cc.phonate();
}
}
4.1.5 UML图
4.2 缺省适配器
本科生毕业条件:1.本科生必须读完读四年修满所有的学分后,参加论文答辩后即可毕业;2.学校采用的弹性学分制,只要修满所有的学分,可以提前安排答辩早毕业。
要求:分别实现以下三种情况的学生类,并编写测试类运行。
- 1)普通的就读四年的全日制大学生 GeneralStudent
- 2)读了3年提前修满学分特殊的大学生SpecialStudent
- 3)读到大二就退学的大学生DiscontinueStudyStudent
4.2.1 目标(Target)角色(Study:学分接口,充当目标接口)
package Adapter.defualtAdapter.Student;
//目标Target角色
public interface Study {
//四个学期和论文答辩
public void StudyGrade1();
public void StudyGrade2();
public void StudyGrade3();
public void StudyGrade4();
public void TheisPresentation();
}
4.2.2 适配者(Adaptee)角色(GeneralStudent、SpecialStudent、DiscountinueStudyStudent:充当适配者)
package Adapter.defualtAdapter.Student;
//Adaptee普通全日制大学生
public class GeneralStudent extends StudyAdapter{
public GeneralStudent(String name) {
super(name);
}
public void StudyGrade1(){
System.out.println("读完大学一年级");
}
public void StudyGrade2(){
System.out.println("读完大学二年级");
}
public void StudyGrade3(){
System.out.println("读完大学三年级");
}
public void StudyGrade4(){
System.out.println("读完大学四年级");
}
public void TheisPresentation() {
super.TheisPresentation();
System.out.println("修满了学分,参加论文答辩,成功毕业!");
}
}
package Adapter.defualtAdapter.Student;
//Adaptee跳级的大学生
public class SpecialStudent extends StudyAdapter{
public SpecialStudent(String name) {
super(name);
}
public void StudyGrade1(){
System.out.println("读完大学一年级");
}
public void StudyGrade2(){
System.out.println("读完大学二年级");
}
public void TheisPresentation() {
super.TheisPresentation();
System.out.println("修满了学分,参加论文答辩,成功毕业!");
}
}
package Adapter.defualtAdapter.Student;
//Adaptee辍学的大学生
public class DiscountinueStudyStudent extends StudyAdapter{
public DiscountinueStudyStudent(String name) {
super(name);
}
public void StudyGrade1(){
System.out.println("读完大学一年级");
}
public void TheisPresentation() {
super.TheisPresentation();
System.out.println("中途辍学!");
}
}
4.2.3 适配器(Adapter)角色(StudyAdapter:学分适配器,充当适配器)
package Adapter.defualtAdapter.Student;
//适配器Adapter
public abstract class StudyAdapter implements Study{
String name;
public StudyAdapter(String name) {
this.name=name;
}
public void StudyGrade1(){}
public void StudyGrade2(){}
public void StudyGrade3(){}
public void StudyGrade4(){}
public void TheisPresentation() {
System.out.println("我是"+name);
StudyGrade1();
StudyGrade2();
StudyGrade3();
StudyGrade4();
}
}
4.2.4 main方法实现缺省适配器模式
package Adapter.defualtAdapter.Student;
public class Test {
public static void main(String[] args) {
// TODO 自动生成的方法存根
StudyAdapter s= new GeneralStudent("Emily");;//针对抽象目标接口编程
s.TheisPresentation();
System.out.println("-----------------------");
StudyAdapter s1= new SpecialStudent("zz");;//针对抽象目标接口编程
s1.TheisPresentation();
System.out.println("-----------------------");
StudyAdapter s2= new DiscountinueStudyStudent("xx");;//针对抽象目标接口编程
s2.TheisPresentation();
}
}
4.2.5 UML图
4.3 双向适配器
- 1.灰太狼临死前他将自己的抓羊秘技传给了已经成年的小灰灰,并留下一件狼皮和一件羊皮。小灰灰也承担起了抓羊的重任;
- 2.小灰灰和羊相爱了,但是在梁山上,狼只能一直披着羊皮生活;
- 3.小灰灰带媳妇回去看妈妈。
他们改造成了一个新型的法宝——狼羊双向适配器。把羊皮和狼皮缝在一起,正面羊皮,翻过来后就是狼皮,这样一件衣服,既能扮狼又能扮羊。
4.3.1 目标(Target)角色(ISheep、IWolf:充当目标接口)
package Adapter.doubleAdapter.wolfsheep;
//目标角色
public interface ISheep {
public void sheepLooks();
public void eatGrass();
}
package Adapter.doubleAdapter.wolfsheep;
//目标角色
public interface IWolf {
public void wolfLooks();
public void eatMeat();
}
4.3.2 适配者(Adaptee)角色(Sheep、Wolf:充当适配者)
package Adapter.doubleAdapter.wolfsheep;
//适配者
public class Sheep implements ISheep {
@Override
public void sheepLooks() {
System.out.println("I am a sheep,too!");
}
@Override
public void eatGrass() {
System.out.println("I eat grass!");
}
}
package Adapter.doubleAdapter.wolfsheep;
//适配者
public class Wolf implements IWolf {
@Override
public void wolfLooks() {
System.out.println("I am a wolf,too!");
}
@Override
public void eatMeat() {
System.out.println("I eat meat!");
}
}
4.3.3 适配器(Adapter)角色(TwoFaceAdapter:充当适配器)
package Adapter.doubleAdapter.wolfsheep;
//适配器
public class TwoFaceAdapter implements ISheep, IWolf {
ISheep sheep =null;
IWolf wolf =null;
public TwoFaceAdapter(ISheep sheep) {
this.sheep=sheep;
}
public TwoFaceAdapter(IWolf wolf) {
this.wolf=wolf;
}
@Override
public void wolfLooks() {
System.out.println("I am a wolf,too!");
}
@Override
public void eatMeat() {
// TODO 自动生成的方法存根
sheep.eatGrass();
}
@Override
public void sheepLooks() {
// TODO 自动生成的方法存根
System.out.println("I am a sheep,too!");
}
@Override
public void eatGrass() {
// TODO 自动生成的方法存根
wolf.eatMeat();
}
}
4.3.4 main方法实现双向适配器模式
package Adapter.doubleAdapter.wolfsheep;
public class Test {
public static void main(String[] args) {
// TODO 自动生成的方法存根
//把羊设配成狼
ISheep fakersheep = new TwoFaceAdapter(new Wolf());
fakersheep.sheepLooks();
fakersheep.eatGrass();
//把狼设配成羊
IWolf fakerwolf = new TwoFaceAdapter(new Sheep());
fakerwolf.wolfLooks();
fakerwolf.eatMeat();
}
}