初学设计模式之桥接模式
1、什么是桥接模式
官方解答:将抽象部分与实现部分分离,使它们都独立的变化
2、先感性的认识一下桥接模式
一看这么官方的解释,这车速,有点晕车,没关系,开车之前前咱们先骑自行车感受一下慢速行驶。
假如有一道常识题希望答题人输出的正确 答案为:居里夫人是化学家(干扰因素有雨果、生物学家)
选择题:A 居里夫人是化学家
B 居里夫人是生物学家、
C 雨果是化学家
D 雨果是生物学家
连线题:(人名) 是 (头衔)
居里夫人 化学家
雨果 生物学家
如果你是出题人你会选择哪种形式?假如现在又加入了干扰因素贝多芬,另外又加了一个出题的干扰维度国籍:波兰,中国。 很显然,出题人为了方便,会选择连线题的方式,首先连线题重复字数较少,而且,如果增加出题的干扰因素,选择题还需要重新排列选项,工作量很大,连线题只需要再增加一个属性维度或者在属性下面再增加一个内容即可,纵向横向可以随意扩展。桥接模式就相当于连线题,把属性桥接到一串,也就是把抽象类桥接到一起。这样的话抽象类里面继承的具体子类可以随意扩展,再拓展多个其他抽象类也完全可以,实现抽象部分与实现部分分离,方便编写、调试和维护。
3、一起来看一个编程需求
首先读一句熟悉的话:老师用粉笔写字
我们把老师看做一个对象,那么它属于职业类,既然它是类那么它就还会有其他对象,比如工程师等;粉笔看成一个对象,那么它属于一个撰写工具类,同样该类也包含钢笔等;如果用面向对象的编程来实现打印这样一句话,要求可能的输出为:老师用粉笔写字;老师用钢笔写字;工程师用粉笔写字;工程师用钢笔写字。同时可能在今后的程序维护中每个还会扩展更多对象,该怎么实现程序呢?
4、传统的面向对象编程方法实现
如果用传统的面向对象的编程方法去实现:建立人的基类,然后继承人类生成具体的老师类和工程师类,继承具体的老师类生成老师用粉笔写字类和老师用钢笔写字类,继承具体的工程师类生成工程师用粉笔写字类和工程师用钢笔写字类。在主函数中生成老师用粉笔写字类的对象直访问成员函数,其他对象也同理,下面是具体的实现程序。
1 /*用普通方法实现 老师用粉笔写字*/
2 #include<iostream>
3 using namespace std;
4
5 //人作为初始基类
6 class People
7 {
8 public:
9 void writing()
10 {
11 cout << "输出:";
12 }
13 };
14
15 //继承 人基类 生成 老师类
16 class Teacher :public People
17 {
18 public:
19 void writing()
20 {
21 m_people.writing();
22 cout << "老师";
23 }
24 protected:
25 People m_people;
26
27 };
28
29 //继承 老师类 生成 老师用粉笔写字类
30 class TeacherChalkWriting :public Teacher
31 {
32 public:
33 void writing()
34 { m_teacher.writing();
35 cout << "用粉笔写字"<<endl;
36 }
37 protected:
38 Teacher m_teacher;
39 };
40
41 //继承 老师类 生成 老师用钢笔写字类
42 class TeacherPenWriting :public Teacher
43 {
44 public:
45 void writing()
46 {
47 m_teacher.writing();
48 cout << "用钢笔写字"<<endl;
49 }
50 protected:
51 Teacher m_teacher;
52 };
53
54 //继承 人基类 生成 工程师类
55 class Engineer :public People
56 {
57 public:
58 void writing()
59 {
60 m_people.writing();
61 cout << "工程师";
62 }
63 protected:
64 People m_people;
65
66 };
67
68 //继承 工程师类 生成 工程师用粉笔写字类
69 class EngineerChalkWriting :public Engineer
70 {
71 public:
72 void writing()
73 {
74 m_engineer.writing();
75 cout << "用粉笔写字" << endl;
76 }
77 protected:
78 Engineer m_engineer;
79 };
80
81 //继承 工程师类 生成 工程师用钢笔写字类
82 class EngineerPenWriting :public Engineer
83 {
84 public:
85 void writing()
86 {
87 m_engineer.writing();
88 cout << "用钢笔写字" << endl;
89 }
90 protected:
91 Engineer m_engineer;
92 };
93
94 //主函数
95 int main()
96 {
97 TeacherChalkWriting m_teacherChalkWriting;//利用 老师用粉笔写字类 生成对象
98 m_teacherChalkWriting.writing(); //调用writing函数
99
100 TeacherPenWriting m_teacherPenWriting; //利用 老师用钢笔写字类 生成对象
101 m_teacherPenWriting.writing(); //调用writing函数
102
103 EngineerChalkWriting m_engineerChalkWriting;//利用 工程师用粉笔写字类 生成对象
104 m_engineerChalkWriting.writing(); //调用writing函数
105
106 EngineerPenWriting m_engineerPenWriting; //利用 工程师用钢笔写字类 生成对象
107 m_engineerPenWriting.writing(); //调用writing函数
108
109 getchar();//为了留住输出界面,按任意键结束
110 return 0;
111 }

