开闭原则: 当应用的需求改变时,在不修改软件实体的源代码或者二进制代码的前提下,可以扩展模块的功能,使其满足新的需求。
实现方式:“抽象约束、封装变化”,通过接口或者抽象类为软件实体定义一个相对稳定的抽象层,而将相同的可变因素封装在相同的具体实现类中。
具体示例:
设计一个图书售卖系统,UML类图如下
第一步:定义父类 book.h
#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
#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
#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
#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中添加实现
//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
#include "book.h"
//增加计算机类 接口函数
class PcomputeBook:public Book
{
public:
//有编程的,数据库的,新加的接口
virtual string getscope()=0;
};
第七步:添加实现computebook.h
#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实现
//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,"兰陵笑笑生","编程类")
结果显示: