读写excel文档入门讲解一

最近工作中需要将数据以各种格式存放到excel文档中,工程是基于C++,并且采用QT设计的界面,因此,通过网络搜索寻找到基于QT来读写excel文档的开源库QXlsx,本文将介绍开源库QXlsx的简单使用和效果,一方面提供入门知识,另一方面也给自己的学习成果做一个总结记录,方便后续查找复习。

一、简单的例子

1、 网上下载QXlsx开源库后,需要将QXlsx加载到Qt Creator工具,直接参考开源库代码中的HowToSetProject.md文件即可

2、展示写入数据,并且生成excel文档的例子

// 头文件
#include "xlsxdocument.h"
#include "xlsxformat.h"
#include "xlsxworkbook.h"

QXlsx::Document xlsx;
// 通过单元格名写入字符串
xlsx.write("A1", "Hello!");
// 通过行列方式写入字符串
xlsx.write(2, 1, "Everyone!");

std::string str_file_name = JPathHandler::GetDebugPath() + "excel_hello.xlsx";
// 生成excel文档
xlsx.saveAs(QString::fromStdString(str_file_name));

3、最后生成的excel文档的内容如下图所示

二、自动换行

有时候写入单元格的内容比较长,超过了单元格的展示范围,而我们又不想扩大单元格的宽度,怎么办呢,可以通过设置自动换行的方式,让写入单元格的内容自动适配当前的单元格。

1、携带配置好的格式写入到单元格

QXlsx::Document xlsx2;

// 初始化格式
QXlsx::Format format2;
format2.setFontSize(10);
format2.setHorizontalAlignment(QXlsx::Format::AlignHDistributed);

// 用上面的格式来初始化下面的单元格
xlsx2.write("A1", "onetwothree,onetwothree,onetwothree", format2);

// 最后生成excel文档(注意JPathHandler::GetDebugPath()自定义的类,用于获取路径,不是开源库的接口)
std::string str_file_name2 = JPathHandler::GetDebugPath() + "excel_align.xlsx";
xlsx2.saveAs(QString::fromStdString(str_file_name2));

2、最后生成的excel文档的内容效果如下图所示

三、多个表单

当需要多个表单来保存不同的数据的时候,这就涉及到创建多个表单的问题。那么将用创建两个表单的例子来作为简单的入门知识点。

1、生成两个表单,并且分别往表单中写入数据

QXlsx::Document xlsx3;

// 第一个表单
xlsx3.addSheet("first year");
xlsx3.write("A1", "first");

// 第二个表单
xlsx3.addSheet("second year");
xlsx3.write("A1", "second");

// 最后生成excel文档
std::string str_file_name3 = JPathHandler::GetDebugPath() + "excel_addsheet.xlsx";
xlsx3.saveAs(QString::fromStdString(str_file_name3));

2、最后生成的excel文档的内容效果如下图所示

四、读取excel文档

写入数据到excel文档之后,肯定有需求场景需要读取excel文档数据来展示,所以,接下来将说明读取excel文档的所有数据的方法。

1、先读取excel文档,然后获取每一个表单,读取每一个表单中的单元格内容

// 读取上一个步骤中生成的excel文档
QXlsx::Document xlsx4( QString::fromStdString(str_file_name3) );

int i_index_sheet = 0;
// 循环读取excel中的每一个表单,最后打印输出每个单元格的内容
foreach( QString current_sheetname, xlsx4.sheetNames() )
{
    QXlsx::AbstractSheet* p_current_sheet = xlsx4.sheet( current_sheetname );
    p_current_sheet->workbook()->setActiveSheet(i_index_sheet);
    QXlsx::Worksheet* wsheet = (QXlsx::Worksheet*) p_current_sheet->workbook()->activeSheet();

    int i_max_row = -1;
    int i_max_col = -1;
    QVector<QXlsx::CellLocation> clList = wsheet->getFullCells( &i_max_row, &i_max_col );
    for ( int i_cellindex = 0; i_cellindex < clList.size(); ++i_cellindex )
     {
        QXlsx::CellLocation cell_location = clList.at(i_cellindex);
        QVariant var = cell_location.cell.data()->value();
        QString str = var.toString();
        LOG(INFO)<< std::string( str.toLocal8Bit() );
    }
    i_index_sheet++;
}

