库是一组代码模块,它按照可复用的方式组织而成。代码库中保存的是有用、可复用的、编译后的代码,这样,程序员不需要处理代码库的任何源代码都能够利用它的功能。
当复用来自库中的任何模块时,其工作由链接器在链接编译阶段完成,即在编译之后的程序代码和标准库中的库二进制文件进行链接,在程序中调用库函数时将直接跳到库中对应的代码并执行。
库是一个文件,它包含了一个或者多个编译后的文件(称为目标文件),并对其进行索引,以便链接器能够更容易地找到对应符号(例如:类名称、类成员、函数、变量等)以及它们的定义。
建立并复用库
如果项目工程依赖于某个外部库,则必须编辑这个工程文件,指定库的位置和名称并将它的值赋予 INCLUDEPATH+=
和 LIBS+=
。
组织库:依赖性管理
如果一个程序元素复用了另外一个程序元素,则它们之间就存在依赖性。也就是说,构建、使用或者测试这个元素时,就要保证另一个元素存在且是正确的。
如果编译 ProgElement2.cpp 时必须包含 ProgElement1.h ,则称这种依赖性为编译时依赖;如果目标文件 ProgElement2.o 包含 ProgElement1.o 中定义的符号,则称其为链接时依赖。
代码复用,这是一个有价值且重要的目标,但总会产生依赖。当设计类和库时,需要确保尽可能地减少不必要的或者无意的依赖性,因为它们会延长编译时间,降低类和库的可复用性。每一次用 #include
指令包含头文件时,都会带入这个头文件中所包含的其他头文件,这样它们之间的依赖性就会相应的增多。
框架与组件
框架是一个 (通常非常大) 通用 (或针对特定领域的) 类与约定的集合,其目的是提高设计的一致性。框架经常被用来创建图形化应用、数据库应用或者其它复杂的软件。如同底层驱动中的机制和策略。
框架一般都具有文档丰富的公共 API 。API 是库中公共函数、类和接口的描述。为了实现框架,可以采用设计模式。
Qt 是许多开源的面向对象框架中的一种,它提供一组可复用的组件 (即每个平台下都有特定的工具集调用) ,用于创建跨平台的应用。而利用 Qt 这样的多平台框架,就可以从其他人的创造性工作中获得大量的好处。
设计模式
- 设计模式是一种高度抽象的模板,可以将它们应用到不同类型的设计问题上。
- 设计模式是“在特定环境下用于解决某类设计问题中类与对象间通信关系的描述”
比如:
Qt 中的序列化器模式:QTextStream 和 QDataStream
序列化器是一种只负责读取或写入对象的对象模板。
QTextStream 序列化器用于读写人类可以直观理解的文本文件。而 QDataStream 序列化器用于读写结构化的二进制数据。(比如说:可以将一个结构体完整的写入到文件,且轻易读出),也用于网络数据的传输。
下面一个实例展现了 QTextStream 和 QDataStream 的方面,即显示了序列化器模式在处理对象存储时的优势。
//MeataDataValue 代表的而是一首歌曲的属性
class MetaDataValue {
public:
friend QTextStream& operator<< (QTextStream& os,
const MetaDataValue& mdv);
friend QTextStream& operator>> (QTextStream& is,
const MetaDataValue& mdv);
friend QDataStream& operator<< (QDataStream& os,
const MetaDataValue& mdv);
friend QDataStream& operator>> (QDataStream& is,
const MetaDataValue& mdv);
friend bool operator== (const MetaDataValue&,
const MetaDataValue&);
virtual QString fileName()const;
virtual QString genre()const;
virtual QString artist()const;
virtual QString trackTitle()const;
virtual QString trackNumber()const;
protected:
bool m_isNull;
QUrl m_Url;
QString m_TrackNumber;
QString m_TractTitle;
QString m_Comment;
QString m_Genrs;
QString m_Artist;
QTime m_TrackTime;
QString m_AlbumTitle;
QImage m_Image;
};
using namespace std;
//写入(插入)数据到流中
QTextStream& operator<< (QTextStream& os,
const MetaDataValue& mdv)
{
QStringList sl;
s. << mdv.m_Url.toString() << mdv.trackTitle() << mdv.artist() ;
os << sl.join("\t") << "\n";//以自定义的格式,插入数据到流中
return os;
}
//序列化器,使用一个序列化流,流中可以保持各式各样的类型,int,QString,QTime 。。。
//重写插入运算符和提取运算符。来操作一条流,效率,代码整洁度远高于操作对象中的单个数据。
//从流中读取(提取)数据
QTextStream& operator>> (QTextStream& is, MetaDataValue& mdv) {
QString line = is.readLine();
QStringList fields = line.split("\t");//以自定义的格式提取数据
while (fields.size() < 9) { //如果长度小于9则用填充的方式来避免 非法 的内存操作。
fields << "";
}
mdv.m_isNull = false;
mdv.m_Url.setUrl(QUrl::fromUserInput(fields[0])); //从流中取出数据,然后定向到内存变量中
; ; ; //代表省略。。。内容为:一系列成员变量的赋值。
return is;//此次只读了一行数据
}
QDataStream& operator<< (QDataStream& os, const MetaDataValue& mdv) {
os << mdv.m_Url << mdv.trackTitle() << mdv.m_Image;
;;;;
/*此次,提取数据,DataStream和TextStream的区别也在于此,
DataStream存的是二进制流,故流中可以是int,QTime,QStringStream等不同类型的数据。
而TextStream中只能存的是字符串,QString*/
return os;
}
QDataStream& operator>> (QDataStream& is, MetaDataValue& mdv) {
is >> mdv.m_Url >> mdv.m_TrackTime >> mdv.m_Artist >> mdv.m_AlbumTitle;;;;
//由于二进制流的特殊之处,成员变量直接对号入座。
return is;
}
反模式
当然除了以上好的设计模式外,还有一些没有效果,低效率的编程实践被称为反模式
例如:
- 软件设计反模式
- 接口膨胀: 接口的功能强大而复杂,以至于难于复用
- 面对对象设计反模式
- 循环依赖:在对象或者软件模块之间引入了不必要的直接或间接的依赖
- “上帝”对象:指拥有太多属性或太多责任的对象。比如:将模型和视图的代码组合在同一个类中
- 编程反模式
- 魔幻数据:算法中包含了未解释的数字
- 魔幻字符串:代码中包含直接的字符串作为事件类型进行比较
- 方法学反模式
- 复制——粘贴编程:复制并修改已有的代码,而不是创建更通用的解决方案
- 一切从头开始:不采用已有的解决方案,而是采用*(执行起来表现要差很多)*定制的解决方案