浅谈设计模式

本文通过访问者模式、单例模式、工厂模式和责任链模式,展示了面向对象在实际工程中的应用。访问者模式解决了大量if-else判断的问题,提供了一种优雅的扩展方式。单例模式确保类只有一个实例,常用于服务器等场景。工厂模式简化了对象创建过程,隐藏了具体实现细节。责任链模式则允许对象形成一条处理链,简化了处理流程。
摘要由CSDN通过智能技术生成


我们一直所说C++是一个面向对象的语言,也了解它的三大基本特性:封装、继承和多态,但是这些和工程实践中的联系却是有一点距离,下面来通过四个设计模式来描述一下面向对象在工程实践中的应用。

访问者模式

情景举例
现在我们有一个基类A, 然后有多个子类B、C、D继承于A类,现在我们有一个存放A类指针的数组来存放每一个对象,对于每一个对象执行相应的类操作(这里简化直接输出子类的名字)。经过思考,之前的智能指针dynamic_cast可以胜任这样的工作。进行判断后,可以输出相应的信息,代码如下所示:

for (int i = 0; i < n; i++) {
        if (dynamic_cast<B *>(arr[i])) { // 通过虚函数表地址来判断是否是B类(指针的强制转换,要求必须是虚函数)
            cout << "this is B class" << endl;
        } else if (dynamic_cast<C *>(arr[i])) {
            cout << "this is C class" << endl;
        } else if (dynamic_cast<D *> (arr[i])) {
            cout << "this is D class" << endl;
        } else {
            cout << "error!" << endl;
        }
    }

这个代码没有问题,但是在工程中就有问题,因为每出现一个子类就需要有一个 if 判断,这显然是不太好的。另外,当我们创建一个新的子类 E 时,如果忘记加 if 判断,程序可以顺利执行,但是只会在运行期才会发现bug,即:“error”。 C++的一种思想就是尽量把错误显示在编译期间,所以有了访问者模式可以很好的解决这个问题。
访问者模式
在基类中A设置一个访问接口,即一个访问者类,将里面的访问函数设置成虚函数,并建立一个子类继承它,在子类里面重写访问函数(不同的对象执行不同的操作),同时在基类A里面设置一个调用接口类的函数,同样设置成虚函数。

class A {
public :
    class IVisitor { // 接口类
    public :
        virtual void visit(A *) = 0; // 设置成纯虚函数
        virtual void visit(B *) = 0;
    };
    virtual void Accept(IVisitor *vis) { // 调用接口的方法,传入接口类对象
        vis->visit(this);// 调用相应的对象的方法
    }
    virtual ~A() {}
};

class B : public A {
public :
    void Accept(IVisitor *vis) { // 子类重写调用函数
        vis->visit(this);
    }
};
class OutputVisitor : public A::IVisitor { // 设置一个专门执行操作的类,继承子访问者类,重写操作visit函数功能
public :
    virtual void visit(A *obj) {
        cout << "this is father class A" << endl;
    }
    virtual void visit(B *obj) {
        cout << "this is a class B object" << endl;
    }
};

使用方式也是很简单,省去了大量的 if else 判断,在增加子类,但是没有重写visitor操作的时候在编译期就会报错。

    OutputVisitor vis;// 生活一个访问者对象
    for (int i = 0; i < n; i++) {
        arr[i]->Accept(&vis);// 直接调用函数即可(虚函数跟着对象走)
        					// 例如arr[i]是B,B->Accept(&vis)相当于执行的是vis->(*B)
    }

单例模式

什么是单例模式?简单来说就是程序中只能够有一个对象。例如http传输中的服务器。那么怎么让程序中构造的对象有且只有一个呢?思考的方向就是不能够随意的创建对象,即不能使用类的一系列构造函数,那么就有办法了,将所有构造函数设置成私有的就可以了!

class HttpServer {
private:
    HttpServer() {}
    HttpServer(const HttpServer &) = delete;
};

接下来的问题又来了,第一个对象怎么获取呢?这时候就需要类方法的时候了,类方法不是成员方法,他不依赖于对象,即时没有对象也可以调用。

