• 设计模式


    TOC
    设计模式参考链接:https://www.w3cschool.cn/shejimoshi/
    C++在线编译器:https://gcc.godbolt.org/ (支持C++11)

    工厂模式

    1. 定义公共接口

    为了表示,这时一个接口类,我认为最好将接口函数定义为纯虚函数,而不仅仅定义为虚函数。
    个人看法:此处公共接口使用默认构造函数,是为了表示这只是一个接口类。接口类不算做一个实体。

    //定义接口
    class Shape
    {
    public:
        virtual void draw () = 0 ;
        virtual void release () = 0 ;
    };
    2. 接口的具体实现
    //接口的具体实现
    class Rectangle : public Shape
    {
    public:
        Rectangle (){
            cout << "Rectangle 0x" << this << endl;
        }
        ~Rectangle (){
            cout << "Rectangle 0x" << this << " release." << endl;
        }
    
        void draw (){
            cout << "This is Rectangle." << endl;
        }
        void release (){
            delete this ;
        }
    };
    
    //接口的具体实现
    class Square : public Shape
    {
    public:
        Square (){
            cout << "Square 0x" << this << endl;
        }
        ~Square (){
            cout << "Square 0x" << this << " release." << endl;
        }
    
        void draw (){
            cout << "This is Square." << endl;
        }
        void release (){
            delete this ;
        }
    };
    
    //接口的具体实现
    class Circle : public Shape
    {
    public:
        Circle (){
            cout << "Circle 0x" << this << endl;
        }
        ~Circle (){
            cout << "Circle 0x" << this << " release." << endl;
        }
    
        void draw (){
            cout << "This is Circle." << endl;
        }
        void release (){
            delete this ;
        }
    };
    3. 创建工厂,基于给定信息生成实体类对象

    使用C++11推荐的enum class。
    在实际生产中,因为项目历史原因,前辈们很多用的是enum导致有时候会有定义冲突,甚至因为几个人同时提交代码,而缺少enum的保护,导致定义冲突,最终导致项目的daily版本编译 BB,所以最好将enum替换为enum class。

    enum class SHAPE
    {
        REC ,
        SQU ,
        CIR
    };
    //创建工厂,基于给定信息生成实体类对象
    class Factory
    {
    public:
        Factory (){}
        ~Factory (){}
    
        Shape *getShape ( SHAPE shape )
        {
            Shape *retShape = nullptr ;
            switch (shape)
            {
                case SHAPE ::REC:
                    retShape = new Rectangle ();
                    break ;
                case SHAPE ::SQU:
                    retShape = new Square ();
                    break ;
                case SHAPE ::CIR:
                    retShape = new Circle ();
                    break ;
                default :
                    retShape = nullptr ;
                    break ;
            } 
    
    
            return retShape;
        }
    };
    4.调用
    int main (
    {
        Factory * factory = new Factory ();
        Shape * shape , * shape1 , * shape2 , * shape3 ;
    
        shape = factory -> getShape ( SHAPE ::REC);
        shape -> draw ();
        shape -> release ();
        shape = factory -> getShape ( SHAPE ::SQU);
        shape -> draw ();
        shape -> release ();
        shape = factory -> getShape ( SHAPE ::CIR);
        shape -> draw ();
        shape -> release ();
    
        shape1 = factory -> getShape ( SHAPE ::REC);
        shape1 -> draw ();
        shape2 = factory -> getShape ( SHAPE ::SQU);
        shape2 -> draw ();
        shape3 = factory -> getShape ( SHAPE ::CIR);
        shape3 -> draw ();
    
        shape1 -> release ();
        shape2 -> release ();
        shape3 -> release ();
    
        delete factory;
    
        return 1 ;
    }
    
    //输出
    Rectangle 0x0x136cc40
    This is Rectangle.
    Rectangle 0x0x136cc40 release.
    Square 0x0x136cc40
    This is Square.
    Square 0x0x136cc40 release.
    Circle 0x0x136cc40
    This is Circle.
    Circle 0x0x136cc40 release.
    Rectangle 0x0x136cc40
    This is Rectangle.
    Square 0x0x136d070
    This is Square.
    Circle 0x0x136d090
    This is Circle.
    Rectangle 0x0x136cc40 release.
    Square 0x0x136d070 release.
    Circle 0x0x136d090 release.
    备注
    • 如果不提供新的方法,则接口类不需要改变;
    • 如果需要加入新的算法,则只需要先构建算法的实体类,再在工厂实体类中返回该算法类即可,对外接口改动很少。

    抽象工厂模式

    工厂模式的工厂模式。

    单例模式

    单例模式目前我仅认为饿汉模式有用,此外,可以将返回的new对象换成static对象,这样可以不用过多地考虑内存释放的问题。而且因为在程序装载时static便被初始化,因而也是线程安全的。

    • 单例在什么时候用呢?
      我认为在诸如log、metadata等这些只需要一个实例对象的时候, 使用单例模式是最好不过的。
    1. 饿汉模式
    class Singleton
    {
    private:
        Singleton (){};
    
        //release
        class Garbo {
        public:
            ~Garbo (){
                if (pInstance)
                {
                    cout << "Garbo delete 0x" << pInstance << endl;
                    delete pInstance;
                }
            }
        };
        static Garbo garbo;
        static Singleton *pInstance;
    
    public:
        ~Singleton (){
            cout << "this 0x" << this << " released." << endl;
        }
        static Singleton * getInstance ()
        {
            return pInstance;
        }
        void run ()
        {
            cout << "this 0x" << this << " run." << endl;
        }
    };
    Singleton * Singleton ::pInstance = new Singleton ();
    Singleton ::Garbo Singleton ::garbo;
    备注
    • Garbo类用于在进程结束时,释放内存。因为主进程结束时会释放全局变量(包括static),所以此时static Garbo garbo这个静态成员变量被释放,调用Garbo的析构函数,从而释放pInstance。
    • pInstance作为static指针,在程序载入时便被初始化。可以参考[static条目] (C++/C++11新特性与用法.md)。

    建造者模式

    一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。

    与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。

    建造者模式更像是把一个大项目拆分成很多串行的小项目,小项目间是有先后执行顺序的。比如说一个feature是由很多node组成(此处先不讨论pipeline),对于一个特定的feature,比如人脸检测,组成这个feature的node必然是有顺序关系的,这时候就可以使用建造者模式了。

    1、创建产品共有的属性接口

    意味着不同的产品之间需要有一些共有的元素,比如以下的例子,KFC的素堡、鸡肉堡、可口可乐、百事可乐等等,它们都有名字、价格、包装、热量等等,这些就是不同产品的共有元素。

    class Item
    {
    public:
        virtual string name() = 0;
        virtual Pack *pack() = 0;
        virtual float price() = 0;
    };
    2、创建不同品类的产品生产工厂

    以下有一个Pack基类,用于提供产品包装的接口。
    需要注意的时,因为Item基类中有pack()纯虚函数,因此Pack类中的pack必须实现或者指定为(纯)虚函数。因为Item对Pack是“Item use Pack”的关系。
    同样,对于基类(接口)更希望使用默认构造函数,而不是显示地表示它有一个构造函数,对于Coke、Pepsi等实体,则倾向于显示的将其构造函数写出来;接口函数同样更推荐使用纯虚函数。

    class Pack
    {
    public:
        virtual string pack() = 0;
    };
    
    
    class Wrapper : public Pack
    {
    private:
        string m_name = "wrapper";
    
    
    public:
        string pack()
        {
            return m_name;
        }
    };
    
    
    class Bottle : public Pack
    {
    private:
        string m_name = "wrapper";
    
    
    public:
        string pack()
        {
            return m_name;
        }
    };
    
    
    class Burger : public Item
    {
    private:
        Pack *m_pack;
    
    
    public:
        Pack *pack()
        {
            m_pack = new Wrapper();
            return m_pack;
        }
    };
    
    
    class ColdDrink : public Item
    {
    private:
        Pack *m_pack;
    
    
    public:
        Pack *pack()
        {
            m_pack = new Bottle();
            return m_pack;
        }
    };
    3、设计产品
    class VegBurger : public Burger
    {
    private:
        string m_name = "VegBurger";
        float m_price = 5.0;
    
    
    public:
        VegBurger(){}
        ~VegBurger(){}
        string name()
        {
            return m_name;
        }
        float price()
        {
            return m_price;
        }
    };
    4、设计产品组合方式
    class Meal
    {
    private:
        vector <Item *> m_items;
        float m_price = 0;
    
    
    public:
        Meal(){}
        ~Meal(){}
    
    
        void addItem(Item *item)
        {
            m_items.emplace_back(item);
        }
    
    
        float price()
        {
            return m_price;
        }
    
    
        void show()
        {
            cout << endl;
            for(Item *item : m_items)
            {
                cout << "It is a " << item->name() << endl;
                cout << " Packing by: " << item->pack()->pack() << endl;
                cout << " Cost: $" << item->price() << endl;
                m_price += item->price();
            }
            cout << "Sum: $" << m_price << endl;
        }
    };
    
    
    class MealBuilder
    {
    public:
        MealBuilder(){}
        ~MealBuilder(){}
    
    
        Meal *prepareVegMeal()
        {
            Meal *meal = new Meal();
            meal->addItem(new VegBurger());
            meal->addItem(new Coke());
            return meal;
        }
    
    
        Meal *prepareNonVegMeal()
        {
            Meal *meal = new Meal();
            meal->addItem(new ChickenBurger());
            meal->addItem(new Pepsi());
            return meal;
        }
    };
    5、测试
    int main()
    {
        MealBuilder mealBuilder;
        Meal *vegMeal = mealBuilder.prepareVegMeal();
        Meal *nonVegMeal = mealBuilder.prepareNonVegMeal();
    
    
        vegMeal->show();
        nonVegMeal->show();
    
    
        return 1;
    }
    //输出
    It is a VegBurger
       Packing by: wrapper
       Cost: $5
    It is a Coke
       Packing by: wrapper
       Cost: $3
    Sum: $8
    
    
    It is a ChickenBurger
       Packing by: wrapper
       Cost: $6
    It is a Pepsi
       Packing by: wrapper
       Cost: $2
    Sum: $8
    备注
    • 改变不同实体是链接方式,就可以得到新的“套餐”(功能);
    • 如果要添加新的实体,首先是要确定其类别,和哪个品类更有共性。

    享元模式

    我认为像是工厂模式,只是有些类被复用了。所以被创建的实体,需要被保存起来,以备不时之需。

    适配器模式

    1、提供同一的对外接口和适配接口
    //定义媒体类别
    typedef enum class MEDIATYPE
    
    {
        MP3,
        MP4,
        AVI,
    }MEDIATYPE;
    
    
    //同一对外的接口类
    
    class MediaPlayer{
    public:
        virtual void play(const MEDIATYPE type, string &file) = 0;
    };
    
    
    //不同播放器的接口类
    
    class AdvanceMediaPlayer{
    public:
        virtual void playvlc(string &file) = 0;
        virtual void playmp3(string &file) = 0;
    };
    2、构建每个适配器的实体类
    class VLCPlayer : public AdvanceMediaPlayer{
    public:
        VLCPlayer(){}
        ~VLCPlayer(){}
    
    
        void playvlc(string &file)
        {
            cout << "play " << file << " by VLC player." << endl;
        }
    
    
        void playmp3(string &file){}
    };
    
    
    class MP3Player : public AdvanceMediaPlayer{
    public:
        MP3Player(){}
        ~MP3Player(){}
    
    
        void playmp3(string &file)
        {
            cout << "play " << file << " by MP3 player." << endl;
        }
    
    
        void playvlc(string &file){}
    };
    3、构建适配器的实体类
    class MediaAdapter : public MediaPlayer{
    private:
        AdvanceMediaPlayer *m_player;
    public:
        MediaAdapter(){}
        MediaAdapter(const MEDIATYPE type){
            switch(type)
            {
                case MEDIATYPE::MP3:
                    m_player = new MP3Player();
                    break;
                case MEDIATYPE::MP4:
                case MEDIATYPE::AVI:
                    m_player = new VLCPlayer();
                    break;
            }
        }
        ~MediaAdapter(){}
    
    
        void play(const MEDIATYPE type, string &file)
        {
            switch(type)
            {
                case MEDIATYPE::MP3:
                    m_player->playmp3(file);
                    break;
                case MEDIATYPE::MP4:
                case MEDIATYPE::AVI:
                    m_player->playvlc(file);
                    break;
            }
            delete m_player;
        }
    };
    4、对外接口类(可选)

    这一层我认为不是必须的。当构建完适配器的实体类之后,我认为适配就已经完成了。但是不可否认,构建这个对外接口类,可以把对外接口包装得更好看一点。

    class AudioPlayer : public MediaAdapter{
    public:
        AudioPlayer(){}
        ~AudioPlayer(){}
    
    
        void play(const MEDIATYPE type, string &file)
        {
            MediaAdapter *mediaadapter = new MediaAdapter(type);
            mediaadapter->play(type, file);
            delete mediaadapter;
        }
    };
    5、测试
    int main()
    {
        AudioPlayer audioPlayer;
        string file;
        file = "test1.mp3";
        audioPlayer.play(MEDIATYPE::MP3, file);
        file = "test2.mp4";
        audioPlayer.play(MEDIATYPE::MP4, file);
        file = "test3.avi";
        audioPlayer.play(MEDIATYPE::AVI, file);
        return 1;
    }
    
    
    //输出
    play test1.mp3 by MP3 player.
    play test2.mp4 by VLC player.
    play test3.avi by VLC player.

    策略模式

    和工厂模式很像,区别可能在产品的调用方式上?当然思想也不一样,工厂模式主要是关注对象生成,测略模式则是关注选择

    Context context = new Context(new OperationAdd());
    context.executeStrategy(10, 5);

    和工厂模式对比一下,主要在选择产品的这个决策从工厂中移除了,转而是将其权限给用户,使用户根据某些条件来选择。
    在学习手册中,称策略模式是解决if...else...的复杂问题的。我是这样理解的:

    如果是不同的目的,但是有类似的操作方式。比如说四则运算,都是a与b通过某种计算得到一个结果,对于编码来说,操作是类似的(区别无非是+、-、*、/的符号),此时则可以用测略模式解决这些复杂的代码(并不一定能解决冗余代码)。
    总结就是:用户自己制定测略、算法透明、算法与测略解耦合。

    责任链模式

    以下以一个log模块作为示例。

    1、接口
    class AbstractLogger
    {
    public:
    typedef enum LEVEL {
    INFO,
    DEBUG,
    ERROR,
    MAX
    } LEVEL;
    
    
    virtual void logMessage(LEVEL level, string message)
    {
    if (level >= m_level)
    {
        write(message);
    }
    if (m_next)
    {
    m_next->logMessage(level, message);
    }
    }
    void setNext(AbstractLogger *next)
    {
      m_next = next;
    }
    
    
    protected:
    LEVEL m_level = MAX;
    AbstractLogger *m_next = NULL;
    virtual void write(string message) = 0;
    };
    2、接口类实现
    class InfoLogger : public AbstractLogger
    {
    public:
        InfoLogger(AbstractLogger::LEVEL level) { m_level = level; }
    private:
    void write(string message)
    {
        cout << "Info Logger: " << message << endl;
    }
    };
    class DebugLogger : public AbstractLogger
    {
    public:
        DebugLogger(AbstractLogger::LEVEL level) { m_level = level; }
    private:
    void write(string message)
    {
        cout << "Debug Logger: " << message << endl;
    }
    };
    class ErrorLogger : public AbstractLogger
    {
    public:
        ErrorLogger(AbstractLogger::LEVEL level) { m_level = level; }
    private:
    void write(string message)
    {
      cout << "Error Logger: " << message << endl;
    }
    };
    3、链接关系的实现
    class Logger
    {
    public:
    static AbstractLogger *getLogger()
    {
    if (!m_infoLogger)
      m_infoLogger = new InfoLogger(AbstractLogger::LEVEL::INFO);
    if (!m_debugLogger)
      m_debugLogger = new DebugLogger(AbstractLogger::LEVEL::DEBUG);
    if (!m_errorLogger)
      m_errorLogger = new ErrorLogger(AbstractLogger::LEVEL::ERROR);
    
    
    m_errorLogger->setNext(m_debugLogger);
    m_debugLogger->setNext(m_infoLogger);
    
    
    return m_errorLogger;
    }
    private:
    static AbstractLogger *m_infoLogger;
    static AbstractLogger *m_debugLogger;
    static AbstractLogger *m_errorLogger;
    };
    AbstractLogger *Logger::m_infoLogger;
    AbstractLogger *Logger::m_debugLogger;
    AbstractLogger *Logger::m_errorLogger;
    4、测试
    Logger::getLogger()->logMessage(AbstractLogger::LEVEL::DEBUG, "this is a debug message.");
    Logger::getLogger()->logMessage(AbstractLogger::LEVEL::ERROR, "this is a error message.");
    Logger::getLogger()->logMessage(AbstractLogger::LEVEL::INFO, "this is a info message.");
    //输出
    Debug Logger: this is a debug message.
    Info Logger: this is a debug message.
    Error Logger: this is a error message.
    Debug Logger: this is a error message.
    Info Logger: this is a error message.
    Info Logger: this is a info message.




    分享自由,尊重著作权
  • 相关阅读:
    H7-TOOL外网远程LUA小程序功能展示
    【第3版emWin教程】第20章 emWin6.x存储设备之基本函数
    【第3版emWin教程】第19章 emWin6.x的2D图形库之绘制图形(含二维码和条形码)
    【第3版emWin教程】第18章 emWin6.x的2D图形库之绘制流位图(SPI Flash方案)
    【第3版emWin教程】第17章 emWin6.x的2D图形库之绘制流位图(QSPI Flash内存映射方案)
    嵌入式新闻早班车-第7期
    【第3版emWin教程】第16章 emWin6.x的2D图形库之绘制流位图(SD卡方案)
    嵌入式新闻早班车-第6期
    【第3版emWin教程】第15章 emWin6.x的2D图形库之绘制位图
    【第3版emWin教程】第14章 emWin6.x的2D图形库之基本绘图
  • 原文地址:https://www.cnblogs.com/jerry323/p/12727837.html
Copyright © 2020-2023  润新知