2、最后输出的打印信息如下所示

[2019-10-20 21:09:48,039626] [bool JQtAttr::TestXlsx():135] first
[2019-10-20 21:09:48,039675] [bool JQtAttr::TestXlsx():135] second

五、总结

从上面的例子中,可以看出QXlsx::Document是控制整个excel文档的类,它可以控制表单的宽高,控制表单等功能,而QXlsx::Format则是控制单元格格式的类,它可以设置字体、字体大小,对齐方式等。

QXlsx的局限性,它不支持多线程,当表单比较多的时候,加载表单会很耗时。

参考链接:

Handling Microsoft Excel file format

QXlsx

 

设计模式详解十一:Flyweight模式

flyweight意思是轻量级,该模式主要为了解决有些场景会产生许多对象,并且其中的许多对象都是相同的。flyweight提供创建对象接口,并且内部保存对象池,当请求的对象没有在对象池中,那么重新创建对象放入对象池,如果请求的对象存在对象池中,那么直接从对象池取出对象返回给请求者,从而避免对象的频繁创建,造成系统开销的问题。

一、设计模式的结构图

Flyweight模式,FlyweightFactory根据key值提供获取Flyweight对象的接口,

二、程序代码实现

1、基类Flyweight的定义和实现

/// 类的定义
class Flyweight
{
public:
    virtual ~Flyweight();
    virtual void Operation(const std::string& strParam);
    std::string GetState();
    
protected:
    Flyweight(std::string strState);
private:
    std::string m_strState;
};

/// 类的实现
Flyweight::Flyweight(std::string strState)
{
    this->m_strState = strState;
}

Flyweight::~Flyweight()
{
}

void Flyweight::Operation(const std::string& strParam)
{
}
string Flyweight::GetState()
{
    return this->m_strState;
}

2、子类SubFlyweight的定义和实现

/// 类的定义
class SubFlyweight:public Flyweight
{
public:
    SubFlyweight(std::string strState);
    ~SubFlyweight();
    
    void Operation(const std::string& strParam);

protected:
private:
};


/// 类的实现
SubFlyweight::SubFlyweight(std::string strState):Flyweight(strState)
{
    DEBUG_LOG("SubFlyweight Build..... ", strState);
}

SubFlyweight::~SubFlyweight()
{
}

void SubFlyweight::Operation(const string& strParam)
{

}

3、FlyweightFactory工厂类的定义和实现

/// 类的定义
class FlyweightFactory
{
public:
    FlyweightFactory();
    ~FlyweightFactory();
    Flyweight* GetFlyweight(const std::string& key);
    
protected:
private:
    vector<Flyweight*> m_fly;
};


/// 类的实现
FlyweightFactory::FlyweightFactory()
{
}

FlyweightFactory::~FlyweightFactory()
{
}

Flyweight* FlyweightFactory::GetFlyweight(const std::string& key)
{
    vector<Flyweight*>::iterator it = m_fly.begin();
    for (; it != m_fly.end();it++)
    {
        if ((*it)->GetState() == key)
        {
            cout<<"already created by users...."<<endl;
            return *it;
        }
    }
    Flyweight* fw = new SubFlyweight(key);
    m_fly.push_back(fw);
    return fw;
}

三、测试验证结果

1、主程序输入如下所示的测试代码

FlyweightFactory* fc = new FlyweightFactory();
Flyweight* fw1 = fc->GetFlyweight("li");
Flyweight* fw2 = fc->GetFlyweight("zheng!");
Flyweight* fw3 = fc->GetFlyweight("li");

2、运行结果如下所示,第三次请求对象,因为已经存在在对象池中,所以直接从对象池中获取对象并返回

[2019-09-14 19:54:45.160  SubFlyweight:31]  SubFlyweight Build.....  = li
[2019-09-14 19:54:45.160  SubFlyweight:31]  SubFlyweight Build.....  = zheng!
already created by users....

四、总结

FlyweightFactory类采用vector存储对象,当然需要根据具体场景来选择合适的容器,比如,map、hash表等。需要注意的是FlyweightFactory查找的key值是存放在Flyweight对象中的成员变量。

设计模式详解十:外观模式

