常见的设计模式

本文主要介绍设计模式的设计原则,常见的设计模式:单例模式、工厂模式、抽象工厂模式

建造者模式、代理模式等。

7大设计原则   


单一职责原则         

  • 类的职责应该尽量的单一,一个方法只做一件事。

使用建议:类中的方法功能应该尽量近似,否则就拆分成俩个类。

用例:聊天通信,拆分成聊天类和通信类。

开闭原则

  • 尽可能是对外扩展

使用建议:类要修改,尽可能是扩展功能,而不是修改。便于出错回滚。

用例:超市促销,不是覆盖原有的价格,而是在价格旁再打上一个新价格。

里氏替换原则:

  • 基类能出现的地方,都能用子类替换
  • 对子类重写基类全部的方法,子类不对外提供自己的方法。

使用建议:子类重写父类的方法,并且放大输出。

用例:有一个员工类,俩个基类分别的长期工和临时工,都重写了父类的方法。

依赖倒置原则

  • 高层模块不应该依赖于底层模块

使用建议:每个类都有抽象类,不继承于具体类。继承于抽象类。

用例:司机---什么车都能开,小车司机---只能开小车 。如果继承出子类,那么小车司机就违背了依赖倒置原则。

迪⽶特法则:最少知道原则

  • 一个类对自己类依赖越少越好。

使用建议:降低不同类间的耦合度,增加的方法不会影响本类。

举例:班主点名,老师在点名册打勾。就违背了最小知道原则,应该由班长完成点名和打勾任务。

接⼝隔离原则 

  • 接口尽量都是需要的,不需要用到的接口,不暴露对外。

使用建议:接口设置尽量单一,对外只提供有用接口。

举例:修改密码界面,只提供密码即可,不提供用户名等资料。

这7大原则总结下来就是抽象实现框架,具体填充扩展细节,接口尽量单一,类间耦合度低。


常见的设计模式

单例模式

类在全局中只有一份实例,并且提供一个全局的访问点,一次创建,可以提供给所有程序调用。比如服务器的配置文件,程序就会通过单例对象,对配置文件读取。

使用场景:

  • 服务器配置文件
  • 日志器,防止文件被重入

单例模式根据创建的时机,划分成饿汉模式、懒汉模式

饿汉模式

在程序运行时,就创建好。

  • 优点:遇到单例时,直接就可以调用,不需要消耗时间创建。
  • 缺点:启动慢、浪费空间。

设计思路:类中的全局变量,会在main函数开始前就被创建。

为了实现单例,需要私有析构和构造函数,防止在堆和栈上直接创建。禁掉拷贝构造和赋值重载。

//饿汉模式
class Singleton{
public:
    Singleton(const Singleton& ) =delete;
    Singleton& operator=(const Singleton& )=delete;
    //获取单例
    static Singleton &GetInstance(){
        return _eton;
    }
private:
    static Singleton _eton;
    Singleton(){
        std::cout<<"构造"<<std::endl;
    }
    ~Singleton(){}
};
Singleton Singleton::_eton;


 懒汉模式

在第一次调用这个类的时候创建

  • 优点:启动快,自主选择单例创建的时机,节省内存。
  • 缺点:创建慢,效率低。

懒汉模式的普通写法:判断单例是否为空,为空就加锁,在判断是否为空 就创建,返回单例。

懒汉的现代写法:C++11后,静态成员的静态变量是局部变量,只有在第一次调用函数的时候才创建。同样需要私有构造、拷贝,禁掉 拷贝构造和赋值重载

//懒汉模式:只有首次调用的时候创建:采用类中静态函数的局部变量
class Singleton{
public:
    static  Singleton& GetInstance(){
        static Singleton _eton;
        return _eton;
    }
    Singleton(const Singleton& ) =delete;
    Singleton& operator=(const Singleton& )=delete;
private:
    Singleton(){
        std::cout<<"构造"<<std::endl;
    }
    ~Singleton(){}
};

工厂模式

工程模式是设计类设计模式。提供一种创建类的最佳方法。创建对象不会暴露上传逻辑,需要创建什么就往工厂放。是一种由共同结构体指向新创建的对象。实现创建和使用的分离。

 简单工厂模式

由一个工厂类传入参数,动态决定返回哪一个子类的指针,而接收指针是用抽象的父类。

比如有一个抽象的水果类,继承出苹果和香蕉。

有一个工厂类,传入你需要的类型,就能返回指定水果的指针

class Fruit
{
public:
    Fruit() {}
    // 设置为纯虚函数
    virtual void show() = 0;
};

class Apple : public Fruit
{
public:
    Apple() {}
    void show() override
    {
        std::cout << "Apple" << std::endl;
    }
};

class Banana : public Fruit
{
public:
    Banana() {}
    void show() override
    {
        std::cout << "Banana" << std::endl;
    }
};