分析:程序一眼看上去似乎没啥问题,但是仔细思考一下程序是不是有一些不足的地方,比如从代码编写和维护角度去考虑
稍加思考就会发现:
1)代码的重复度过高,不精简
2)如果再扩展一个乃至多个维度:在教室里老师用粉笔写字,大学校园的教室里老师用粉笔写字,中国大学校园的教室里老师用粉笔写字,就像要出选择题一样要重新排列很多内容,需要一个一个 类的去继承,不方便扩展
5、利用桥接模式去实现
既然上述实现过程有弊端,那然我们用用桥接模式设计一下程序,将一个属性的抽象类作为另外一个属性抽象类的成员变量串起来,实现的代码如下:
1 /*用桥接模式实现 老师用粉笔写字*/
2 #include<iostream>
3 using namespace std;
4
5 //人作为初始基类
6 class People
7 {
8 public:
9 void writing()
10 {
11 cout << "输出:";
12 }
13 };
14
15 //撰写工具抽象类,可以衍生出粉笔、钢笔等
16 class AbstractWritingTool
17 {
18 public:
19 virtual void writing()
20 {
21 void;
22 };
23 };
24
25 //职业抽象类,可以衍生出老师、工程师等
26 class AbstractProfession:public People
27 {
28 public:
29 virtual void writing()
30 {
31 void;
32 }
33 public:
34 People people;
35 AbstractWritingTool *m_abstractWritingTool;
36 };
37
38 //继承职业抽象类,衍生出老师类
39 class Teacher :public AbstractProfession
40 {
41 public:
42 void writing()
43 {
44 people.writing();
45 cout << "老师";
46 m_abstractWritingTool->writing();
47 };
48 };
49
50 //继承职业抽象类,衍生出工程师类
51 class Engineer :public AbstractProfession
52 {
53 public:
54 void writing()
55 {
56 people.writing();
57 cout << "工程师";
58 m_abstractWritingTool->writing();
59 };
60
61 };
62
63 //继承撰写工具抽象类,衍生出粉笔写字类
64 class ChalkWriting :public AbstractWritingTool
65 {
66 public:
67 void writing()
68 {
69 cout << "用粉笔写" << endl;
70 };
71 };
72
73 //继承撰写工具抽象类,衍生出钢笔写字类
74 class PenWriting :public AbstractWritingTool
75 {
76 public:
77 void writing()
78 {
79 cout << "用钢笔写" << endl;
80 };
81 };
82
83 //主函数
84 int main()
85 {
86 ChalkWriting* m_chalkWriting = new ChalkWriting; //生成粉笔写字对象
87 PenWriting *m_penWriting = new PenWriting; //生成钢笔写字对象
88
89 Teacher m_teacher; //生成老师对象
90 m_teacher.m_abstractWritingTool= m_chalkWriting; //用生成粉笔写字对象给老师的撰写工具进行赋值
91 m_teacher.writing(); //调用writing函数
92 m_teacher.m_abstractWritingTool = m_penWriting; //用生成钢笔写字对象给老师的撰写工具进行赋值
93 m_teacher.writing(); //调用writing函数
94
95 Engineer m_engineer; //生成工程师对象
96 m_engineer.m_abstractWritingTool = m_chalkWriting; //用生成粉笔写字对象给工程师的撰写工具进行赋值
97 m_engineer.writing(); //调用writing函数
98 m_engineer.m_abstractWritingTool = m_penWriting; //用生成钢笔写字对象给工程师的撰写工具进行赋值
99 m_engineer.writing(); //调用writing函数
100
101 getchar();//保留输出界面,按任意键结束
102 return 0;
103 }

我们可以看到可以得到和上面一样的输出,但是这样做代码的重读量减少了,更重要的是如果再增加地点维度,只要再新建立一个地点抽象类就可以,如果在职业属性下再扩展医生、公务员等,只需要创建子类去继承职业抽象类就可以了,实现了抽象类的随意扩展(横向的),每个抽象类衍生出的子类也可以随意拓展(纵向的),这就是官方解释的抽象部分与实现部分分离,各自可以实现独立变化。
6 、小结
最后用图去对比一下普通方法(图 6.1)与桥接模式(图 6.2)的不同

图6.1 普通方法
图6.2 桥接模式
邮箱:wuhong_jian@126.com