Bridge mode coding

现在我们来学习桥接模式,这个包建在结构这个包下面,bridge,那桥接模式就是把抽象和实现,分离出来,

然后中间通过组合,来搭建他们之间的桥梁,那我们现在有一个场景,例如中国有很多银行,有中国农业银行,

还有工商银行,那这两个银行也比较好记,一个叫ABC Bank,还有叫ICBC Bank,我们就有自己的账号,那关于我们的

储蓄账号呢,分为定期账号,还有活期账号,定义账号可以存三个月,半年一年,银行也有一定的利息,而这种利息是比

活期账号利息高很多的,那我们现在抽象的来想一下,那这里面就分两大块,一块是银行,另外一块就是我们的账号,

首先我们来创建一个类,这个类是一个接口,为什么要用接口呢,创建完这个接口之后,我们还要具体写他的实现,这个接口

就是Account账号

一旦你看到这个图,一定会很清晰的,这些都打开,那我们一起来看一下这个图,我们一起来回顾一下,首先我们创建了

Account,这个是什么呢,他是这个桥的实现接口,左侧是实现层,右侧是抽象层,所以桥接模式最重要的是把桥的左侧

和桥的右侧,你要桥接的两侧,划分清楚,Account可以理解成,他就是这个桥的实现接口,而下边这两个Account,一个

是定期账号,一个是活期账号,他们两个就是具体的实现类,他们来实现Account接口,然后注意,第三步我们创建了Bank,

创建了一个抽象类Bank,用的是什么呢,使用了Account这个接口,这个就是桥接模式的核心,注意这个抽象类使用了Account

接口,通过什么方式呢,通过组合的方式,所以这条线右侧有一个菱形,一个Bank里面含有一个Account,那下边的ICBCBank,

还有ABCBank,他们就是具体继承了Bank,这个抽象类具体的类实现,那我们通过这个桥接模式,把实现部分Account,和抽象

部分Bank,进行一个桥接,那当然我们说的实现部分,是指Account的具体的接口实现类,而抽象部分,就是指Bank这个抽象类,

而中间的这个横线呢,也就是桥接模式的一个关键,连接两个继承体系的一个桥,当然我们这里面是组合,也有通过聚合方式

实现桥接的,如果我们不用这种方式,会怎么样呢,打个比方,我们首先写一个Bank,然后这两个银行爱存不存,还有ABCBank,

然后我们在ICBCBank下边,再创建两个账号,一个是SavingAccount,另外一个是DepositAccount,同理呢ABCBank下边,也会有

这两个账号,那这导致了一个什么问题,如果我们银行很多,还有账号的类型很多的话,这个子类就要爆炸了,下边会有无数个的

类,那前面我们也有说,桥接模式在一定程度上,可以避免子类过多,子类剧增,子类爆炸的情况,那如果我们用刚刚我们说的方式,

就是通过最简单的一个继承的方案,所以桥接模式通过组合的方式,通过复合复用原则,来达到抽象层,和实现层,他们两之间进行

分离,并且最重要的一点,他们两可以独立的发展,再创建一个银行,在这里面创建银行,继承Bank就可以,在这里面创建一个账号,

继承Account就可以,他们两之间就可以无限组合,那这个就桥接模式的精华所在,希望你们能够深入理解一下,在这个模式当中,

使用组合优于继承,这个效果是非常非常明显的,那他到底有多方便呢,很简单,我们来写一下Test

注意中间是一个桥梁,最大的特色是连接两个继承体现中间的那座桥,也就是我们看到的一比一的关系,

左边是抽象部分,右边是实现部分,抽象部分可以独立的扩展自己,实现部分也可以独立的扩展自己,可以加

无数个银行,而账号这边呢,可以加各种理财账号,各种各样的账号,至少不管他们之间如何排列组合,我们都不会

发生类爆炸的情况,刚才我们在前面也说了,如果用继承的方式来实现的话,会是什么样子,那希望通过我们的

coding实战,对桥接模式有更深刻的理解,还有千万不要忘记委托行为的小坑
package com.learn.design.pattern.structural.bridge;

/**
 * 在这里有两个方法
 * 
 * 
 * @author Leon.Sun
 *
 */