class Factory
{
public:
    static std::shared_ptr<Fruit> BuildFruit(const std::string& name)
    {
        if (name == "Apple")
            return std::make_shared<Apple>();
        else if (name == "Banana")
        {
            return std::make_shared<Banana>();
        }
        return std::shared_ptr<Fruit>();
    }
};

int main()
{
    std::shared_ptr<Fruit> apple = Factory::BuildFruit("Apple");
    apple->show();

    std::shared_ptr<Fruit> banana = Factory::BuildFruit("Banana");
    banana->show();
    return 0;
}

优点:获取对象简单,代码简单

缺点:违反开闭原则

工厂方法模式 

简单工厂下的扩展,一个产品对于一个工厂。假设现在有俩个产品,A产品,B产品,A工厂和B工厂。A工厂负责生成产品A,B工厂生成产品B。用户不需要知道具体的产品名,只需要往对于的工厂里取数据。

设计:抽象产品A ,B。派生出具体产品A和B。抽象工厂,拍派生出具体工厂A,B。

 class Fruit
 {
 public:
     Fruit() {}
     // 设置为纯虚函数
     virtual void show() = 0;
 };

class Apple : public Fruit
{
public:
    Apple() {}
    void show() override
    {
        std::cout << "Apple" << std::endl;
    }
};

class Banana : public Fruit
{
public:
    Banana() {}
    void show() override
    {
        std::cout << "Banana" << std::endl;
    }
};

class FruitFactory{
    public:
        virtual std::shared_ptr<Fruit> BuildFruit()=0;
};

class AppleFactory :public FruitFactory
{
    public:
        std::shared_ptr<Fruit> BuildFruit() override
        {
            return  std::make_shared<Apple>();
        }
};

class BananaFactory :public FruitFactory
{
    public:
        std::shared_ptr<Fruit> BuildFruit() override
        {
            return  std::make_shared<Banana>();
        }
};

int main()
{
    std::shared_ptr<FruitFactory>  factory(new AppleFactory());
    auto apple=factory->BuildFruit();
    apple->show();

    factory.reset(new BananaFactory());
    auto banana=factory->BuildFruit();
    banana->show();

    return 0;
}

优点:方便生成对象

缺点:违反开闭原则,需要为每一个类都配置一个工厂,代码冗余

抽象工厂模式

工厂方法解决了简单工厂工厂类职责太重的问题,但是需要为每一个类都创建一个对象,会存在大量的开销。一种方法是将相同的类归于一个族,一个工厂来负责生成一个家族的产品。

下面就以水果家族和动物家族演示抽象工厂的创建


class Fruit
{
public:
    Fruit() {}
    // 设置为纯虚函数
    virtual void show() = 0;
};

class Apple : public Fruit
{
public:
    Apple() {}
    void show() override
    {
        std::cout << "Apple" << std::endl;
    }
};
class Banana : public Fruit
{
public:
    Banana() {}
    void show() override
    {
        std::cout << "Banana" << std::endl;
    }
};

class Animal
{
public:
    virtual void show() = 0;
};

class Dog : public Animal
{
public:
    void show() override
    {
        std::cout << "Dog" << std::endl;
    }
};

class Lamp : public Animal
{
public:
    void show() override
    {
        std::cout << "Lamp" << std::endl;
    }
};

class Factory
{
public:
    virtual std::shared_ptr<Fruit> BuildFruit(const std::string &name) = 0;
    virtual std::shared_ptr<Animal> BuildAnimal(const std::string &name) = 0;
};
class FruitFactory : public Factory
{
public:
    std::shared_ptr<Animal> BuildAnimal(const std::string &name)  override
    {
        return std::shared_ptr<Animal>();
    }
    std::shared_ptr<Fruit> BuildFruit(const std::string &name) override
    {
        if (name == "Apple")
            return std::make_shared<Apple>();
        else if (name == "Banana")
            return std::make_shared<Banana>();
        return std::shared_ptr<Fruit>();
    }
};

class AnimalFactory : public Factory
{
public:
    std::shared_ptr<Fruit> BuildFruit(const std::string &name) override
    {
        return std::shared_ptr<Fruit>();
    }
    std::shared_ptr<Animal> BuildAnimal(const std::string &name) override
    {
        if (name == "Dog")
            return std::make_shared<Dog>();
        else if (name == "Lamp")
            return std::make_shared<Lamp>();
        return std::shared_ptr<Animal>();
    }
};

class FactoryProduce{
    public:
        static std::shared_ptr<Factory> Build(const std::string &name)
        {
            if(name=="水果")
                return std::make_shared<FruitFactory>();
            else if(name=="动物"){
                return std::make_shared<AnimalFactory>();
            }
            return std::shared_ptr<Factory>();
        }
};