外观模式,即Facade模式,封装了内部细节,对外提供统一接口。比如做一件事的时候,需要经过几个步骤,但是使用者并不关心内部细节,因此,这时候就可以采用外观模式,将几个步骤封装起来,然后提供一个统一的接口。

一、设计模式的结构图

外观模式对外提供统一接口OperationWrapper, 内部封装两个子系统的接口。

二、程序代码实现

1、定义和实现子系统类1

/// 类的定义
class Subsystem1
{
public:
    Subsystem1();
    ~Subsystem1();
    void Operation();
    
protected:
private:
};


/// 类的实现
Subsystem1::Subsystem1()
{
}

Subsystem1::~Subsystem1()
{
}

void Subsystem1::Operation()
{
    DEBUG_LOG("", "Subsystem1 operation...");
}

2、定义和实现子系统类2

/// 类的定义
class Subsystem2
{
public:
    Subsystem2();
    ~Subsystem2();
    void Operation();

protected:
private:
};


/// 类的实现
Subsystem2::Subsystem2()
{
}

Subsystem2::~Subsystem2()
{
}
void Subsystem2::Operation()
{
    DEBUG_LOG("", "Subsystem2 operation...");
}

3、定义和实现外观模式

/// 类的定义
class Facade
{
public: Facade();
    ~Facade();
    void OperationWrapper();

protected:
private:
    Subsystem1* m_subs1;
    Subsystem2* m_subs2;
};


/// 类的实现
Facade::Facade()
{
    this->m_subs1 = new Subsystem1();
    this->m_subs2 = new Subsystem2();
}

Facade::~Facade()
{
    delete m_subs1;
    delete m_subs2;
}

void Facade::OperationWrapper()
{
    this->m_subs1->Operation();
    this->m_subs2->Operation();
}

从外观模式类看,它负责控制两个子系统类的创建和销毁,对外提供统一接口,内部实现调用两个子系统的函数。

三、测试验证结果

1、主程序输入如下所示的测试代码

Facade* facade = new Facade();
facade->OperationWrapper();

2、运行结果如下所示

[2019-09-08 16:16:18.993  Operation:21]   = Subsystem1 operation...
[2019-09-08 16:16:18.993  Operation:34]   = Subsystem2 operation...

四、总结

外观模式在实际中应用比较广泛,它通常采用单例模式实现,比如采用外观模式封装各个对象的创建过程。

设计模式详解九:迭代器模式

迭代器模式, 即iterator模式,封装集合对象,提供访问集合的接口。

一、设计模式的结构图

迭代器模式的处理逻辑是:iterator迭代器内部保存持有集合对象,用于访问集合,而集合提供创建返回迭代器的函数接口。

二、程序代码实现

1、 定义实现迭代器的基类

#ifndef Object
typedef int Object;
#endif


/// 类定义
class Iterator
{
public:
    virtual ~Iterator();
    virtual void First() = 0;
    virtual void Next() = 0;
    virtual bool IsDone() = 0;
    virtual Object CurrentItem() = 0;
    
protected:
    Iterator();
private:
};


/// 类实现

Iterator::Iterator()
{
    
}

Iterator::~Iterator()
{
    
}

2、 定义实现迭代器的子类

/// 类的定义
class Aggregate;
class ConcreteIterator : public Iterator
{
public:
    ConcreteIterator();
    ConcreteIterator(Aggregate *ag, int idx = 0);
    ~ConcreteIterator();
    void First();
    void Next();
    bool IsDone();
    Object CurrentItem();

protected:
private:
    Aggregate* m_ag;
    int m_idx;
};

/// 类的实现
ConcreteIterator::ConcreteIterator()
{
    
}

ConcreteIterator::ConcreteIterator(Aggregate *ag, int idx)
{
    this->m_ag = ag;
    this->m_idx = idx;
}

ConcreteIterator::~ConcreteIterator()
{
}

Object ConcreteIterator::CurrentItem()
{
    return m_ag->GetItem(m_idx);
}

void ConcreteIterator::First()
{
    m_idx = 0;
}

void ConcreteIterator::Next()
{
    if (m_idx < m_ag->GetSize())
    {
        m_idx++;
    }
}

bool ConcreteIterator::IsDone()
{
    return (m_idx == m_ag->GetSize());
}

3、定义实现集合的基类

#ifndef Object
typedef int Object;
#endif