public interface Account {
	/**
	 * 一个呢是打开我们的账号
	 * 打开账号又分为打开哪个银行的账号呢
	 * 例如leon有工商银行的账号
	 * 还有中国农业银行的账号
	 * 所以这里也是一个抽象
	 * 那既然打开账号呢
	 * 就要返回账号
	 * openAccount这么一个方法
	 * 
	 * 
	 * @return
	 */
    Account openAccount();
    /**
     * 还有是查看我们账户的类型
     * 是活期储蓄
     * 还是定期储蓄
     * 还有一个showAccountType
     * 看一下这个账号的类型
     * 就现在的场景而言
     * 我们有中国农业银行
     * 中国工商银行
     * 这是两个银行
     * 还有有活期账号
     * 还有定期账号
     * 这个呢是两个不同类型的账号
     * 他们交叉一下
     * 就会有四种组合
     * 那一旦说我们的银行不断的扩展
     * 账号类型也不断地扩展的话
     * 那么对于桥接模式
     * 就再适合不过了
     * 他们两个都可以随着自己的层级
     * 进行扩展
     * 随着我们coding的时候呢
     * 希望对这一块的理解呢
     * 会加深
     * 首先呢我们创建一个定期账号
     * 来实现这个接口
     * DepositAccount
     * 
     * 
     */
    void showAccountType();

}
package com.learn.design.pattern.structural.bridge;

/**
 * 他来实现Account
 * 实现两个方法
 * 这个账号是一个定期账号
 * 我们再创建一个活期账号的类
 * 新建一个类
 * SavingAccount
 * DepositAccount是定期存款
 * 如果提前取出来的话需要交罚金的
 * 
 * 
 * 
 * @author Leon.Sun
 *
 */
public class DepositAccount implements Account {
	/**
	 * 写一下这个类的实现
	 * 
	 * 
	 */
    @Override
    public Account openAccount() {
    	/**
    	 * 首先输出
    	 * "打开定期账号"
    	 * 
    	 * 
    	 */
        System.out.println("打开定期账号");
        /**
         * 返回值直接new一个
         * 非常适合回来来看
         * 写完之后你们先体会
         * 回头来给大家讲
         * 因为我几年前在学模式的时候
         * 当时从上至下的来学习的时候
         * 理解的并不是很透彻
         * 但是一旦写完之后
         * 再回头来看
         * 一切都非常清晰明了
         * 我们边写也会边讲
         * 一起来体会
         * 我们再回头重点来讲
         * 那我们继续来coding
         * 那现在就得写我们的银行类
         * 创建一个类Bank
         * 
         * 
         */
        return new DepositAccount();
    }

    @Override
    public void showAccountType() {
    	/**
    	 * 这个是一个定期账号
    	 * 
    	 * 直接到这里
    	 * 这是一个定期账号
    	 * 那这个时候F8通过
    	 * 
    	 * 
    	 */
        System.out.println("这是一个定期账号");
    }
}
package com.learn.design.pattern.structural.bridge;

/**
 * 他也实现Account接口
 * 实现这两个方法
 * SavingAccount是随时可以取钱的那种
 * 
 * 
 * @author Leon.Sun
 *
 */
public class SavingAccount implements Account {
	/**
	 * 打开这个账号需要返回这个账号
	 * 
	 * 
	 */
    @Override
    public Account openAccount() {
    	/**
    	 * 所以我们输出一下打个标记
    	 * 
    	 * 
    	 */
        System.out.println("打开活期账号");
        //...
        /**
         * new一个SavingAccount
         * 
         * 
         */
        return new SavingAccount();
    }

    @Override
    public void showAccountType() {
    	/**
    	 * 直接输出"这是一个活期账号"
    	 * 
    	 * 
    	 */
        System.out.println("这是一个活期账号");
    }
}
package com.learn.design.pattern.structural.bridge;

/**
 * 这个银行类很简单
 * 他要写成一个抽象类
 * 为什么呢
 * 因为现在我们要把Account
 * 引入到Bank里边
 * 然后通过这种组合的方式
 * 交给子类来实现他的行为
 * 而这个行为就是openAccount
 * 至少我要确定
 * 打开的是哪个银行的账号
 * 那首先呢既然要交给子类
 * 
 * 
 * @author Leon.Sun
 *
 */