int main(){

    std::shared_ptr<Factory> factor=FactoryProduce::Build("动物");
    auto dog=factor->BuildAnimal("Dog");
    dog->show();
    std::shared_ptr<Factory> fruit_factor=FactoryProduce::Build("水果");
    auto apple=fruit_factor->BuildFruit("Apple");
    apple->show();
    return 0;
}

优点:开销相对较小,创建产品也相对简单。

缺点:扩展复杂,既要扩展家族,也要往具体的工厂添加。

适用场景:

  • (属于同一产品族)一起创建时需要大量的重复代码

建造者模式

建造者模式是一种建造型的设计模式。将复杂的对象分解成简单的步骤一步一步组装出对象。

做到复杂对象的构建与表示分离。

建造者的模式的五个核心步骤

  • 抽象产品类
  • 构建具体产品类
  • 抽象builder类:创建一个产品对象所需要的接口
  • 具体产品建造者类: 实现各个结果,构建部件
  • 指挥者类:统一组装,通过指挥者构建产品

class Computer
{
public:
    Computer() {}
    void set_dispaly(const std::string &display)
    {
        _display = display;
    }
    void set_board(const std::string &board)
    {
        _board = board;
    }

    virtual void set_os() = 0;
    void showPara()
    {
        std::string s = "笔记本参数:\n";
        s += "\t 显示器:" + _display+"\n";
        s += "\t 主板:" + _board+"\n";
        s += "\t 操作系统:" + _os;
        std::cout<<s<<std::endl;
    }

protected:
    std::string _display;
    std::string _board;
    std::string _os;
};

class MacBook : public Computer
{
public:
    void set_os() override
    {
        _os = "Mac-Os";
    }
};

// 建造零部件抽象类
class Builder
{
public:
    virtual void BuildDisplay(const std::string &display) = 0;
    virtual void BuildBoard(const std::string &board) = 0;
    virtual void BuildOs() = 0;
    virtual std::shared_ptr<Computer> Build() = 0;
};

// 具体类
class BuidlerMacBook : public Builder
{
public:
    BuidlerMacBook() : _cmp(new MacBook) {}

    void BuildDisplay(const std::string &display) override
    {
        _cmp->set_dispaly(display);
    }
    void BuildBoard(const std::string &board) override
    {
        _cmp->set_board(board);
    }
    void BuildOs()
    {
        _cmp->set_os();
    }
    std::shared_ptr<Computer> Build() override
    {
        return _cmp;
    }
private:
    std::shared_ptr<Computer> _cmp;
};

//指挥者
class Dirctor{
public:
    Dirctor(Builder* builder)
        :_builder(builder)
    {
    }
    void Construct(const std::string &display,const std::string &board)
    {
        _builder->BuildDisplay(display);
        _builder->BuildBoard(board);
        _builder->BuildOs();
    }
private:
    std::shared_ptr<Builder> _builder;
};

int main()
{
    Builder *build=new BuidlerMacBook();
    //指挥者
    std::unique_ptr<Dirctor> dirctor(new Dirctor(build));
    dirctor->Construct("sangxing","CHERRY");
    auto max=build->Build();
    max->showPara();
    return 0;
}
  • 优点:创建与表示分离
  • 缺点:产品发生变化,需要更改builder类

适用场景:复杂对象,有多个步骤组装的产品


代理模式

代理模式控制对原对象的引用。一般情况下,一个对象不适合被直接访问,或者原对象无法满足需求的时候,创建一个中介,隔离原对象和客户端。这个中介就是代理模式。

下面以房东出租房间。中介发布出租,带人看房等写一份代理模式的例子

#include<string>
#include <iostream>

/*通过租房来学习代理模式
    代理模式:代理者与被代理者有相同的接口,但是是加强版的更加详细
*/


class RentHouse{
public:
    virtual void rent()=0;
};

class Landlord:public RentHouse{
public:
    void rent() override{
        std::cout<<"房东出租房间"<<std::endl;
    }
};

class Intermediary:public Landlord{
public:
    void rent() override{
        std::cout<<"中介发布公告"<<std::endl;
        std::cout<<"中介带人看房"<<std::endl;
        _landlord.rent();
        std::cout<<"中介完成交易"<<std::endl;
    };
private:
    Landlord _landlord;
};
int main()
{
    Intermediary intermediary;
    intermediary.rent();
    return 0;
}

优点:

  • 职责明确,只需要考虑自己应该做的事情。
  • 高扩展性,真实对象改变不影响代理。
  • 安全访问,代理模式隔断访问,提供安全的接口。

缺点:

  • 增加系统复杂度。代理冗余
  • 请求速度慢:请求要先经过中介,最后到达真实对象。

适用场景:

  • 当对象被控制访问或者需要对原对象补充访问等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深度搜索

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值