1.程序架构
(1)程序架构底有什么用
我们在开发程序时应该避免陷入过早处理细节的陷阱,不要一上来就开始写代码,应该花应有的
时间分析客户需求,准确的讲应该是分析模块需求,因为我们开发的程序往往都是被分配的模块。
在清楚自己需要实现的模块的业务意图和搭建出程序的实现框架后,再去进行具体细节的操作。
在搭建架构时,相应的应该形成相关的模块文档和详细设计文档。
与程序框架相关的问题有:
(1)程序框架思维的源头
来自于用户业务逻辑需求,不管怎么进行框架的设计,你所设计的框架必须以满足用户需求
为中心而展开的。
(2)合理的框架对于当下和未来的意义
(1)好的框架的设计,有利于高效完成项目开发
(2)好的框架的设计,有利于在未来高效地维护和升级程序。
这两方面的要求本其实是对立的:
因为如果你希望将可扩展性和兼容性做到完美,这必然要求对设计框架进行充分的思考和论
证,程序中会加入很多的冗余的代码,但是这一过程本身就是比较耗费时间和资源的。
如果希望节省时间和资源,那么设计的架构必然相对粗糙,因此可能带来很多隐患。如何在
这两者间把握一个度是需要程序设计者认真思考的。
对于很多小型项目来说,由于许程序较小,业务逻辑简单,所以程序的框架也很简单,所
以开发周期都较短,因此这两方面的矛盾并不会太激烈,在开发的过程中很容易得到协调。
(3)程序框架结构从那些方面体现着手
(1)整个程序的业务逻辑在整体上一定要体现清晰的分层思想
比如一般都可以将程序分为如下三层:
交互层:用户交互(内部再分层次)
|
|API
V
中间层:核心逻辑实现(内部再分层次)
|
|API
V
底 层:数据保存和交互层[文件/数据库](内部再分层次)
以上是开发时,站在具体的业务逻辑上实现的分层结构,在开发程序的过程中,我们
应该有意识的区分这三层结构,层级之间留下相应的API连接。TCP/IP协议栈就是典型
的分层结构。
分层的好处:
(1)能够有效的提高开发效率
(1)分层结构有利于任务划分
将任务独立化,有利于任务的模块化设计,提高相关程序代码的内聚性。
(2)提高协同工作效率,节省人力资源
实现任务独立分配后,很容易做到了专人做专事,极大的提高了人事资
源的利用率。
(2)层级(模块)间弱耦合性
(1)需要修改程序时,不会牵一发而动全身。
(2)升级时,更换某层的代码时,只需要遵循相同的API接口,其它层级间不需
要做任何改动,这就是降低耦合的好处。
(2)程序中使用哪些手段体现出层次结构呢
(1)继承/多态等语法特性
这个我们在将c时就讲过了c实现多态的方式,将c++时也讲了c++的多态实现方
式,我们说多态非常重要的用途就是实现程序的分层,有效的降低层间耦合性。
(2)设计模式
设计模式为开发人员提供了一种快速借用前辈设计经验进行设计的有效途径。
设计模式运用了面向对象编程语言的重要特性,如封装、继承、多态。因此
设计模式的分层是凌驾于多态等面向对象语法特性之上的一种更高层次的抽象
层次架构管理。
使用前人总结的这些设计模式,使我们可以快速高效的套用这些设计来解决我
们面临的相似问题。
(3)人为分层设计
在设计模式之上的更高一层的就是人为的实现封装分成,比如写一个linux下的
嵌入式层序,涉及数据处理和界面交互,那我们我们在实现时,就可以人为的
分为两层,
(1)界面层:用c++写QT程序实现
(2)数据处理:用c调用调用linux系统API写一个程序实现
这两个程序之间使用进程间通信使之进行连接。
以上这两层实际上是人为的一种设计分层的结果,在单个程序里面我们还会用
到多态和具体的设计模式来实现具体的分层。
实际上在这几种分层架构之上,还有更高层次的架构理念,这就是程序架构师需要思考内容,
这个已经超出普通编程工作者讨论的范畴。
3.设计模式
(1)什么是设计模式
设计模式是一种算法,目的是为了程序编写/升级/维护更加简单和高效,设计模式与具体的语言
没有关系,几乎所有的面向对象的语言都可以使用这些设计模式编写程序,实际上c语言同样会使
用这些设计模式,只是没有因为没有面向对象的特点,而有些设计模式对面向对象的语法特性依赖
性较强,在c中有些不合适。
就像数据结构一样,数据结构是一种算法抽象,任何一门语言都可以编程来实现这些数据结构,数据
结构是对复杂数据组织管理的算法抽象,设计模式是高效地实现和维护程序代码的算法抽象。所以设
计模式和数据结构一样,是完全脱离具体语言的更高层次的抽象存在。
已知的设计模式大概有20多种,但是实际上这些设计模式在实际开发中到底有起到的作用有多大,
需要看具体情况。
设计模式不是万能的,每种设计模式都有其自己的应用目的和适应的情况,如果脱离了适应的情况,
使用相关设计模式时,不但不能起到简化程序结构,提高开发效率的效果,还会使问题变得复杂化。
我们在编程中实际上最常见的有三种设计模式:
(1)单例模式
(2)迭代器
(3)工厂模式
单例模式的目的是为了保证程序中实例化的对象是唯一的。
迭代器主要目的是为了不暴露对象内部结构的情况下能够像指针一样实现遍历。
工厂个模式的主要目的希望将程序中大量需要使用的对象的实例化进行集中化的管理,采用这样设
计,不管是对于当前的程序开发,还是对以后程序的修改维护和系统升级,都带来高效的实现。
但是工厂模式并不是王万能的,很多人也因此一直在讨论工厂模式是不是必须要的,但是工厂模式
有它自己的应用目的,如果脱离了它的使用环境,工厂模式就没有意义了。
一般来说对于嵌入式开发来说,软件级别相对较小,我们其实不需要使用所谓的工厂模式,迭代器
也是如此,迭代器固然很重要,但是正常情况下我们不需要实现自己的迭代器,我们呢会直接使用
c++/java提供的迭代器,它们已经能够满足我们几乎所有的需求了。
2. 单例模式
(1)为什么需要单例子模式
单例模式使用还是比较广泛的,在面向对象语言java/c#等实现的程序中都在广泛使用单例模式,
之所以使用单利模式,其意图是为了保证该类在整个程序中某个类实例化的对象是唯一的。
为什么有这样的需求呢?
比如:
(1)例化一个针对鼠标和键盘操作的对象,因为鼠标和键盘通常是唯一的,那么针对鼠标
和键盘的操作的对象必须时唯一。
(2)对程序日志文件的操作对象也应该时唯一。
(3)希望设计一个交互界面,不管这个交互界面被启动多好次,只能有这个一个交互界面
存在。
实际上在安卓开发中,大量安卓类库提供的类对象的实例化使用的都是单利模式。
(2)单利模式的直观表现形式
以Student类为例,我们常见的对象实例化的形式为
Student stu;
Student *stup = new Student();
但是使用单例模式时,常见实例化对象的样子是如下这样的。
Student *stup = Student.get_instance();
不是直接使用new方式进行实例化的,因为在单例模式中,构造函数都被隐藏了,
如果不隐藏的话,调用构造函数就可以生成非常多不同的对象,这显然不符合单
例模式的要求。
(3)单利模式举例
例子1:
#include <iostream>
#include <string>
#include <cassert>
using namespace std;
class Student {
public:
void set_name(string &name) { this->name=name; }
void set_num(string &num) { this->num=num; }
string get_name() { return this->name; }
string get_num() { return this->num; }
static Student *get_instance(const string name=" ", const string num=" ") {
if(NULL == stu_instance)
stu_instance = new Student(name, num);
return stu_instance;
}
~Student() {
delete stu_instance;
}
private:
Student(const string name, const string num)
:name(name), num(num) { }
static Student *stu_instance;
string name;
string num;
};
Student * Student::stu_instance = NULL;
int main(void)
{
Student *stup1 = Student::get_instance("zhangsan", "9527");
cout << stup1->get_name() <<" "<< stup1->get_num() << endl;
Student *stup2 = Student::get_instance("wangwu", "007");
cout << stup2->get_name() <<" "<< stup2->get_num() << endl;
return 0;
}
运行结果:
zhangsan 9527
zhangsan 9527
例子分析:
从运行的结果老看,完全实现了单例模式的要求,从例子中观察到,如果想实现单例模式
,该例子需要实现这几个方面的问题:
(1)构造函数必须被隐藏
这时实现单例模式很重要的条件。
(2)定义一个静态的获取对象指针的成员函数
static Student *get_instance(const string name=" ", const string num=" ") {
if(NULL == stu_instance)
stu_instance = new Student(name, num);
return stu_instance;
}
之所以是静态的,是因为单例模式类的构造函数被隐藏,那么外部无法实例化对象,
如果为非静态函数的话,由于外部无法实例化对象,无法调用该函数。
但是定义为静态后,可以直接使用类名进行调用,不需要依赖对象而存在。
(3)定义一个静态Student的类型指针的成员
static Student *stu_instance;
之所以为静态,是因为静态成员函数get_instance只能操作静态数据成员。
有些同学可能会疑问,为什么不定义成为如下这样呢?
static Student stu_instance;
前面课程中我们强调过,因为类和结构体中不允许定义自己类型的对象。
(4)由于成员stu_instance指向的空间是动态分配的,我们必须在析构函数中释放该空间
~Student() {
delete stu_instance;
}
(5)必须加入判断语句
if(NULL == stu_instance)
因为这是保证单例的很重要一环,如果该指针成员不为空的话,就意味着该对象已经被开辟出来了,
后面的共用这个对象即可。
例子2:
实际上面实现单利模式的例子有点啰嗦:
(1)需要初始化静态数据成员stu_instance
(2)必须在析构函数释放动态分配的对象空间
针对这;两个缺点,我们可以将上面的例子改成下面这种形式也是可以的,而且实现会跟为简洁。
#include <iostream>
#include <string>
#include <cassert>
using namespace std;
class Student {
public:
void set_name(string &name) { this->name=name; }
void set_num(string &num) { this->num=num; }
string get_name() { return this->name; }
string get_num() { return this->num; }
static Student *get_instance(const string name=" ", const string num=" ") {
static Student stu_instance(name, num);
return &stu_instance;
}
private:
Student(const string name, const string num)
:name(name), num(num) { }
string name;
string num;
};
int main(void)
{
Student *stup1 = Student::get_instance("zhangsan", "9527");
cout << stup1->get_name() <<" "<< stup1->get_num() << endl;
Student *stup2 = Student::get_instance("wangwu", "007");
cout << stup2->get_name() <<" "<< stup2->get_num() << endl;
return 0;
}
运行结果:
zhangsan 9527
zhangsan 9527
例子分析:
从结果看,我们同样是实现了单例模式,而且实现方式更为简洁,本例为了实现单例模式:
(1)构造函数必须被隐藏
(2)get_instance必须时static的,原因与上面的例子是一样的
(3)局部变量stu_instance必须是static,只有这样才能所有调用该成员函数人共享的时
同一个空间空间。
这个例子简洁了非常多,没有了静态成员的初始化,也不需要在析构函数释放空间。
3.工厂模式
但是作为学习者,我们应该对设计模式有一定的了解,需要知道模式是一个什么样的存在,
它的意义是什么?迭代器在将c++基础课程的时候我们简单介绍过,这里介绍一下工厂模式
的实现。
(1)为什么需要工厂模式
概括起来讲的话,工厂模式就是将大量实例化对象的操作放在工厂类中进行集中化的管理。
具体来讲,使用工厂模式两大目的:
(1)集中化管理各种类的实例化操作,避免在程序中直接使用new进行实例化对象,
这些new操作应该集中在工厂类中完成。
(2)工厂类中实现对对象的额外的初始化操作(不仅仅只是初始化成员变量)
比如需要读写文件,读写数据库等操作,我们就可以将这些代码从构造器中独立出来。
独立出来的这些代码就放在工厂类中去完成,降低代码过于集中所带来的风险。
使用工厂模式时,基本都与多态相关,因此工厂模式基本都涉继承。
(2)三种工厂模式
(1)简单工厂(复杂度最低)
#include <iostream>
using namespace std;
enum CTYPE{BMWCAR, DZCAR};
/* 抽象类 */
class Car {
public:
virtual void run()=0;
};
class BMWcar : public Car {
public:
virtual void run() { cout<<"BMWcar is runing" << endl; }
};
class DZcar : public Car {
public:
virtual void run() { cout<<"DZcar is runing" << endl; }
};
class Factory {
public:
Car *create_car(enum CTYPE ctype) {
if(BMWCAR == ctype) {
return (new BMWcar());
}
else if(DZCAR == ctype) {
return (new DZcar());
}
}
};
int main(void)
{
BMWcar *bmwcar = (BMWcar *)(new Factory())->create_car(BMWCAR);
DZcar *dzcar = (DZcar *)(new Factory())->create_car(DZCAR);
bmwcar->run();
dzcar->run();
return 0;
}
(2)工厂方法模式(复杂度提升)
#include <iostream>
using namespace std;
enum CTYPE{BMWCAR, DZCAR};
/* 抽象类 */
class Car {
public:
virtual void run()=0;
};
/* 具体产品:宝马汽车 */
class BMWcar : public Car {
public:
virtual void run() { cout<<"BMWcar is runing" << endl; }
};
/* 具体产品:大众汽车 */
class DZcar : public Car {
public:
virtual void run() { cout<<"DZcar is runing" << endl; }
};
/* 工厂抽象类 */
class Factory {
public:
virtual Car *create_car() = 0;
};
/* 生产宝马的工厂 */
class BMWFactory : public Factory {
public:
BMWcar *create_car() { return (new BMWcar()); }
};
int main(void)
{
Car *bmwcar = (new BMWFactory())->create_car();
Car *dzcar = (new DZFactory())->create_car();
bmwcar->run();
dzcar->run();
return 0;
}
(3)抽象工厂模式(复杂度最高)
#include <iostream>
using namespace std;
enum CTYPE{BMWCAR, DZCAR};
/* 小汽车抽象类 */
class Car {
public:
virtual void run()=0;
};
/* 具体小汽车产品:宝马汽车 */
class BMWcar : public Car {
public:
virtual void run() { cout<<"BMWcar is runing" << endl; }
};
/* 具体小汽车产品:大众汽车 */
class DZcar : public Car {
public:
virtual void run() { cout<<"DZcar is runing" << endl; }
};
/* bus的抽象类 */
class Bus {
public:
virtual void run()=0;
};
/* 具体bus产品:金龙bus */
class JLBus : public Bus {
public:
virtual void run() { cout<<"JLBus is runing" << endl; }
};
/* 具体bus产品:东风bus */
class DFBus : public Bus {
public:
virtual void run() { cout<<"DFBus is runing" << endl; }
};
class Factory {
public:
virtual Car *create_car() = 0;
virtual Bus *create_bus() = 0;
};
/* 工厂:生产BMW的小汽车和金龙的BUS出口 */
class Outter_Factory : public Factory {
public:
BMWcar *create_car() { return (new BMWcar()); }
JLBus *create_bus() { return (new JLBus()); }
};
/* 工厂:生产DZ的小汽车和东风的BUS出口 */
class Inner_Factory : public Factory {
public:
DZcar *create_car() { return (new DZcar()); }
DFBus *create_bus() { return (new DFBus()); }
};
int main(void)
{
/* 出国的车 */
BMWcar *bmwcar = (BMWcar *)(new Outter_Factory())->create_car();
JLBus *jlbus = (JLBus *)(new Outter_Factory())->create_bus();
cout << "出国的车" <<endl;
bmwcar->run();
jlbus->run();
/* 国内使用的车 */
DZcar *dzcar = (DZcar *)(new Inner_Factory())->create_car();
DFBus *dfbus = (DFBus *)(new Inner_Factory())->create_bus();
cout << "\n国内使用的车" <<endl;
dzcar->run();
dfbus->run();
return 0;
}
(3)单例模式与工厂模式的结合
单例模式的目的是为了实现对象存在的唯一性,工厂模式是对实例化操作的封装。
我们通过工厂模式实例化对象时,希望也能够实现单例模式,比如下面这个例子。
例子:
#include <iostream>
using namespace std;
enum CTYPE{BMWCAR, DZCAR};
/* 抽象类 */
class Car {
public:
virtual void run()=0;
};
class BMWcar : public Car {
public:
virtual void run() { cout<<"BMWcar is runing" << endl; }
};
class DZcar : public Car {
public:
virtual void run() { cout<<"DZcar is runing" << endl; }
};
class Factory {
public:
static BMWcar bmwcar;
static DZcar dzcar;
Car *create_car(enum CTYPE ctype) {
if(BMWCAR == ctype) return &bmwcar;
else if(DZCAR == ctype) return &dzcar;
}
};
BMWcar Factory::bmwcar;
DZcar Factory::dzcar = DZcar();
int main(void)
{
BMWcar *bmwcar = (BMWcar *)(new Factory())->create_car(BMWCAR);
DZcar *dzcar = (DZcar *)(new Factory())->create_car(DZCAR);
bmwcar->run();
dzcar->run();
return 0;
}
当然class Factory 也可以改成如下实现方式:
class Factory {
public:
static BMWcar *bmwcar;
static DZcar *dzcar;
Car *create_car(enum CTYPE ctype) {
if(BMWCAR==ctype && NULL==bmwcar) return (bmwcar=new BMWcar());
else if(DZCAR==ctype && NULL==dzcar) return (dzcar=new DZcar());
}
};
BMWcar* Factory::bmwcar;
DZcar* Factory::dzcar = 0;
(4)工厂模式实例
#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define STUFILE "./stufile.txt"
#define TEAFILE "./teafile.txt"
using namespace std;
enum CTYPE{STU, TEA};
class People {
public:
People(const string name=" ", const string id=" ", const string tel=" ", const string age=" ")
:name(name), id(id), tel(tel), age(age) { }
string name;
string id;
string tel;
string age;
};
class Student : public People {
public:
Student(const string name=" ", const string id=" ", const string tel=" ",
const string age=" ", const string num=" ", const string score=" ")
:People(name, id, tel, age), num(num), score(score){ }
string num;
string score;
};
class Teacher : public People {
public:
Teacher(const string name=" ", const string id=" ", const string tel=" ",
const string age=" ", const string tno=" ", const string rank=" ")
:People(name, id, tel, age), tno(tno), rank(rank){ }
string tno;
string rank;
};
class Factory {
public:
People *create(enum CTYPE ctype) {
if(STU == ctype) {
myopen(string(STUFILE));
fin>>name>>id>>tel>>age>>num>>score;
return (new Student(name, id, tel, age, num, score));
} else if(TEA == ctype) {
myopen(string(TEAFILE));
fin>>name>>id>>tel>>age>>tno>>rank;
return (new Teacher(name, id, tel, age, tno, rank));
}
}
private:
void myopen(string fname) {
fin.open(fname.c_str(), ios::in);
if(!fin) {
cout<<"open stufile fail:"<<strerror(errno)<<endl;
exit(-1);
}
}
string name, id, tel, age, num, score, tno, rank;
fstream fin;
};
int main(void)
{
Student *stup = (Student *)(new Factory())->create(STU);
cout << "学生信息:"<<stup->name<<" "<<stup->id<<" "<<stup->tel;
cout<<" "<<stup->age<<" "<<stup->num<<" "<<stup->score<<endl<<endl;
Teacher *teap = (Teacher *)(new Factory())->create(TEA);
cout << "老师信息:"<<teap->name<<" "<<teap->id<<" "<<teap->tel;
cout<<" "<<teap->age<<" "<<teap->tno<<" "<<teap->rank<<endl<<endl;
return 0;
}
运行结果:
学生信息:student 9527 1833434543 22 1111 89.6
老师信息:teacher 7654 18334454512 40 2222 99.7
例子分析:
这个例子使用的虽然时简单工厂模式,但是在实际的开发中,很多时候使用其实就是简单的工厂模式
,除非有特殊需求,很少使用复杂的工厂模式,如果本身解决的问题就很简单的话,使用复杂工厂模
式只会将问题复杂化。
通过使用简单工厂模式,本例达到了如下目的:
(1)实现了模块化
将实例化具体对象的操作都交给了Factory工厂类,在main函数中,操作逻辑得到了简化,
程序逻辑显得更加清晰。
(2)在Factory中加入了很多额外操作代码
我们将打开和读写文件的操作都放在了Factory工厂类中实现,虽然这些操作可以放在类的
构造函数中处理,但是这么做会使类的构造函数更加简洁。
(3)实现了实例化操作的集中管理
如果需要修改实例化的代码时,只需要找到工厂类,便可实现所有的修改,如果我们是直接使用
new方式实例化的话,意味着这些实例化操作散落在了程序中各个角落,修改时需要在程序中到处
寻找,很不利于实现程序的高效维护。
设计模式
最新推荐文章于 2021-09-18 17:21:53 发布