public abstract class Bank {
	/**
	 * protected
	 * 声明一个account
	 * 子类能够拿到Account
	 * 然后我来写一下他的构造器
	 * 
	 * 
	 */
    protected Account account;
    /**
     * 构造器我可以把account拿过来
     * 也可以通过set注入的方式
     * 
     * 然后在父类上也打一个断点
     * 
     * 所以就把这个account赋值给抽象类Bank
     * 里面组合的Account
     * F6单步
     * 可以看到这个时候已经有值了
     * 
     * 
     * @param account
     */
    public Bank(Account account){
    	/**
    	 * 然后赋值
    	 * 把这些断点去掉
    	 * 看一下结果
    	 * 那在使用桥接模式的时候
    	 * 有一个小坑
    	 * 这个小坑是什么呢
    	 * 例如我们现在打开中国工商银行账号
    	 * 然后直接显示了这是一个定期账号
    	 * 这个没有什么问题
    	 * 但是我们要注意
    	 * 我们委托之后
    	 * 到底有没有使用他呢
    	 * 我们看一下
    	 * 
    	 * 
    	 */
        this.account = account;
    }
    /**
     * 然后我们要写一个抽象方法
     * 那这个方法是什么呢
     * 正式Account接口的方法
     * 那一会就知道
     * 为什么我这个想抽象类里边
     * 还要写一个抽象方法
     * 而这个抽象方法
     * 和接口里面声明的
     * 抽象方法是一样的
     * 那这里面我也要强调一下
     * 声明成一样的方法名
     * 这个并不是强制的
     * 那我们声明成一样的方法名呢
     * 是为了你们更方便的理解
     * 因为Bank里面的具体的方法
     * 要委托给Account的openAccount
     * 这个方法
     * 所以就把它们声明成一样的方法名
     * 其实不叫一样的方法名也是OK的
     * 只不过这种委托关系
     * 在我们这个case里边
     * 逻辑比较简单
     * 并不会增强这个openAccount方法
     * 所以就直接委托给他
     * 他们两个的方法名是一样的
     * 里边的实现所表达的含义也是一样的
     * 然后这里边再说一下
     * 看到的Account类
     * 那在我们现在这个结构中
     * Account就是具体的实现
     * 因为我们要通过Account的接口实现
     * Bank就是抽象
     * 然后抽象类里面的某个行为
     * 委托给Account这个接口
     * 那前面我们也有说
     * 桥接模式是指抽象和实现分离
     * 那实现是什么呢
     * 实现正式Account的两个实现类
     * 那Account是有名词的含义
     * 那还有动词
     * 动词就是报账 查账
     * 我们可以认为Account是一个行为接口
     * 里面是两个动作
     * 一个是open
     * 一个是show
     * 打开账号
     * 还是查看账号类型呢
     * 这些都是动作
     * 那这里面还要再强调一次
     * 抽象和实现分离
     * 在桥接模式当中
     * 抽象是指抽象类
     * 因为还有各种扩展
     * 而具体的实现呢
     * 一般也都是行为
     * 这里也说了
     * 当然不排除特殊情况
     * 这里面的account也是动词
     * 那桥接模式当中的实现
     * 就是Account的两个接口实现
     * 而这个方法名字
     * 并不强制一模一样
     * 不过我们这里是强制委托
     * 如果方法要达到的目的是一模一样的
     * 所以就直接copy这个方法名了
     * 这里面还是比较灵活的
     * 并没有强制限制
     * 那我们现在来创建中国农业银行
     * ABCBank
     * 
     * 
     * @return
     */
    abstract Account openAccount();
}
package com.learn.design.pattern.structural.bridge;

/**
 * 他继承Bank
 * 这个抽象父类
 * 
 * 
 * @author Leon.Sun
 *
 */
public class ABCBank extends Bank {
	
	/**
	 * 红线说没有构造器
	 * 我们构造一个
	 * 调用super
	 * 调用父类的构造器
	 * 
	 * 
	 * @param account
	 */
    public ABCBank(Account account) {
        super(account);
    }

    /**
     * 这里面实现就很简单了
     * 构造的时候传入的什么Account
     * 就返回什么Account
     * 而这个Account正式父类的Account
     * 所以这里面直接返回Account就可以了
     * 
     * 
     */
    @Override
    Account openAccount() {
    	/**
    	 * 直接输出
    	 * "打开中国农业银行账号"
    	 * 同时我们建立一个爱存不存账号
    	 * ICBCBank
    	 * 
    	 * 
    	 */
        System.out.println("打开中国农业银行账号");
        /**
         * 然后调用了ICBCBank
         * 很明显这个就是ICBC的银行
         * 所以他肯定知道这个是中国工商银行
         * 那具体是什么账号
         * 他把它再进行一个返回
         * 那这个返回之后
         * 就来到了DepositAccount
         * 
         * 在这里我们有时候写业务逻辑的时候
         * 经常有忘记委托的这种情况
         * 隐瞒了一个点
         * 这个平时在使用桥接模式的时候
         * 一定不要忘记
         * 不要把具体的实现自己完成
         * 而是要委托给Account来实现
         * 否则创建相同名字的方法
         * 也就没有意义了
         * 因为只有通过具体的委托
         * 以后account里边的openAccount方法
         * 如果扩展的话
         * Bank这里边是不需要动的
         * 不要把这个实现移到openAccount里边
         * 这里一定要注意
         * 一定要把你具体的行为委托出去
         * 委托给谁呢
         * 正是委托给注入的Account
         * 那我们再run一下
         * 来看一下实际的结果
         * 刚刚那个小坑也是故意埋下的一个伏笔
         * 希望通过这坑
         * 一定要印象深刻
         * 我们再看一下结果
         * 
         * 
         */
        account.openAccount();
        return account;
    }
}
package com.learn.design.pattern.structural.bridge;

