当前位置 博文首页 > 韩超的博客 (hanchao5272):设计模式-适配器模式-以电压适配器为

    韩超的博客 (hanchao5272):设计模式-适配器模式-以电压适配器为

    作者:[db:作者] 时间:2021-09-05 16:14

    超级链接: Java常用设计模式的实例学习系列-绪论

    参考:《HeadFirst设计模式》


    1.关于适配器模式

    适配器模式是一种结构型模式。

    适配器模式:将一个类的接口转换成客户期望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

    下面以一张经典的图来描述适配器模式:

    在这里插入图片描述

    本文以电压适配器为场景来学习适配器模式

    • 手机充Phone电需要的电压是5V的。
    • 充电宝PortableBatteryPower提供的电源是5V的,所以手机可以直接通过充电宝进行充电。
    • 家用电源HouseholdPower提供的电压是220V的,所有手机不能直接通过家用电源进行充电。
    • 通过电压适配器,可以将家用电源的220V电源转换为手机充电可用的5V电源。

    2.手机与充电宝

    假定现在有充电宝,则可以直接进行手机充电。

    分析上面的场景,其实手机充电依赖的不仅仅是充电宝,而是依赖的一种5V的电源。

    所以,首先,我们定义一个5V的电源,并以充电宝作为一种实现。

    5V电源:接口:V5Power

    /**
     * <p>5V的电源</P>
     *
     * @author hanchao
     */
    public interface V5Power {
        /**
         * 获取名称
         */
        String getName();
    
        /**
         * 获取电压
         */
        Integer getVoltage();
    }
    

    5V电源:实现:充电宝:PortableBatteryPower

    /**
     * <p>充电宝</P>
     *
     * @author hanchao
     */
    public class PortableBatteryPower implements V5Power {
    
        @Override
        public String getName() {
            return "充电宝";
        }
    
        @Override
        public Integer getVoltage() {
            return 5;
        }
    }
    

    然后,定义一个手机Phone,它具有充电功能batteryCharge(),其能接受的参数是V5Power类型的。

    /**
     * <p>手机</P>
     *
     * @author hanchao
     */
    @Slf4j
    public class Phone {
        /**
         * 充电
         */
        public void batteryCharge(V5Power v5Power) {
            log.info("1.连接电源 ==> " + v5Power.getName());
            log.info("2.当前电压 : " + v5Power.getVoltage());
            log.info("3.进行充电 ... ");
        }
    }
    

    最后,我们写一段测试代码:AdapterDemo

        public static void main(String[] args) {
            //首先,需要一部手机
            Phone phone = new Phone();
    
            log.info("如果有充电宝,则直接用它充电");
            V5Power v5Power = new PortableBatteryPower();
            phone.batteryCharge(v5Power);
        }
    

    运行结果

    2019-07-09 12:44:03,556  INFO [main-1] pers.hanchao.designpattern.adapter.AdapterDemo:22 - 如果有充电宝,则直接用它充电 
    2019-07-09 12:44:03,560  INFO [main-1] pers.hanchao.designpattern.adapter.Phone:19 - 1.连接电源 ==> 充电宝 
    2019-07-09 12:44:03,560  INFO [main-1] pers.hanchao.designpattern.adapter.Phone:20 - 2.当前电压 : 5 
    2019-07-09 12:44:03,560  INFO [main-1] pers.hanchao.designpattern.adapter.Phone:21 - 3.进行充电 ... 
    

    3.手机与家用电源

    假定现在只有家用电源,则不能直接进行手机充电。

    分析上面的场景,其实手机不仅仅不能通过家用电源直接充电,所有220V的电源都不能直接用于手机充电。

    所以,我们定义一个220V的电源,并以家用电源作为一种实现。

    220V电源:接口:V220Power

    /**
     * <p>220V的电源</P>
     *
     * @author hanchao
     */
    public interface V220Power {
        /**
         * 获取名称
         */
        String getName();
    
        /**
         * 获取电压
         */
        Integer getVoltage();
    }
    

    220V电源:实现:家用电源:HouseholdPower

    /**
     * <p>家用电源</P>
     *
     * @author hanchao
     */
    public class HouseholdPower implements V220Power {
    
        @Override
        public String getName() {
            return "家用电源";
        }
    
        @Override
        public Integer getVoltage() {
            return 220;
        }
    }
    

    因为手机Phone的充电方法batteryCharge(V5Power v5Power)参数是V5Power类型的,肯定无法直接传入V220Power类型的参数。

    4.适配器

    此时,我们需要实现一个适配器Adapter,这个适配器应该实现以下两个功能:

    • 它的类型应是V5Power的,如此才能作为batteryCharge()方法的参数。所以有:Adapter implements V5Power
    • 它应包含V220Power的对象,如此才能实现最初的目的:使用V220的电源。所以有成员变量:V220Power v220Power

    上面所描述的,其实就是开始的示例图中的那句话:适配器本身包含接口乙,并实现了接口甲。

    根据这个思路,很容易实现适配器代码。

    适配器:V220=>V5:V220ToV5Adapter

    /**
     * <p>USB接口转换器</P>
     * <p>
     * 1.实现适配目标对象V5Power
     *
     * @author hanchao
     */
    @Slf4j
    public class V220ToV5Adapter implements V5Power {
        /**
         * 2.以组合的方式持有被适配对象v220Power
         */
        private V220Power v220Power;
    
        /**
         * 3.在构造时初始化被适配对象v220Power
         */
        public V220ToV5Adapter(V220Power v220Power) {
            this.v220Power = v220Power;
        }
    
        /**
         * 重写V5Power的方法,完成V220到V5的转换过程
         */
        @Override
        public String getName() {
            return " 电源适配器 ==> " + v220Power.getName();
        }
    
        /**
         * 重写V5Power的方法,完成V220到V5的转换过程
         */
        @Override
        public Integer getVoltage() {
            log.info("原始电压为:" + v220Power.getVoltage());
            log.info("进行电压转换:" + v220Power.getVoltage() + " --> " + 5);
            log.info("转换之后的电源为:5");
            return 5;
        }
    }
    

    测试代码:AdapterDemo

        public static void main(String[] args) {
            //首先,需要一部手机
            Phone phone = new Phone();
            log.info("如果只有家用电源,则需要通过电源适配器进行转换");
            V220Power v220Power = new HouseholdPower();
            v5Power = new V220ToV5Adapter(v220Power);
            phone.batteryCharge(v5Power);
        }
    

    测试结果

    2019-07-09 13:01:44,981  INFO [main-1] pers.hanchao.designpattern.adapter.AdapterDemo:26 - 如果只有家用电源,则需要通过电源适配器进行转换 
    2019-07-09 13:01:44,985  INFO [main-1] pers.hanchao.designpattern.adapter.Phone:18 - 1.连接电源 ==>  电源适配器 ==> 家用电源 
    2019-07-09 13:01:44,985  INFO [main-1] pers.hanchao.designpattern.adapter.adapter.V220ToV5Adapter:41 - 原始电压为:220 
    2019-07-09 13:01:44,986  INFO [main-1] pers.hanchao.designpattern.adapter.adapter.V220ToV5Adapter:42 - 进行电压转换:220 --> 5 
    2019-07-09 13:01:44,986  INFO [main-1] pers.hanchao.designpattern.adapter.adapter.V220ToV5Adapter:43 - 转换之后的电源为:5 
    2019-07-09 13:01:44,986  INFO [main-1] pers.hanchao.designpattern.adapter.Phone:19 - 2.当前电压 : 5 
    2019-07-09 13:01:44,986  INFO [main-1] pers.hanchao.designpattern.adapter.Phone:20 - 3.进行充电 ...
    

    5.总结

    最后以UML类图来总结本文的电压适配器场景以及适配器模式。

    在这里插入图片描述

    适配器模式遵循了一条很重要的OOP原则,那就是:多用组合,少用继承

    适配器模式,能够有效的解决接口不兼容问题。

    适配器模式的优点:

    • 解耦:将不兼容接口客户端解耦,两者都不必修改原有代码。
    • 灵活性高、扩展性好:如果当前适配器不适用,重新编写一个新的适配器即可。
    • 增加代码复用性高:接口本来不能复用,在适配之后,则可以被复用。
    cs