设计模式之开闭原则C++实现

2022-06-16 14:33:16 浏览数 (1)

开闭原则: 当应用的需求改变时,在不修改软件实体的源代码或者二进制代码的前提下,可以扩展模块的功能,使其满足新的需求。

实现方式:“抽象约束、封装变化”,通过接口或者抽象类为软件实体定义一个相对稳定的抽象层,而将相同的可变因素封装在相同的具体实现类中。

具体示例:

设计一个图书售卖系统,UML类图如下

第一步:定义父类 book.h

代码语言:javascript复制
#pragma once
#include <iostream>
using namespace std;
#include <cstring>

//对外接口
/*
1、接口类中不应该声明成员变量,静态变量。

2、可以声明静态常量作为接口的返回值状态,需要在对应的cpp中定义并初始化,
    访问时需要使用"接口类型::静态常量名"访问

2、定义的接口方法使用virtual 修饰符 和 “=0” 修饰,表示该方法是纯虚的。

3、因为接口类是无法创建对象的,所以不应该编写构造函数和析构函数*/

class Book
{
    public:修改后的UML类图        //书籍名称
        virtual string getname()=0;
        //书籍价格
        virtual int getprice()=0;
        //书籍作者
        virtual string getauthor()=0;

};

第二步:定义小说类novelbook.h

代码语言:javascript复制
#include "book.h"
//子类
class NovelBook:public Book
{  
  public:
      NovelBook(){}

      NovelBook(string _name, int _price, string _author)
      {
          this->book_name=_name;
          this->book_price=_price;
          this->book_author=_author;
      }

      ~NovelBook(){}

      //对接口函数的实现
      string getname(){return this->book_name;}
      int getprice(){return this->book_price;}
      string getauthor(){return this->book_author;}

  //private://这样的话子类用不了了
  public:
      string book_name;
      int book_price;
      string book_author;
};

第三步:定义主函数main.cpp

代码语言:javascript复制
#include "offnovelbook.h"
#include "computebook.h"
#include <list>

class BookStore
{
    public:
        BookStore(){}
        ~BookStore(){}
  
        void get()
       {  
            for (int i=0;i<4;i  )
                 Novel_bookList.push_back(b1[i]);
           
       }
      
       void play()
       {  

        list<NovelBook>::iterator item1;
              
         for (item1= Novel_bookList.begin();item1!= Novel_bookList.end();item1  )
         {
              book_name=item1->getname();
              book_price=item1->getprice();
              book_author=item1->getauthor();
              cout<<"name "<<book_name<<" price "<<book_price
                <<" author "<<book_author<<endl;
         }
                   cout<<"************************"<<endl;
        
          }
    
   private:
    string book_name;
     int book_price;
    string book_author;

       list<NovelBook> Novel_bookList;
     NovelBook b1[4]={
        NovelBook("天龙八部",3200,"金庸"),
       NovelBook("巴黎圣母院",5600,"雨果"),
       NovelBook("悲惨世界",3500,"雨果"),
       NovelBook("金瓶梅",4300,"兰陵笑笑生")
    };

};

int main()
{  
    BookStore bbook;
 
     bbook.get();
     bbook.play();

    return 0;
}

结果显示:

项目投产了, 书籍正常销售出去, 书店赢利很多,此时,老板为了促销,实行全部书籍打9折销售。

此时,有如下三种方法可以解决这个问题:

1)修改接口 book.h:在Book上新增加一个方法getOffPrice(), 专门用于进行打折处理, 所有的实现类实现该方法。修改的后果是, 实现类NovelBook要修改, BookStore中的方法也修改, 同时Book作为接口应该是稳定且可靠的, 不应该经常发生变化, 否则接口作为契约的作用就失去了效能。

2)修改实现类 novelbook.h:修改NovelBook类中的方法, 直接在getPrice()中实现打折处理, 通过class文件替换的方式可以完成部分业务变化。但是该方法还是有缺陷的---例如采购书籍人员也是要看价格的, 由于该方法已经实现了打折处理价格, 因此采购人员看到的也是打折后的价格, 会因信息不对称而出现决策失误的情况。