/// 类的定义
class Iterator;
class Aggregate
{
public:
    virtual ~Aggregate();
    virtual Iterator* CreateIterator() = 0;
    virtual Object GetItem(int idx) = 0;
    virtual int GetSize() = 0;
    
protected:
    Aggregate();
private:
};


/// 类的实现
Aggregate::Aggregate()
{
}

Aggregate::~Aggregate()
{
}

4、定义实现集合的子类

/// 类的定义
class ConcreteAggregate:public Aggregate
{
public:
    enum {SIZE = 3};
    ConcreteAggregate();
    ~ConcreteAggregate();
    Iterator* CreateIterator();
    Object GetItem(int idx);
    int GetSize();
    
protected: private:
    Object m_objs[SIZE];
};


/// 类的实现
ConcreteAggregate::ConcreteAggregate()
{
    for (int i = 0; i < SIZE; i++)
        m_objs[i] = i;
}

ConcreteAggregate::~ConcreteAggregate()
{
}

Iterator* ConcreteAggregate::CreateIterator()
{
    return new ConcreteIterator(this);
}

Object ConcreteAggregate::GetItem(int idx)
{
    if (idx < this->GetSize())
    {
        return m_objs[idx];
    }
    else
    {
        return -1;
    }
}

int ConcreteAggregate::GetSize()
{
    return SIZE;
}

 

三、测试验证结果

1、主程序输入如下所示的测试代码

Aggregate* ag = new ConcreteAggregate();
Iterator* it = new ConcreteIterator(ag);
for (; !(it->IsDone()) ; it->Next())
{
    std::cout<< "item = "<< it->CurrentItem()<<std::endl;
}

2、运行结果如下所示,迭代器访问集合内的所有数据,并输出显示

item = 0
item = 1
item = 2

四、总结

迭代器的实现过程中,为了避免集合过多的暴露公有接口,可以将迭代器声明为集合的友元,来访问集合的私有数据。另外,从上面的代码实现中,可以看出集合提供创建迭代器的接口,而迭代器持有集合对象,内部包装集合的访问接口。

C++11:启动线程的五种方式

程序开发过程中,面对各种各样的需求场景,其中涉及到线程的应用,本文将总结启动线程的五种方式,方便后续复习以及查阅。

  1. 函数指针方式启动线程
  2. 函数对象方式启动线程
  3. Lambda函数的方式启动线程
  4. 类函数指针的方式启动线程
  5. std::bind的方式启动线程

一、函数指针方式

1、 定义实现线程处理函数,然后启动线程调用该处理函数

void ThreadFunction(void)
{
    std::cout << __func__ << ": thread executing "  << std::endl;
}

// 函数指针方式启动线程
void JTestThread::TestStartThread_Method01(void)
{
    std::thread thread_obj(ThreadFunction);
    if (thread_obj.joinable())
    {
        thread_obj.join();
    }
}

2、 运行结果

ThreadFunction: thread executing

二、函数对象方式

1、创建类ThreadOpeartor,然后重载operator()运算符,然后以仿函数的形式作为线程的参数来启动线程

class ThreadOpeartor
{
public:
    void operator()()
    {
        std::cout << __func__ << ":  thread executing "  << std::endl;
    }
    
};

// 函数对象方式启动线程
void JTestThread::TestStartThread_Method02(void)
{
    std::thread thread_obj( (ThreadOpeartor()) ) ;
    if (thread_obj.joinable())
    {
        thread_obj.join();
    }
}

2、运行结果

operator():  thread executing

三、lambda方式

1、lambda函数即匿名函数作为线程的参数来启动线程

// Lambda函数的方式启动线程
void JTestThread::TestStartThread_Method03(void)
{
    std::thread thread_obj( []{
        std::cout << __func__ << ": lambda thread executing" << std::endl;
    } ) ;
    if (thread_obj.joinable())
    {
        thread_obj.join();
    }
}

2、运行结果

operator(): lambda thread executing

四、类函数指针方式

1、定义类成员函数,然后以函数指针作为参数来启动线程

void JTestThread::TestThread04(void)
{
    std::cout << __func__ << ": thread executing" << std::endl;
}

// 类函数指针的方式启动线程
void JTestThread::TestStartThread_Method04(void)
{
    std::thread thread_obj(&JTestThread::TestThread04, this) ;
    if (thread_obj.joinable())
    {
        thread_obj.join();
    }
}

