学习实践:使用模式,原则实现一个C++数据库访问类

一、概述

在我参与的多个项目中,大家使用libMySQL操作MySQL数据库,而且是源码即复用,在多个项目中有多套相同或相似的源码,这样的复用方式给开发带来了不变,而且libMySQL的使用比较麻烦,要应对很多的细节,很容易出错。

我要写一个动态链接库,将对libMySQL的操作封装起来,以二进制复用代替源码级复用;要提供线程安全的接口,用户无需关系是否加锁这样细节性的问题,减少出错及死锁的机会,当然也要允许用户自己选择是否线程安全的访问数据库;要简化访问数据库的流程,接口越简单越好。

我从2011年开始写这个库,我给它起名字叫HiDB。HiDB从2011年到现在,经历了一个由简到繁又有繁到简的过程,中间也包含了自己对程序理解的变化。

很多的考虑看起来很细碎,例如代码的注释风格(选择我们一贯的注释风格还是doxygen的注释风格,代码大全中也讲到了注释风格。是否需要借鉴);代码的命名规范(匈牙利命名法还是Java,C#的命名法,还有很多开源项目的命名规范);C++设计新思维中讲到Policy classes,是否可以应用到这个库中;Effective C++中讲的接口与实现分离。

这些年自己考虑过的一些东西包括注释风格的选择(我们一贯的注释,doxygen的注释风格,代码大全中也讲到了注释风格),出错时是使用错误码报出错误还是使用异常报出错误?C++设计新思维中讲的Policy classes,Effective C++中讲的接口与实现分析(Impl)

二、接口

(一)接口概述

首先确定一下HiDB的接口。该库对外显示为一个HiDB类,该类应该包含的接口包括:

1: 用户选择线程安全还是非线程安全

2: 打开数据库连接

3: 关闭数据库连接

4: 执行insert,update等非查询操作的接口(ExecuteNoQuery)

5: 获得查询结果第一行第一列的接口(ExecuteScaler)

6: 获得一次查询的所有记录(多行多列)的接口(ExecuteQuery)

7: 执行事务的接口(OnTransaction)

所有的接口都在产生异常时抛出,需要对异常做一个封装,这个异常中最好能标识出异常产生的位置,异常的编号,异常的描述,引起异常的SQL语句。我设计的异常如下:

/** @brief 数据库操作异常 */
class HI_DB_EXPORT HiDBException
{
public:
HiDBException();
public:
std::string ToSrting();
public:
std::string m_sql; /**< 本次操作的SQL语句 */
std::string m_descript; /**< 异常描述 */
std::string m_position; /**< 异常位置 */
long m_errorId; /**< 异常编号 */
HiDBType m_dbTyp; /**< 数据库类型 */
};

为了方便的抛出异常,我提供了一个宏(这个宏只有HiDB库使用)

/** @brief 异常语句宏 */
#define HiDBHelperOnError(ps, script,sql, id) HiDBException exception;exception.m_position = ps;exception.m_descript = script;exception.m_sql = sql;exception.m_errorId = id;throw exception;//return false;

提供该宏,除了简化用户输入外,还有一个想法就是如果用户不想使用异常,可以修改该宏,例如返回false。

熟悉ADO.NET的朋友应该可以看出这些接口是借鉴了ADO.NET的。

(二)具体接口

<1> 构造函数

本来在《C++设计新思维》中,有个Policy classes,适合这种根据用户需要提供安全或非安全接口的需求,但是Policy classes适合模板,不适合非模板,所以在这儿就没有使用,只是在构造函数中添加了一个布尔参数isUsingLock,如果为true则提供线程安全接口,否则接口是非线程安全的。

HiDB打算在将来除支持MySQL外,还支持其他数据库,所以在构造函数中,除isUsingLock外,还有一个选择数据库类型的接口。

将数据库类型写成枚举,则枚举为:

/** @brief 数据库类型 */
enum HiDBType
{
HiDBType_Invail, /**< 无效类型 */
HiDBType_MySQL, /**< MySQL */
};

构造函数的声明就明确下来了:

/**
* @brief 构造函数
* @param[in] type 数据库类型
* @param[in] isUsingLock 是否需要使用互斥锁
*/
HiDB(HiDBType type = HiDBType_MySQL, bool isUsingLock = false);

<2>打开数据库连接
打开数据库连接比较简单:

/**
* @brief 打开数据库连接
* @param[in] conn 数据库连接字符串
* @retval true:成功,false;失败
* @par 实例:
* @code
* HiDB db;
* if (db.Open("host=127.0.0.1;port=3306;dbname=test;user=root;pwd=root;charset=gbk;"))
* {
* // 打开成功
* }
* else
* {
* // 打开失败
* }
* @endcode
*/
bool Open(const char* conn) throw (HiDBException);

该接口的conn参数是一个字符串,这样字符串就具有扩展性,可以针对不同的数据库,进行不同的处理(这一点感觉不是很好,但是能提供接口的稳定性)。不同数据库,需要满足特定的格式,在MySQL中,要使用类似于“host=127.0.0.1;port=3306;dbname=test;user=root;pwd=root;charset=gbk;”的格式。

<3> 关闭数据库连接

/**
* @brief 关闭据库连接
*/
void Close(void);

<4> IsOpen接口

这个接口是延伸出来的,既然有open,close,提供一个IsOpen好像也是应该的,让用户了解当前的打开状态。

/**
* @brief 数据库连接是否打开
*/
bool IsOpen();

<5> 执行非查询语句接口

执行SQL语句的接口,应该可以接收可变参数,除可变参数外,还应该有一个包含sql语句的字符串参数,所以接口定义如下:

/**
* @brief 执行SQL语句,并不返回结果
* @param[in] conn SQL语句
* @retval true:成功,false;失败
* @par 实例:
* @code
* HiDB db;
* if (db.ExecuteNoQuery("UPDATE table SET Paramer1=‘%s‘
* and Paramer2=‘%s‘ OR Paramer3=%d", "test1", "test2", 3))
* {
* // 执行成功
* }
* else
* {
* // 执行失败
* }
* @endcode
*/
bool ExecuteNoQuery(const char* sql, ...) throw (HiDBException);

<6> 获得查询结果第一行第一列的接口

该接口的参数与执行非查询语句的接口一致,但是返回值应该为字符串,如果执行失败,则应该返回空字符串。触发异常时,抛出HiDBException异常。

/**
* @brief 执行SQL语句,返回一个结果
* @param[in] sql SQL语句
* @retval 获得的数据,如果为空,则失败
*/
std::string ExecuteScalar(const char* sql, ...) throw (HiDBException);

<7> 获得一次查询的所有记录(多行多列)的接口

该接口的参数与执行非查询语句的接口一致。

返回结果应该是包含多行多列的一个数据集,在ADO.NET中有DataTable,在这儿,我们可以用stl中的vector存储多行,map存储每行数据的多列。

多以需要定义一个数据集:

#ifndef HiDBTable

typedef std::map<std::string, std::string> HiDBMap;

/** @brief 查询结果 */
typedef std::vector<HiDBMap> HiDBTable; /**< 查询结果 */
#endif

因为HiDBTable中包含多个map,所以最好避免拷贝,使用stl的shared_ptr来避免多次拷贝:

/**
* @brief 执行SQL语句,返回一个结果集合
* @param[in] sql SQL语句
* @retval 存储查询记录的智能指针
*/
std::shared_ptr<HiDBTable> ExecuteQuery(const char* sql, ...) throw (HiDBException);

<8> 执行事务的接口

执行事务接口是一个Command模式的接口,参数应该是一个函数对象。该对象为无参无返回值的函数对象即可。stl中提供了function对象。(在最初的版本中是自己实现函数对象的)

/**
* @brief 在事务中执行处理
* @param[in] fun 处理函数
*/
void OnTransaction(const std::function<void()>& fun) throw (HiDBException);

(三) 接口的使用案例

HiDB m_DB = new HiDB(HiDBType_MySQL, true);
try
{
bool ret = m_DB->Open(
"host=127.0.0.1;port=3306;dbname=test;user=root;pwd=root;charset=gbk;"
);
m_DB->ExecuteNoQuery("drop table if exists table1;");
string val = m_DB->ExecuteScalar(
"SELECT column4 FROM table1 WHERE column1=‘%s‘ AND column3=%d",
&val, "hitest", 59);
shared_ptr<HiDBTable> table = this->m_DB->ExecuteQuery(
"SELECT * FROM table1 WHERE column1=‘%s‘ OR column1=‘%s‘",
"hitest", "mytest");
}
catch(HiDBException& e)
{
// ...
}

(四) 其他

其实,我以前提供的接口比现在要复杂很多,首先我模仿ADO.NET,对SQL参数进行了封装,封装了SQL参数的名称,类型,是否为空,值等信息,对获得的数据也进行了类似的封装。 ,还针对SQL参数提供了Create和Delete函数。

有一个同时看了我的接口后说,我的接口太复杂了,分层不是明确。我接受了他的建议,就将接口修改为现在的接口了。

另外,执行事务接口,最开始我是自己创建函数对象的,这也增加了复杂度,后来使用了stl的function对象,辅以lambda表达式,则使用起来简单多了。

(五) 完整的接口:

<1>HiDBCommon.h 提供接口应用到的相关枚举和结构体

#pragma once

/**
* @defgroup 数据库模块
* @{
*/
#include "HiDBExport.h"

#include <string>
#include <vector>
#include <map>
#include <sstream>

/** @brief 数据库类型 */
enum HiDBType
{
HiDBType_Invail, /**< 无效类型 */
HiDBType_MySQL, /**< MySQL */
};

#ifndef HiDBTable

typedef std::map<std::string, std::string> HiDBMap;

/** @brief 查询结果 */
typedef std::vector<HiDBMap> HiDBTable; /**< 查询结果 */
#endif

/** @brief 数据库操作异常 */
class HI_DB_EXPORT HiDBException
{
public:
HiDBException();
public:
std::string ToSrting();
public:
std::string m_sql; /**< 本次操作的SQL语句 */
std::string m_descript; /**< 异常描述 */
std::string m_position; /**< 异常位置 */
long m_errorId; /**< 异常编号 */
HiDBType m_dbTyp; /**< 数据库类型 */
};

/** @brief 异常语句宏 */
#define HiDBHelperOnError(ps, script,sql, id) HiDBException exception;exception.m_position = ps;exception.m_descript = script;exception.m_sql = sql;exception.m_errorId = id;throw exception;//return false;

/**//** @}*/ // 数据库模块

<2> HiDB.h 主要的接口:

#pragma once

/**
* @defgroup 数据库模块
* @{
*/

#include <memory>
#include <functional>

#include "HiDBCommon.h"

class HiDBImpl;

#pragma warning (disable: 4290)

/**
* @brief 数据库操作类,封装数据库的通用操作,本类使用策略模式实现
* @author 徐敏荣
* @date 2012-06-14
*
* @par 修订历史
* @version v0.5 \n
* @author 徐敏荣
* @date 2012-06-14
* @li 初始版本
* @version v0.6 \n
* @author 徐敏荣
* @date 2014-08-04
* @li 简化程序
*
*/
class HI_DB_EXPORT HiDB
{
public:

/**
* @brief 构造函数
* @param[in] type 数据库类型
* @param[in] isUsingLock 是否需要使用互斥锁
*/
HiDB(HiDBType type = HiDBType_MySQL, bool isUsingLock = false);

/**
* @brief 析构函数
*/
~HiDB();

public:

/**
* @brief 打开数据库连接
* @param[in] conn 数据库连接字符串
* @retval true:成功,false;失败
* @par 实例:
* @code
* HiDB db;
* if (db.Open("host=127.0.0.1;port=3306;dbname=test;user=root;pwd=root;charset=gbk;"))
* {
* // 打开成功
* }
* else
* {
* // 打开失败
* }
* @endcode
*/
bool Open(const char* conn) throw (HiDBException);

/**
* @brief 关闭据库连接
*/
void Close(void);

/**
* @brief 数据库连接是否打开
*/
bool IsOpen();

public:

/**
* @brief 执行SQL语句,并不返回结果
* @param[in] conn SQL语句
* @retval true:成功,false;失败
* @par 实例:
* @code
* HiDB db;
* if (db.ExecuteNoQuery("UPDATE table SET Paramer1=‘%s‘
* and Paramer2=‘%s‘ OR Paramer3=%d", "test1", "test2", 3))
* {
* // 执行成功
* }
* else
* {
* // 执行失败
* }
* @endcode
*/
bool ExecuteNoQuery(const char* sql, ...) throw (HiDBException);

public:

/**
* @brief 执行SQL语句,返回一个结果
* @param[in] sql SQL语句
* @retval 获得的数据,如果为空,则失败
*/
std::string ExecuteScalar(const char* sql, ...) throw (HiDBException);

public:

/**
* @brief 执行SQL语句,返回一个结果集合
* @param[in] sql SQL语句
* @retval 存储查询记录的智能指针
*/
std::shared_ptr<HiDBTable> ExecuteQuery(const char* sql, ...) throw (HiDBException);

public:

/**
* @brief 在事务中执行处理
* @param[in] fun 处理函数
*/
void OnTransaction(const std::function<void()>& fun) throw (HiDBException);

private:
/**
* @brief 数据库操作实现指针
*/
HiDBImpl* m_Impl; /**< 数据库操作实现指针 */
};

/**//** @}*/ // 数据库模块

三 实现

实现采用了从《Effective C++》中学习到的实现与接口相分析的原则,在HiDB中使用HiDBImpl实现访问数据库的逻辑。

(一) 可变参数的处理

当然,在HiDB中是需要解决根据sql参数和可变参数拼装成一个完整SQL语句的问题。

该问题使用一个宏来实现:

#if !defined(HISDB_ON_VARLIST)
#define HISDB_ON_VARLIST(x, y) char chArr[2048] = {0};char* pchar = &chArr[0];va_list pArgList;va_start(pArgList, y);::_vsnprintf(chArr, 2047, x, pArgList); va_end(pArgList) ;
#endif

(二) 互斥锁的实现

自己根据临界区,实现了一个互斥锁,互斥锁接口如下:

1: 构造函数: 实现临界区的初始化

2: 析构函数: 实现临界区的删除

3: 进入临界区

4: 离开临界区

实现函数如下:

/**
* @brief 临界区访问类,主要封装windows临界区的访问,该类主要在栈中使用,利用局部变量的构造和析构函数出入临界区
* @author 徐敏荣
* @date 2012-06-14
*
* @par 修订历史
* @version v0.5 \n
* @author 徐敏荣
* @date 2012-06-14
* @li 初始版本
*
*/
class HiCritical
{
public:

/**
* @brief 构造函数
*/
HiCritical()
{
::InitializeCriticalSection(&cs);
}

/**
* @brief 析构函数
*/
~HiCritical()
{
::DeleteCriticalSection(&cs);
}

/**
* @brief 进入临界区
*/
void Enter()
{
::EnterCriticalSection(&cs);
}

/**
* @brief 离开临界区
*/
void Leave()
{
::LeaveCriticalSection(&cs);
}

CRITICAL_SECTION* GetSection()
{
return &cs;
}
private:

/**
* @brief 临界区对象
*/
CRITICAL_SECTION cs; /**< 临界区对象 */
};

另外还提供一个临界区管理类(HiCriticalMng),在构造该类时,进入临界区,析构该类时离开临界区。如果构造函数中传入的是NULL,则不进行任何互斥处理。

/**
* @brief 临界区访问管理类,利用构造函数进入临界区,利用西沟函数离开临界区
* 如果向构造函数提供NULL参数,则不使用临界区。
*
*/
class HiCriticalMng
{
public:
HiCriticalMng(HiCritical& crl): cl(&crl)
{
cl->Enter();
}

HiCriticalMng(HiCritical* crl): cl(crl)
{
if (cl)
{
cl->Enter();
}
}

~HiCriticalMng()
{
if (cl)
{
cl->Leave();
}
}

private:
HiCritical* cl;
};

(三) HiDBImpl的接口

作为数据库访问类,HiDBImpl实现HiDB需要的所有接口,所以HiDBImpl与HiDB接口类似,但是HiDBImpl接口接收的参数为完整的SQL语句(因为带可变参数的SQL语句已经被HiDB处理了)。

HiDBImpl不但要支持MySQL,以后还要支持其他数据库,所以不能有LibMySQL相关的东西,HiDBImpl应该是一个基类,可以被派生,例如派生出支持LibMySQL的子类。

HiDBImpl要线程安全的,所以要包含互斥锁HiCritical,又要可以非线程安全(HiCriticalMng支持NULL参数),所以HiCritical需要时这指针,这样,HiDBImpl的接口就出来了。

HiDBImpl接口如下:

#pragma once
/**
* @defgroup 数据库操作实现类接口类
* @brief 数据库操作实现类接口类,声明数据库操作实现类的接口。
* @author 徐敏荣
* @date 2012-06-14
*
* @par 修订历史
* @version v0.5 \n
* @author 徐敏荣
* @date 2012-06-14
* @li 初始版本
* @{
*/

#include "DB/HiDB.h"
class HiCritical;
/**
* @brief 数据库操作实现类接口类,声明数据库操作实现类的接口
*
*/
class HiDBImpl
{
public:

/**
* @brief 构造函数
* @param[in] isUsingLock 是否需要使用互斥锁
*/
HiDBImpl(bool isUsingLock);

/**
* @brief 析构函数
*/
virtual ~HiDBImpl();

public:

/**
* @brief 打开数据库连接
* @param[in] conn 数据库连接字符串
* @retval true:成功,false;失败
*/
virtual bool Open(const char* conn) = 0;

/**
* @brief 关闭据库连接
*/
virtual void Close(void) = 0;

public:

/**
* @brief 执行SQL语句,并不返回结果
* @param[in] conn SQL语句
* @retval true:成功,false;失败
*/
virtual bool ExecuteNoQuery(const char* sql) = 0;

public:

/**
* @brief 执行SQL语句,返回一个结果
* @param[in] sql SQL语句
* @param[out] value 取得的结果
* @retval true:成功,false;失败
*/
virtual std::string ExecuteScalar(const char* sql) = 0;
public:

/**
* @brief 执行SQL语句,返回一个结果集合
* @param[in] sql SQL语句
* @param[out] table 取得的结果集合
* @retval true:成功,false;失败
*/
virtual std::shared_ptr<HiDBTable> ExecuteQuery(const char* sql) = 0;

public:

/**
* @brief 事物处理
* @retval true:成功,false;失败
*/
virtual void OnTransaction(const std::function<void()>& fun) = 0;

protected:

/**
* @brief 临界区对象,为空表示不需要考虑资源并发访问
*/
HiCritical* m_pCritical;
};

/**//** @}*/ // 数据库操作实现类接口类

(四)HiDB的实现:

由HiDB负责实现可变参数转换为完整SQL语句,HiDBImpl负责实现所有数据库访问逻辑,并要为以后添加其他数据库支持这些需求可以推到出HiDB的实现代码:

#include <stdarg.h>
#include "DB/HiDB.h"
#include "HiDBMySQL.h"

using namespace std;

#if !defined(HISDB_ON_VARLIST)
#define HISDB_ON_VARLIST(x, y) char chArr[2048] = {0};char* pchar = &chArr[0];va_list pArgList;va_start(pArgList, y);::_vsnprintf(chArr, 2047, x, pArgList); va_end(pArgList) ;
#endif

static bool IsImplOK(HiDBImpl* db)
{
if (!db)
{
return false;
}
/*
if (!db->IsOpen())
{
return false;
}*/
return true;
}

// 构造函数
HiDB::HiDB(HiDBType type, bool isUsingLock):m_Impl(NULL)
{
if (type == HiDBType_MySQL)
{
this->m_Impl = new HiDBMySQL(isUsingLock);
}
}

// 析构函数
HiDB::~HiDB()
{
if (this->m_Impl)
{
delete this->m_Impl;
this->m_Impl = NULL;
}
}

// 打开数据库连接
bool HiDB::Open(const char* conn)
{
if (!this->m_Impl)
{
return false;
}

return this->m_Impl->Open(conn);
}

bool HiDB::IsOpen()
{
if (!this->m_Impl)
{
return false;
}

return true;//this->m_Impl->IsOpen();
}

void HiDB::Close(void)
{
if (!IsImplOK(this->m_Impl))
{
return;
}

return this->m_Impl->Close();
}

bool HiDB::ExecuteNoQuery(const char* sql, ...)
{
if (!IsImplOK(this->m_Impl))
{
return false;
}

HISDB_ON_VARLIST(sql, sql);

return this->m_Impl->ExecuteNoQuery(chArr);
}

string HiDB::ExecuteScalar(const char* sql, ...)
{
if (!IsImplOK(this->m_Impl))
{
return "";
}

HISDB_ON_VARLIST(sql, sql);

return this->m_Impl->ExecuteScalar(chArr);
}

std::shared_ptr<HiDBTable> HiDB::ExecuteQuery(const char* sql, ...)
{
if (!IsImplOK(this->m_Impl))
{
return NULL;
}
HISDB_ON_VARLIST(sql, sql);

return this->m_Impl->ExecuteQuery(chArr);
}

void HiDB::OnTransaction(const std::function<void()>& fun)
{
if (!IsImplOK(this->m_Impl))
{
HiDBHelperOnError("HiDB::OnTransaction",
"HiDB is not impl", "", 0);
}
return this->m_Impl->OnTransaction(fun);
}

四 后记

至此,HiDB所有主要的接口和主要的实现就介绍的差不多,其他更多的实现,可以参照源码自己实现。类图将在本文后面提供。

后续的工作包括对HiDB进行测试,我期望能进行自动化测试,类似于Junit。但是我并不知道C++有哪些自动化测试工具,没办法,只有自己写C++自动化测试工具来测试HiDB了,后面的文章我打算介绍一下我写的一个C++自动化测试工具。

HiDB类图:

源代码:http://download.csdn.net/detail/xumingxsh/7778417

我用“逆水行船”账号发布不到首页,就用这个账号发布吧。

这个程序自己陆陆续续编写修改了好几年,我就不信没人看,没人觉得有用。

学习实践:使用模式,原则实现一个C++数据库访问类

时间: 2024-10-24 22:41:33

学习实践:使用模式,原则实现一个C++数据库访问类的相关文章

一个C#的XML数据库访问类

原文地址:http://hankjin.blog.163.com/blog/static/33731937200942915452244/ 程序中不可避免的要用到配置文件或数据,对于数据量比较小的程序,部署数据库花费的时间就显得浪费了,因此用XML来存储不妨为一个很好的办法,而且结合C#的DataSet,我们可以很轻易的封装出一个代码简单而功能强大的数据访问类XMLConfigconfig.xml<root>  <table1>    <rowName1>hello&l

分享一个PHP数据库分页类

本帖最后由 luenmicro 于 2014-11-12 23:19 编辑 分享一个PHP数据库分页类. [code]<?php    class page    {        private $pagesize;        private $lastpage;        private $totalpages;        private $nums;        private $numPage=1; function __construct($page_size,$tota

自己封装的一个mysql数据库工具类

<?php class SqlHelper{ public $conn; //连接资源变量 public $host; //主机 public $user;  //用户名 public $password; //密码 public $db; //数据库 function __construct($host="localhost",$user="root",$password,$db){ $this->host=$host; $this->user=

学习实践:使用模式,原则实现一个C++自动化测试程序

个人编程中比较喜欢重构,重构能够提高自己的代码质量,使代码阅读起来也更清晰.但是重构有一个问题,就是如何保证重构后带代码实现的功能与重构前的一致,如果每次重构完成后,对此不闻不问,则会有极大的风险,如果每次重构后,都进行一边测试,则工作量会很巨大,最终可能是即使代码有重构的欲望,也会尽量克制住,不去重构.除非代码能够进行自动化测试.实际上进行测试的是接口,而不是所有代码,只要能够保持接口不变,自动化测试的工作量也没有想象中的巨大.其实我们在单元测试的时候,会测试各种异常情况,只不过,没有将这些测

《Head First 设计模式》学习笔记——模板方法模式

模板方法模式是类的行为模式.准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑.不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现.这就是模板方法模式的用意. 设计模式 模板方法模式:在一个方法中定义一个算法的框架,而将一些步骤延迟到子类中.模板方法使得子类可以在不改变算法结果的情况下,重新定义算法中的某些步骤. 模板就是一个方法,这个方法将算法定义成一组步骤,其中的任何步骤都可以是抽象的,由子类负责实现.这样可以确

设计模式学习笔记--外观模式

好久没写设计模式的blog了,这次重新回来填坑,先找一个最简单但是却最常用的设计模式来学习,外观模式.其实说是一个设计模式,其实我们在实际的编程中无时无刻不在用外观模式,可以说这个设计模式已经渗透到编程的各个方便,可能我们自己没感觉出来罢了. 一.外观模式的定义 先来看一下外观模式的定义: 外观模式(Facade),为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层的接口,这个接口使得这一系列子系统更加容易使用. 简单解释一下,所谓外观模式,就是在我们设计系统的时候,将若干个子系统的功

设计模式学习02—工厂模式

1.动机与定义 我们在程序中使用一个对象时,需要new一下,如果需要设置其他值就再初始化一下.比如我要使用一个按钮,手动new一个矩形按钮,然后初始化一些值,如显示文字,背景色等. // 矩形按钮 IButton btn = new RecButton(); // 初始化其他值 btn.setText("提交"); btn.setBackgroundColor("#00aaff"); // 其他初始化省略 // 圆形按钮 IButton btn2 = new Rou

前端学习实践笔记--JavaScript深入【1】

这一年中零零散散看过几本javascript的书,回过头看之前写过的javascript学习笔记,未免有点汗颜,突出“肤浅”二字,然越深入越觉得javascript的博大精深,有种只缘身在此山中的感觉,茫茫然而不得其要领,索性在一边写博文中,求得突破,乃至更上一层楼. 看过的书籍推荐: <javascript语言精粹> Douglas Crockford <javascript设计模式> Addy Osmani <javascript设计模式> Ross Harmes

设计模式学习05—原型模式

一.动机与定义 之前学习原型模式一直以为原型模式目的是为了方便的创建相同或相似对象,用复制对象的方式替换new的方式,还研究了深克隆和浅克隆.最近仔细看了GOF的设计模式,发现原型模式的本意并不仅仅是复制对象这么简单. 复制对象确实是一方面,当我们需要大量相似,甚至相同对象的时候,除了一个个的new之外,还可以根据一个原型,直接复制出更多的对象.但是如果把原型模式认为只是复制对象这么简单就错了. 创建型模式主要讲如何创建对象,通常包含何时创建,谁来创建,怎么创建等.GOF书里面写的意图是,用原型