3)通过扩展实现变化:增加一个子类OffNovelBook, 覆写getPrice方法, 高层次的模块, 通过OffNovelBook类产生新的对象, 完成业务变化对系统的最小化开发。

因此,第三种方案修改,修改后的UML类图如下

第四步:增加一个offnovelbook.h

代码语言:javascript复制
#include "novelbook.h"
//增加一个打折销售类
class OffNovelBook:public NovelBook
{
    public:
        OffNovelBook(string _name, int _price, string _author )
        {
            this->book_name=_name;
            this->book_price=_price;
            this->book_author=_author;
        }
        //打折价格,覆盖基类的
        int getprice()
        { 
            off_book_price=NovelBook::getprice();
            off_book_price=off_book_price*90/100;
            return off_book_price;
        }

    private:
        int off_book_price;
};

第五步:在main.cpp中添加实现

代码语言:javascript复制
//void get()中添加:
for (int i=0;i<4;i  )
     OffNovel_bookList.push_back(b2[i]);
 
 //void play()中添加:
      list<OffNovelBook>::iterator item2;
              
         for (item2= OffNovel_bookList.begin();item2!=OffNovel_bookList.end();item2  )
         {
              book_name=item2->getname();
              book_price=item2->getprice();
              book_author=item2->getauthor();
              cout<<"name "<<book_name<<" price "<<book_price
                <<" author "<<book_author<<endl;
         }
 //添加变量
     list<OffNovelBook> OffNovel_bookList;
     OffNovelBook b2[4]={
        OffNovelBook("天龙八部",3200,"金庸"),
       OffNovelBook("巴黎圣母院",5600,"雨果"),
       OffNovelBook("悲惨世界",3500,"雨果"),
       OffNovelBook("金瓶梅",4300,"兰陵笑笑生")
    };

结果显示:

这次老板开心了,又赚了一大笔。可是又过了一段时间,项目需求变化了,老板新进了计算机类的书籍,但是计算机书籍还有很多种,有编程语言,数据库需要分类。

熟悉了以上设计原则后,画出总UML类图如下

第六步:添加虚Pcomputebook.h

代码语言:javascript复制
#include "book.h"
//增加计算机类 接口函数
class PcomputeBook:public Book
{  
    public:
        //有编程的,数据库的,新加的接口
        virtual string getscope()=0;
};

第七步:添加实现computebook.h

代码语言:javascript复制
#include "Pcomputebook.h"

//子类
class ComputeBook:public PcomputeBook
{  
    public:
        ComputeBook(){}

       ComputeBook(string _name, int _price, string _author,string _scope)
        {
            this->book_name=_name;
            this->book_price=_price;
            this->book_author=_author;
            this->book_scope=_scope;
        }
       // ~ComputelBook(){}
        //对接口函数的实现
        string getname(){return this->book_name;}
        int getprice(){return this->book_price;}
        string getauthor(){return this->book_author;}
        string getscope(){return this->book_scope;}
    //private://这样的话子类用不了了
    public:
        string book_name;
        int book_price;
        string book_author;
        string book_scope;

};

第八步:添加main.cpp实现

代码语言:javascript复制
//void get()
for (int i=0;i<4;i  )
                 Compute_bookList.push_back(b3[i]);
 //void play()       
        for (item3= Compute_bookList.begin();item3!=Compute_bookList.end();item3  )
         {
              book_name=item3->getname();
              book_price=item3->getprice();
              book_author=item3->getauthor();
              book_scope=item3->getscope();
              cout<<"name "<<book_name<<" price "<<book_price
                <<" author "<<book_author<<" scope "<<book_scope<<endl;
         }
//
   string book_scope;
       list<ComputeBook> Compute_bookList;
     ComputeBook b3[4]={
        ComputeBook("C  ",3200,"金庸","编程类"),
       ComputeBook("C",5600,"雨果","编程类"),
       ComputeBook("Java",3500,"雨果","编程类"),
       ComputeBook("Python",4300,"兰陵笑笑生","编程类")

结果显示:

0 人点赞