2、运行结果

TestThread04: thread executing

五、std::bind方式

1、类成员函数作为stb::bind的参数, 然后stb::bind作为线程参数来启动线程

void JTestThread::TestThread05(void)
{
    std::cout << __func__ << ": thread executing" << std::endl;
}

// std::bind的方式启动线程
void JTestThread::TestStartThread_Method05(void)
{
    std::thread thread_obj(std::bind(&JTestThread::TestThread05, this)) ;
    if (thread_obj.joinable())
    {
        thread_obj.join();
    }
}

2、运行结果

TestThread05: thread executing

六、总结

  • 函数指针方式:定义普通函数
  • 函数对象方式:重载operator()运算符
  • Lambda函数方式:匿名函数
  • 类函数指针的方式:对象函数指针
  • std::bind的方式: 实际上也是对象函数指针

七、参考链接

如何正确创建线程

怎样通过函数对象创建独立线程

设计模式详解八:Starategy模式

strategy策略模式就是实现和抽象解耦,逻辑处理放到Context中,并在逻辑处理中调用抽象接口,而组合对象实现具体的内容。

一、设计模式的结构图

策略模式context实现主要逻辑,具体实现放到strategy类,strategy是抽象基类,子类继承该基类,实现具体的功能,context与strategy是一对多的组合关系,这样实现的好处就是context可以支持多个strategy类型对象,strategy类型也可以复用到其他不同的逻辑算法。

二、程序代码实现

1、定义实现抽象基类Strategy

class Strategy
{
public:
    Strategy();
    virtual ~Strategy();
    virtual void AlgrithmInterface() = 0;

protected:
private:
};

Strategy::Strategy()
{
}

Strategy::~Strategy()
{
    DEBUG_LOG("", "~Strategy...");
}

void Strategy::AlgrithmInterface()
{
}

2、定义实现基类Strategy的子类ConcreteStrategyA

class ConcreteStrategyA:public Strategy
{
public:
    ConcreteStrategyA();
    virtual ~ConcreteStrategyA();
    void AlgrithmInterface();

protected:
private:
};

ConcreteStrategyA::ConcreteStrategyA()
{
}

ConcreteStrategyA::~ConcreteStrategyA()
{
    DEBUG_LOG("", "~ConcreteStrategyA...");
}

void ConcreteStrategyA::AlgrithmInterface()
{
    DEBUG_LOG("", "ConcreteStrategyA::AlgrithmInterface...");
}

3、定义实现基类Strategy的子类ConcreteStrategyB

class ConcreteStrategyB:public Strategy {
public:
    ConcreteStrategyB();
    virtual ~ConcreteStrategyB();
    void AlgrithmInterface();
    
protected:
private:
};

ConcreteStrategyB::ConcreteStrategyB()
{
}

ConcreteStrategyB::~ConcreteStrategyB()
{
    DEBUG_LOG("", "~ConcreteStrategyB...");
}

void ConcreteStrategyB::AlgrithmInterface()
{
    DEBUG_LOG("", "ConcreteStrategyB::AlgrithmInterface...");
}

4、定义实现Context类

class Context
{
public:
    Context(Strategy* stg);
    ~Context();
    void DoAction();
protected:
private:
    Strategy* m_stg;
};

Context::Context(Strategy* stg)
{
    m_stg = stg;
}

Context::~Context()
{
    if (!m_stg)
        delete m_stg;
}
void Context::DoAction()
{
    m_stg->AlgrithmInterface();
}

三、测试验证结果

1、主程序输入如下所示的测试代码

Strategy* p_strategy;
p_strategy = new ConcreteStrategyA();
Context* p_context = new Context(p_strategy);
p_context->DoAction();
if (NULL != p_context)
{
    delete p_context;
    p_context = NULL;
}

2、运行结果如下图所示,Context对象内部最终调用ConcreteStrategyA对象的算法功能。

[2019-06-30 14:30:24.680  AlgrithmInterface:35]   = ConcreteStrategyA::AlgrithmInterface...

四、总结

策略模式,不变的逻辑算法放到Context对象中实现,而动态变化的算法实现则由Strategy子类来实现,这里有一点需要注意的是Context析构的时候,也会析构Strateby类型的对象,所以,外部定义的Strateby对象不需要再次进行delete。