class HttpServer {
public :
    static HttpServer *getInstance() {
        return instance;
    }
private:
    static HttpServer *instance;
    HttpServer() {}
    HttpServer(const HttpServer &) = delete;
    ~HttpServer() {} // 当多个对象共有一个指针的时候,删除对象就出错了
    				//所以我们要将析构函数设置为私有的
};

现在我们只剩下初始化HttpServer *instance就可以了,由两种方法可以初始化它,也对应着两种模式:
饿汉模式:在主函数之前就直接初始化一个对象。

HttpServer *HttpServer::instance = new HttpServer();

懒汉模式:就是在程序第一次用到它的时候才实例化第一个对象。
当instance为空的时候实例化一个对象并返回,不为空的时候直接返回对象的指针。

class HttpServer {
public :
    static HttpServer *getInstance() {
        if (instance == nullptr) {
            instance = new HttpServer();
        }
        return instance;
    }
 };
HttpServer *HttpServer::instance = nullptr;

现在就是懒汉模式的基本结构了,因为目前仍存在一些bug,就是因为在多线程的情况下,可能会有多个线程同时判断instance=nullpr,这个时候就需要互斥锁了,如下:

class HttpServer {
public :
    // 类函数,来返回一个对象的实例化
    static HttpServer *getInstance() {
        if (instance == nullptr) {
           // 只有是第一次的时候才加锁,提高了效率。 
           std::unique_lock<std::mutex> lock(m_mutex);
            if (instance == nullptr) {
                instance = new HttpServer();
            }
        }
        return instance;
    }
private:
    static HttpServer *instance;
    static std::mutex m_mutex;
    HttpServer() {}
    HttpServer(const HttpServer &) = delete;
    ~HttpServer() {}
};
HttpServer *HttpServer::instance = nullptr;
std::mutex HttpServer::m_mutex;

工厂模式

工厂:抽象类,接口类
工厂类+产品类
创建对象的时候不需要new 很多, 直接交给工厂处理
由于直接交给工厂类负责,所以直接调用工厂的函数,无需每次判断不同的子类,分别new一个

例如:
用时间种子随机创建子类,需要判断才来new不同的对象,现在只需要调用一个工厂类的封装函数

另外: 工厂专门构造对象,即外部不能随意构造对象,应该将构造和拷贝函数设置成为受保护的而尽量不使用私有的(子类new一个对象的时候,要调用父类的构造函数)。

#include <iostream>
#include <algorithm>
#include <time.h>
using namespace std;

/* 说明: Car的工厂类 
 * 当Car的属性比较多的时候,生成对象就很麻烦,要初始化很多属性
 * 只有工厂可以创建对象, 构造函数和拷贝函数设置成受保护的(私有的,设置子类的就不能构造)
*/
class ICar{
public:
    
    class IFactory { // 工厂类设置在类里面表示车类的工厂
    public:
        virtual ICar *create() = 0;
    };
    
    virtual void run() = 0; // 纯虚函数
protected:
    ICar(){}
    ~ICar(){}
};


class BenzCar : public ICar { // 子类的构造函数设置为私有的,外部不能再创建对象
    BenzCar(){}
public:
    class Factory : public ICar::IFactory {
    public:
        virtual ICar *create() { // 子类自己的专门的工厂
            return new BenzCar();
        }
    };

    virtual void run() {
        cout << "BenzCar run" << endl;
    }
};

class BmwCar : public ICar {
    BmwCar(){}
public:
    class Factory : public ICar::IFactory {
    public:
        virtual ICar *create() { // 子类自己的专门的工厂
            return new BmwCar();
        }
    };

    virtual void run() {
        cout << "BmwCar run" << endl;
    }
};
//初始化两个工厂
ICar::IFactory *fac[2] = {new BenzCar::Factory(), new BmwCar::Factory()};

int main() {
    srand(time(0));
    ICar *cars[10];
    for (int i = 0; i < 10; i++) {
        cars[i] = fac[(rand() % 2)]->create();
    }

    for (int i = 0; i < 10; i++) {
        cars[i]->run();
    }

    return 0;
}

责任链模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值