• Java设计模式——适配器模式


    引言

      我们总是在一件事情上有一些为难。一个比较经典的案例就是,比如我有一个双孔插座,但是我的电脑是三脚的插头,我怎么把这个三脚插头插进这个双孔插座里面去?

      对于一个DotAer来说,我在想能不能让一个近卫军团英雄去做天灾军团的事情呢?这些情况就可以使用本文要说的适配器模式来解决。下面看看我是怎么做的吧。


    版权说明

    著作权归作者所有。
    商业转载请联系作者获得授权,非商业转载请注明出处。
    作者:Coding-Naga
    发表日期: 2016年1月5日
    链接:http://blog.csdn.net/lemon_tree12138/article/details/50326851
    来源:CSDN
    更多内容:分类 >> 设计模式


    定义

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


    举例背景

      现在假设我们在玩一盘DotA。我们知道在DotA里面,有天灾军团和近卫军团。

      对于天灾军团的使命就是保护冰封王座,攻击世界之树;而近卫军团的使用则是攻击冰封王座,保护世界之树。

      天灾军团里面我们选取UG,近卫军团里面我们选取小黑。

      一盘DotA就这样开始了。。。


    类图


    图-1 适配器模式类图


    类图分析

      现在我们现在分析一下这张类图。从类图中我们可以看到,Traxex和Mercurial都是简单地实现了各自阵营的接口。这个没什么好说的。不过,仔细看一下DisguiserAdapter这个适配器类,我们可以看到这个类持有一个近卫军团的对象,但是,肩负了一些天灾军团的使命。这一点是适配模式的关键。很好理解,如果说我这个天灾军团的适配器,持有了一近卫军团的英雄,还是干了近卫军团的事情(注:这里是指公有方法中的显式调用,而不是这个近卫军团的英雄真是肩负天灾军团的使命。),那我要这个适配器干嘛呢?详细说明,在下面的代码分析再作分析。


    代码实现

    近卫军团接口(KonoeHero)

      这个接口没什么太多需要说明的东西在里面。就是定义了两个使命,需要它的实现类们进行实现。天灾军团的接口也是一样的。

    public interface KonoeHero {
    
        /**
         * 攻击冰封王座
         */
        public void attackFrozenThrone();
        
        /**
         * 保护世界之树
         */
        public void protectWorldTree();
    }

    近卫军团接口实现(Traxex)

      这里拿小黑来举例,她实现了近卫军团的接口。需要实现近卫军团的接口方法。

    public class Traxex implements KonoeHero {
    
        @Override
        public void attackFrozenThrone() {
            System.out.println("我是" + Traxex.class.getSimpleName() + ",我在攻击冰封王座。");
        }
    
        @Override
        public void protectWorldTree() {
            System.out.println("我是" + Traxex.class.getSimpleName() + ",我在保护世界之树。");
        }
    
    }

    适配器实现(DisguiserAdapter)

      这里我们以天灾军团的适配器为例说明(当然,以哪个阵营为例说明都一样,这个不用说)。适配里面持有一个近卫军团的对象,因为是近卫军团的对象,所以这个对象只会履行近卫军团的使命,那就是攻击冰封王座,保护世界之树。

      说到这里你不是会有疑问,怎么这里还是自己本身阵营的使命?要是这样,我干嘛还要使用这个适配器模式,直接拿近卫军团的英雄不就好了么?关于这一点我们先留一个悬念,后面会说到。

    public class DisguiserAdapter implements DisasterHero {
    
        // 持有一个近卫军团的对象
        private KonoeHero hero;
        
        public DisguiserAdapter(KonoeHero _hero) {
            hero = _hero;
        }
        
        @Override
        public void protectFrozenThrone() {
            hero.attackFrozenThrone();
        }
    
        @Override
        public void attackWorldTree() {
            hero.protectWorldTree();
        }
    
    }
      在上面我们可以看到,这个近卫军团的英雄对象,被一个看上去像天灾军团使命的方法调用,而实际上却干的是近卫军团的事情。现在来解答一下刚才的问题,为什么这里的适配器本质上还是履行自己本身阵营的使命?我们说,如果你的插座是提供220V的交流电,而我们的计算机需要的是20V的。我们使用了一个适配器进行适配,好让220V的交流电可以正常提供给计算机。这里的计算机实际上还是在被一个20V的电压进行直接供电,只是看上去像是被一个220V的电压供电了一样。

    适配器实现的改进(DisguiserAdapter2)

      好了,到了这里适配器模式,基本上就是这样了。不过,在上面适配器的构造函数中却传入了一个近卫军团的对象。这一点有一些让人看上去逻辑混乱。下面是采用了匿名类的方式进行了改进,让我们真正的感觉好像是使用了一个天灾军团的英雄,做了近卫军团的事情一样。

    public class DisguiserAdapter2 implements DisasterHero {
    
        // 持有一个近卫军团的对象
        private KonoeHero hero;
        
        public DisguiserAdapter2() {
            initEvent();
        }
        
        private void initEvent() {
            if (hero == null) {
                hero = new KonoeHero() {
                    
                    @Override
                    public void protectWorldTree() {
                        System.out.println("我是近卫军,我在攻击冰封王座。");
                    }
                    
                    @Override
                    public void attackFrozenThrone() {
                        System.out.println("我是近卫军,我在保护世界之树。");
                    }
                };
            }
        }
        
        @Override
        public void protectFrozenThrone() {
            hero.attackFrozenThrone();
        }
    
        @Override
        public void attackWorldTree() {
            hero.protectWorldTree();
        }
    }

    测试类

    public class WorldOfWarcraft {
    
        public static void main(String[] args) {
            
            System.out.println("
    -------------------- 近卫军团的任务 --------------------");
            KonoeHero konoeHero = new Traxex();
            konoeHero.attackFrozenThrone();
            konoeHero.protectWorldTree();
            
            System.out.println("
    -------------------- 天灾军团的任务 --------------------");
            DisasterHero disasterHero = new Mercurial();
            disasterHero.protectFrozenThrone();
            disasterHero.attackWorldTree();
            
            System.out.println("
    -------------------- 适配器做了这样一件事 --------------------");
            DisguiserAdapter adapter = new DisguiserAdapter(new Traxex());
            adapter.attackWorldTree();
            adapter.protectFrozenThrone();
            
            System.out.println("
    -------------------- 适配器做了这样一件事 --------------------");
            DisguiserAdapter2 adapter2 = new DisguiserAdapter2();
            adapter2.attackWorldTree();
            adapter2.protectFrozenThrone();
        }
    }

    测试结果

    -------------------- 近卫军团的任务 --------------------
    我是Traxex,我在攻击冰封王座。
    我是Traxex,我在保护世界之树。
    
    -------------------- 天灾军团的任务 --------------------
    我是Mercurial,我在保护冰封王座。
    我是Mercurial,我在攻击世界之树。
    
    -------------------- 适配器做了这样一件事 --------------------
    我是Traxex,我在保护世界之树。
    我是Traxex,我在攻击冰封王座。
    
    -------------------- 适配器做了这样一件事 --------------------
    我是近卫军,我在攻击冰封王座。
    我是近卫军,我在保护世界之树。

    GitHub源码下载

    https://github.com/William-Hai/DesignPatternCollections

  • 相关阅读:
    MySQL之索引优化
    使用Nginx+Lua(OpenResty)开发高性能Web应用
    Eclipse设置背景色
    删除排序数组中的重复项再练习
    计数排序_数组与集合时间比较
    nodejs+redis应用
    redis的一些优缺点
    Redis的线程模型
    GC仅仅是守护线程,空闲执行
    SpringIOC和AOP的生活案例
  • 原文地址:https://www.cnblogs.com/fengju/p/6336020.html
Copyright © 2020-2023  润新知