设计模式详解七:Adapter模式

Adapter模式就是当已经设计好的接口与第三方库的接口不一致的时候,可以起到兼容的作用。Adapter模式一般分为:类模式(继承方式)和对象模式(组合方式)。

一、设计模式的结构图

1、类模式是采用继承的方式来复用Adaptee的接口

2、对象模式是采用组合的方式来调用Adaptee接口

二、程序代码实现

1、定义和实现用户的接口Target

//定义用户接口
class Target
{
public:
    Target();
    virtual ~Target();
    virtual void Request();
    
protected:
private:
};

//实现用户接口
Target::Target()
{
}

Target::~Target()
{
}

void Target::Request()
{
    DEBUG_LOG("", "Target::Request...");
}

2、 定义和实现第三方库接口Adaptee

//第三方
class Adaptee
{
public:
    Adaptee();
    ~Adaptee();
    void SpecificRequest();

protected:
private:
};


Adaptee::Adaptee()
{
}

Adaptee::~Adaptee()
{
}

void Adaptee::SpecificRequest()
{
    DEBUG_LOG("", "Adaptee::SpecificRequest...");
}

3、定义和实现类模式的适配器

//适配器1
class AdapterOne:public Target,private Adaptee
{
public:
    AdapterOne();
    ~AdapterOne();
    void Request();
    
protected:
private:
};


AdapterOne::AdapterOne()
{
}

AdapterOne::~AdapterOne()
{
}

void AdapterOne::Request()
{
    this->SpecificRequest();
}

4、定义和实现对象模式的适配器

//适配器2
class AdapterTwo:public Target
{
public:
    AdapterTwo(Adaptee* ade);
    ~AdapterTwo();
    void Request();

protected:
private:
    Adaptee* _ade;
};


AdapterTwo::AdapterTwo(Adaptee* ade)
{
    this->_ade = ade;
}

AdapterTwo::~AdapterTwo()
{
}

void AdapterTwo::Request()
{
    _ade->SpecificRequest();
}

三、测试验证结果

1、主函数输入如下所示的测试代码段,用来测试对象模式的适配器模式

Adaptee* ade = new Adaptee;
Target* adt = new AdapterTwo(ade);
adt->Request();

2、运行结果如下图所示,调用Request之后,内部实际上调用的是第三方库接口

[2019-06-23 13:51:20.713  SpecificRequest:36]   = Adaptee::SpecificRequest...

四、总结

适配器顾名思义就是一个转接口,将目标对象和第三方连接起来,Target与Adapter之间存在多态的特性,而Adapter内部调用Adaptee完成实际的功能。

设计模式详解六:装饰者模式

Decorator模式,即装饰者模式是通过组合的方式给类添加新的职责。通过组合的好处是不会增加类的继承深度。

一、设计模式的结构图

结构图中,SubComponent和Decorator都继承自同一个基类Component, SubComponet继承自Component基类,这个体现多态的特性,大家应该都明白。而Decorator也继承自Component基类是为什么呢?我的理解是,Decorator是为component添加新的职责,它应该是属于Component,后续才能同SubComponent一样的方式进行使用。Decorator作为SubDecorator的基类,是为了能够提供更多的修饰,比如后续有新的修饰SubDecorator2,那么原来的实现的接口是不需要修改的。

二、程序代码实现

1、定义和实现Component基类

class Component
{
public:
    virtual ~Component();
    virtual void Operation();
protected:
    Component();
private:
};


Component::Component()
{
}

Component::~Component()
{
}

void Component::Operation()
{
}

2、定义和实现SubComponent

class SubComponent:public Component
{
public:
    SubComponent();
    ~SubComponent();
    void Operation();

protected:
private:
};

SubComponent::SubComponent() {}

SubComponent::~SubComponent() {}

void SubComponent::Operation()
{
    DEBUG_LOG("", "SubComponent operation..." );
}

3、定义和实现Decorator类

class Decorator:public Component
{
public:
    Decorator(Component* com);
    virtual ~Decorator();
    void Operation();
    
protected:
    Component* m_com;
private:
};


Decorator::Decorator(Component* com)
{
    this->m_com = com;
}

Decorator::~Decorator()
{
    delete m_com;
}

