zoukankan      html  css  js  c++  java
  • 浅析设计模式(七)——适配器模式

    适配器模式(Adapter,结构型模式)

    本文的结构

    一、适配器模式的定义

      定义:将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

      简单点理解:转换接口,进行适配。

      从适配的方式来看,有2种可行的方式,即【类的适配器】和【对象的适配器】,换个说法就是【继承(接口实现)】和【组合】,两者的效果如下

    继承(接口实现,is-a):

    • 用一个具体的Adapter类对Adaptee和Target进行匹配,但无法匹配Adaptee的子类
    • 作为Adaptee的子类,Adapter可重新定义Adaptee的部分行为

    组合(has-a)

    • 允许一个Adapter与多个Adaptee(其本身或者所有子类)一起工作
    • 重新定义Adaptee的行为会比较困难,需要生成Adaptee的子类并且使得Adapter引用这个子类。

      Java是单继承(类),多实现(接口)。在使用时,需要根据实际情况进行考虑。比如说,Adaptee和Target都是类,无法多重继承,就只能使用对象的适配器;如果需要使用到一群子类,使用对象的适配器进行组合(子类的父接口)会更加合适;如果仅仅是适配一个第三方接口,可以使用类的适配器进行适配,也可以使用对象的适配器进行适配。

      举个不恰当的例子:spring工程中常见的service,也会调用到其他的service,可以看成是最简单粗暴的一种代理。。。

    二、适配器模式的参与者及其角色

    1. Target

    • 定义Client使用的与特定领域相关的接口,即目标接口

    2. Client

    • 与符合Target接口的对象协同。

    3. Adaptee

    • 定义一个已经存在的接口,这个接口需要适配

    4. Adapter

    • 对Adaptee的接口与Target接口进行适配

    三、适配器模式的类图

    类的适配器模式:

    对象的适配器模式:

    四、适配器模式的示例

       下面使用对象的适配器进行简单地说明。

      这里有鸭子类和火鸡类,可以通过适配器将鸭子适配(伪装)成火鸡进行调用,反过来也可以将火鸡适配(伪装)成鸭子进行调用。

    1. Target 和 3. Adaptee

      鸭子类和火鸡类是需要相互进行适配的类。

    鸭子及其实现类:

    // 鸭子接口
    public interface Duck {
        public void quack();
        public void fly();
    }
    
    // 鸭子实现类
    public class MallardDuck implements Duck {
        @Override
        public void quack() {
            System.out.println("Quack");
        }
    
        @Override
        public void fly() {
            System.out.println("I'm flying");
        }
    }

    火鸡及其实现类

     1 // 火鸡接口
     2 public interface Turkey {
     3     public void gobble();
     4     public void fly();
     5 }
     6 
     7 // 实现类
     8 public class WildTurkey implements Turkey {
     9     @Override
    10     public void gobble() {
    11         System.out.println("Gobble gobble");
    12     }
    13 
    14     @Override
    15     public void fly() {
    16         System.out.println("I'm flying a short distance");
    17     }
    18 }

     2. Client

      这里看成是正常的客户端调用,可以看到将火鸡Turkey进行适配包装后,可以和调用鸭子Duck一样调用适配后的火鸡。

    鸭子Duck

     1 public class DuckTestDrive {
     2     public static void main(String[] args) {
     3         Duck duck = new MallardDuck();
     4 
     5         Turkey turkey = new WildTurkey();
     6         Duck turkeyAdapter = new TurkeyAdapter(turkey);
     7 
     8         System.out.println("The Turkey says...");
     9         turkey.gobble();
    10         turkey.fly();
    11 
    12         System.out.println("
    The Duck says...");
    13         testDuck(duck);
    14 
    15         System.out.println("
    The TurkeyAdapter says...");
    16         testDuck(turkeyAdapter);
    17     }
    18 
    19     static void testDuck(Duck duck) {
    20         duck.quack();
    21         duck.fly();
    22     }
    23 }

    火鸡Turkey

    public class TurkeyTestDrive {
        public static void main(String[] args) {
            Duck duck = new MallardDuck();
            Turkey duckAdapter = new DuckAdapter(duck);
     
            for(int i=0;i<10;i++) {
                System.out.println("The DuckAdapter says...");
                duckAdapter.gobble();
                duckAdapter.fly();
            }
        }
    }

    4. Adapter

      火鸡适配成鸭子:

     1 public class TurkeyAdapter implements Duck {
     2     Turkey turkey;
     3 
     4     public TurkeyAdapter(Turkey turkey) {
     5         this.turkey = turkey;
     6     }
     7 
     8     public void setTurkey(Turkey turkey) {
     9         this.turkey = turkey;
    10     }
    11 
    12     @Override
    13     public void quack() {
    14         turkey.gobble();
    15     }
    16 
    17     @Override
    18     public void fly() {
    19         for (int i = 0; i < 5; i++) {
    20             turkey.fly();
    21         }
    22     }
    23 }

      鸭子适配成火鸡:

     1 public class DuckAdapter implements Turkey {
     2     Duck duck;
     3     Random rand;
     4 
     5     public DuckAdapter(Duck duck) {
     6         this.duck = duck;
     7         rand = new Random();
     8     }
     9 
    10     public void setDuck(Duck duck) {
    11         this.duck = duck;
    12     }
    13 
    14     @Override
    15     public void gobble() {
    16         duck.quack();
    17     }
    18 
    19     @Override
    20     public void fly() {
    21         if (rand.nextInt(5) == 0) {
    22             duck.fly();
    23         }
    24     }
    25 }

      由此可以看到接口的适配和转换。

    五、参考

    1、参考《Head First设计模式》和GoF《设计模式:可复用面向对象软件的基础》

    2、代码可参考【github传送门】、UML类图参考【github传送门

  • 相关阅读:
    SQLSERVER2008数据库增量备份还原方式
    使用VS2003遇到“无法显示进程。没有正确安装调试器。请运行安装程序安装或修复调试器。”的解决方法
    IIS7下配置最大上传附件大小需要注意的事项
    运行常用指令
    跨库查询推荐使用的方法
    获取客户端IP需要注意的一个问题
    如何判断一个表是否建立索引约束等信息的SQL语句
    SQLServer2005重建索引前后对比
    一个鼠标滚轮控制大小的缩放类。
    全兼容的纯CSS级联菜单
  • 原文地址:https://www.cnblogs.com/wpbxin/p/9827532.html
Copyright © 2011-2022 走看看