/**
 * 继承Bank这个类
 * 
 * @author Leon.Sun
 *
 */
public class ICBCBank extends Bank {
	/**
	 * 然后构造器加上
	 * 
	 * 
	 * @param account
	 */
    public ICBCBank(Account account) {
    	/**
    	 * 进入这里
    	 * 
    	 * F6
    	 * 现在来创建工商银行
    	 * 并且把这个账号拿过来了
    	 * 他呢又调用父类的构造器
    	 * 
    	 * 
    	 */
        super(account);
    }

    @Override
    Account openAccount() {
    	/**
    	 * 打开的是工商银行账号
    	 * 现在我们来看一下UML图
    	 * 
    	 * 注意这里
    	 * 我们打开中国工商银行账号之后
    	 * 我们要把具体的行为委托给Account
    	 * 
    	 * 
    	 */
        System.out.println("打开中国工商银行账号");
        /**
         * 调用它的openAccount
         * 这样才是真正实现openAccount这个行为
         * 委托给account
         * 委托来执行
         * 所以ABCBank也是一样的
         * 
         * 
         */
        account.openAccount();
        /**
         * 直接return account
         * 
         */
        return account;
    }
}
package com.learn.design.pattern.structural.bridge;

/**
 * 
 * @author Leon.Sun
 *
 */
public class Test {
    public static void main(String[] args) {
    	/**
    	 * 我们创建一个银行
    	 * 爱存不存银行
    	 * ICBCBank
    	 * 这里面放一个DepositAccount
    	 * 
    	 * 首先我们进入他的构造器来看一下
    	 * 
    	 * 
    	 */
        Bank icbcBank = new ICBCBank(new DepositAccount());
        /**
         * 然后从这里面拿一个账号
         * icbcBank.openAccount
         * 打开这个账号
         * 
         * 这里打开了中国工商银行账号
         * 
         * 同时打开这个银行之后
         * 我又打开了定期账号
         * 这是因为我们把定期账号
         * 注入到ICBC账号里边
         * 所以他打开账号的时候
         * 首先打开中国工商银行账号
         * 然后打开里面具体的的定期账号
         * 
         * 
         */
        Account icbcAccount = icbcBank.openAccount();
        /**
         * 然后调用它的showAccountType方法
         * 
         * 这里是一个定期账号
         * 
         * 最后show一下AccountType
         * 也就是这个
         * 现在这个使用才是非常标准的使用方式
         * 现在我们再结合前面的原则来说
         * 如果我们不把具体的实现委托给Account
         * 这个接口的话
         * 那我们对ABC Account的实现
         * 是非常可能不符合迪米特原则的
         * 那我们回来再看一下
         * 
         */
        icbcAccount.showAccountType();

        /**
         * 后面直接加一个2
         * 那现在我们来run一下
         * 我们来看一下结果
         * 
         *
         *
         */
        Bank icbcBank2 = new ICBCBank(new SavingAccount());
        /**
         * 这里打开一个中国工商银行账号
         * 
         * 
         */
        Account icbcAccount2 = icbcBank2.openAccount();
        /**
         * 这个是一个活期账号
         * 
         * 
         */
        icbcAccount2.showAccountType();

        /**
         * 这个时候我再创建一个ABCBank
         * 这里面就换一下了
         * SavingAccount
         * 这个时候即使我需要一个工商银行的SavingAccount
         * 也是很容易的
         * 我们直接copy一份出来
         * 
         * 
         * 
         */
        Bank abcBank = new ABCBank(new SavingAccount());
        /**
         * 打开中国农业银行账号
         * 
         * 
         */
        Account abcAccount = abcBank.openAccount();
        /**
         * 这是一个活期账号
         * 那现在debug来跟一个
         * 非常简单
         * 我们直接run debug
         * 
         * 
         */
        abcAccount.showAccountType();
    }
}

 

Guess you like

Origin blog.csdn.net/Leon_Jinhai_Sun/article/details/91038716