void Decorator::Operation()  {}

4、定义和实现SubDecorator

class SubDecorator:public Decorator
{
public:
    SubDecorator(Component* com);
    ~SubDecorator();
    void Operation();
    void AddedBehavior();
    
protected:
private:
};


SubDecorator::SubDecorator(Component* com):Decorator(com)  {}

SubDecorator::~SubDecorator()  {}

void SubDecorator::AddedBehavior()
{
    DEBUG_LOG("", "SubDecorator::AddedBehacior...." );
}

void SubDecorator::Operation()
{
    m_com->Operation();
    this->AddedBehavior();
}

三、测试验证结果

程序输入如下所示的代码段,然后运行调试查看结果:

Component* com = new SubComponent();
Decorator* dec = new SubDecorator(com);
dec->Operation();
//com在dec在析构函数中释放
delete dec;

运行结果如下所示,SubDecorator内部调用SubComponent后,并且为SubComponent添加新的行为。

[2019-06-02 16:20:02.135  Operation:29]   = SubComponent operation...
[2019-06-02 16:20:02.135  AddedBehavior:53]   = SubDecorator::AddedBehacior....

四、总结

Decorator模式提供了一种添加职责的方式,采用组合的方式,因此,当需要添加操作的时候,可以考虑Decorator模式来解决。

备注:Decorator与Component之间是整体与部分的关系,一个Decorator可以对应多个Component对象。

设计模式详解五:Factory模式


创建Factory模式(工厂模式),主要有两个目的,(1)如果一个基类有很多的子类,那么可以通过Factory模式统一创建子类对象, 这样可以达到方便维护,提高扩展性的目的; (2) 将具体化类的工作延迟到子类中,举个例子:父类A会调用到类B,进行逻辑的判断处理,但是类B是一个抽象类,类A不知道类B的具体类是什么,当创建类A的子类C就可以知道类B的子类D,这样就可以达到将不变的逻辑放在父类处理,而将具体的实现放到子类中。

一、设计模式的结构图

1、Factory类提供创建类Product的接口CreateProduct,这是工厂模式的第一个作用:负责维护众多类的创建。

2、Factory类是抽象类,Product也是抽象类,可以在抽象类中做公共的处理,而具体的实现工作延迟到子类,这就是工厂模式的第二个作用。

二、程序代码实现

首先定义和实现生产基类和子类

1、定义生产基类和它的子类

class Product;

//生产基类
class Product
{
public:
    virtual ~Product() = 0;
    
protected:
    Product();
    
private:
};

//生产子类
class SubProduct:public Product
{
public:
    ~SubProduct();
    SubProduct();
    
protected:
private:
};

2、实现生产基类和它的子类

Product::Product()
{
}

Product::~Product()
{
}

SubProduct::SubProduct()
{
    DEBUG_LOG("", "SubProduct...");
}

SubProduct::~SubProduct()
{
}

 

下面是定义和实现工厂的基类和它的子类

1、定义工厂的基类和它的子类

//工厂基类
class Factory
{
public:
    virtual ~Factory() = 0;
    virtual Product* CreateProduct() = 0;
    
protected:
    Factory();
    
private:
};

//工厂子类
class SubFactory:public Factory
{
public:
    ~SubFactory();
    SubFactory();
    
    Product* CreateProduct();
protected:
    
private:
};

2、实现工厂的基类和它的子类

Factory::Factory()
{
}

Factory::~Factory()
{
}

SubFactory::SubFactory()
{
    DEBUG_LOG("", "SubFactory...");
}

SubFactory::~SubFactory()
{
}

Product* SubFactory::CreateProduct()
{
    return new SubProduct();
}

三、测试验证结果

程序输入如下所示的代码段,然后运行调试查看结果:

Factory *fac = new SubFactory();
Product *product = fac->CreateProduct();

delete fac;
delete product;

运行结果如下所示,构造具体的工厂对象,然后工厂对象创建具体的生产对象。

[2019-05-25 14:03:49.814  SubFactory:45]   = SubFactory...
[2019-05-25 14:03:49.814  SubProduct:27]   = SubProduct...

四、总结

工厂模式可以解决两个问题,第一个是负责对象的创建,第二个是将具体类的工作延迟到子类中。上面的示例中,通过SubFactory调用CreateProduct创建具体类SubProduct,如果这时候又要创建一个新的类SubProduct2,  那么有得在SubFactory中创建方法CreateProduct2来创建具体类SubProduct2,这样类SubFactory就封闭不了,那么解决的办法就是创建Factory的一个新的子类SubFactory2。另一个方式就是CreateProduct方法提供一个参数,然后通过参数来决定创建具体的Product。

设计模式详解四:Bridge模式

Bridge模式是将两个结合的对象进行解耦,以便达到一个对象的修改不会影响到另一个对象的目的。比如,不同操作系统和不同算法的两个对象,操作系统可以调用不同的算法,而不同的算法可以运行在不同的操作系统上。这里操作系统我们认为是抽象部分,算法是实现部分。

一、设计模式的结构图

Abstraction对应操作系统部分, Imp对应算法部分。Abstraction是操作系统的基类,SubAbstraction是该基类的子类,负责实现操作系统上的公共的部分。Imp是算法的基类,SubImpA和SubImpB是该基类的子类,负责实现算法功能。Abstraction和Imp之间是一对多的关系,一个操作系统可以支持多种算法。具体形象的结构图如下图:

二、程序代码实现

首先定义实现操作系统的部分。

1、定义操作系统的基类以及它的子类

//操作系统基类
class Abstraction
{
public:
    virtual ~Abstraction();
    virtual void Operation() = 0;

protected:
    Abstraction();
    private:
};

//具体操作系统的实现
class SubAbstraction:public Abstraction {
public:
    SubAbstraction(Imp* imp);
    ~SubAbstraction();
    void Operation();

protected:
    
private:
    Imp* m_imp;
};

2、实现操作系统的基类以及它的子类

Abstraction::Abstraction()
{
}
Abstraction::~Abstraction()
{
}


SubAbstraction::SubAbstraction(Imp* imp)
{
    m_imp = imp;
}

SubAbstraction::~SubAbstraction()
{
}

void SubAbstraction::Operation()
{
    m_imp->Operation();
}

然后定义实现算法的部分

1、定义算法的基类以及它的子类

//算法的基类
class Imp
{
public:
    virtual ~Imp();
    virtual void Operation() = 0;

protected:
    Imp();
private:
};

//具体算法的实现A
class SubImpA:public Imp
{
public:
    SubImpA(); ~SubImpA();
    virtual void Operation();
    
protected:
private:
};

//具体算法的实现B
class SubImpB:public Imp
{
public:
    SubImpB(); ~SubImpB();
    virtual void Operation();
    
protected:
private:
};

2、实现算法的基类以及它的子类

Imp::Imp()
{
}

Imp::~Imp()
{
}

void Imp::Operation()
{
    DEBUG_LOG("", "call Imp");
}


SubImpA::SubImpA()
{
}

SubImpA::~SubImpA()
{
}

void SubImpA::Operation()
{
    DEBUG_LOG("", "call SubImpA");
}


SubImpB::SubImpB()
{
}

SubImpB::~SubImpB()
{
}

void SubImpB::Operation()
{
    DEBUG_LOG("", "call SubImpB");
}

三、测试验证结果

主程序中输入如下的代码段,然后编译运行查看它的运行结果

Imp *imp = new SubImpA();
Abstraction *abs = new SubAbstraction(imp);
abs->Operation();

Imp *imp2 = new SubImpB();
Abstraction *abs2 = new SubAbstraction(imp2);
abs2->Operation();

运行结果如下所示,操作系统和算法的对象通过组合的方式结合,操作系统根据不同的算法对象调用不同的算法,而操作系统内部的处理逻辑是不需要改变的。

[2019-05-19 21:16:12.453  Operation:61]   = call SubImpA
[2019-05-19 21:16:12.453  Operation:73]   = call SubImpB

四、总结

Bridge模式的原理是采用组合的方式,将两个对象结合起来,两个对象各自实现内部的处理逻辑,但是对外接口保持不变。比如上面的操作系统和算法两个对象,操作系统结合C++的多态特性,调用算法的接口,而算法接口不变,根据不同的算法内部实现不同的算法处理逻辑。当增加新的操作系统的时候,算法不需要修改。当增加新的算法的时候,操作系统也不需要修改。真正实现两个对象解耦的目的。