昨天,有小伙伴问我面向对象编程跟面向过程编程的主要区别是什么。面向对象编程又有什么优势。
针对这两个问题,我们不妨举个例子。
情景描述
家住四川的小浩手抓饼店,清单如下:
小浩做的手抓饼香脆可口,再加上小浩的独家配方,吸引了许多当地人来品尝小浩做的手抓饼。
由于经营有方,小浩赚得盆满钵满,但小浩很有雄心,想在其他地方开连锁店。为了保证各连锁店做的手抓饼基本一致,小浩决定所有连锁店手抓饼制作的独家配方统一由总店放送。
其中有一家打算开在山东,经过实地考察,小浩发现山东人并不是很能吃辣,但他们喜欢放五香等。小浩因此微调了山东连锁店的加料菜单。
问题
现在小浩想要你设计一套系统帮助他管理他的连锁店。
主要功能:
- 计算每份订单价格
- 提供每份订单的制作流程
面向对象VS面向过程
面向过程
//四川连锁店
//四川饼价格计算
//手抓饼
double SichuanbingPrice() {
return 5.0;
}
//麻辣火腿
double MalahuotuiPrice() {
return 1.2;
}
//麻辣鸡排
double MalajipaiPrice() {
return 2.30;
}
//香辣猪排
double XianglazhupaiPrice() {
return 1.0;
}
double SichuanSumPrice(vector<int> &foods) {
double sum = 0.0;
for (size_t i = 0; i < foods.size(); i++) {
if (foods[i] == 0) {
sum += SichuanbingPrice();
}
if (foods[i] == 1) {
sum += MalahuotuiPrice();
}
if (foods[i] == 2) {
sum += MalajipaiPrice();
}
if (foods[i] == 3) {
sum += XianglazhupaiPrice();
}
}
return sum;
}
//四川手抓饼制作
void SichuanMake(vector<int> &foods) {
cout << "此处利用小浩的手抓饼制作工艺来制作(连锁店不得更改)" << endl;
string temp = "";
for (size_t i = 0; i < foods.size(); i++) {
if (foods[i] == 0) {
temp += " 加入饼 ";
}
if (foods[i] == 1) {
temp += " 加入麻辣火腿 ";
}
if (foods[i] == 2) {
temp += " 加入麻辣鸡排 ";
}
if (foods[i] == 3) {
temp += " 加入香辣猪排 ";
}
}
cout << temp << endl;
cout << "加入小浩独家配方(连锁店不得更改)" << endl;
}
//山东连锁店
//山东手抓饼价格计算
//手抓饼
double ShandongbingPrice() {
return 5.0;
}
//五香火腿
double WuxianghuotuiPrice() {
return 1.5;
}
//五香鸡排
double WuxiangjipaiPrice() {
return 2.0;
}
//五香猪排
double WuxiangzhupaiPrice() {
return 1.0;
}
double ShandongSumPrice(vector<int> &foods) {
double sum = 0.0;
for (size_t i = 0; i < foods.size(); i++) {
if (foods[i] == 0) {
sum += ShandongbingPrice();
}
if (foods[i] == 1) {
sum += WuxianghuotuiPrice();
}
if (foods[i] == 2) {
sum += WuxiangjipaiPrice();
}
if (foods[i] == 3) {
sum += WuxiangzhupaiPrice();
}
}
return sum;
}
//山东手抓饼制作
void ShandongMake(vector<int> &foods) {
cout << "此处利用小浩的手抓饼制作工艺来制作(连锁店不得更改)" << endl;
string temp = "";
for (size_t i = 0; i < foods.size(); i++) {
if (foods[i] == 0) {
temp += " 加入饼 ";
}
if (foods[i] == 1) {
temp += " 加入五香火腿 ";
}
if (foods[i] == 2) {
temp += " 加入五香鸡排 ";
}
if (foods[i] == 3) {
temp += " 加入五香猪排 ";
}
}
cout << temp << endl;
cout << "加入小浩独家配方(连锁店不得更改)" << endl;
}
面向对象
我们首先根据需求,仿照简单工厂模式设计类图
我们设计了两个基类:一个是手抓饼的基类:foodbase(产品)一个是连锁店的基类:storebase(工厂)
根据上述类图设计类
class FoodBase {
public :
FoodBase() = default;
virtual double getPrice() =0;
virtual string getMethod() = 0;
};
class ShandongFoods :public FoodBase {
public:
ShandongFoods() = default;
};
class Wuxianghuotui :public ShandongFoods {
public:
Wuxianghuotui() = default;
double getPrice() {
return 1.5;
}
string getMethod() {
return " 加入五香火腿 ";
}
};
class Wuxiangjipai :public ShandongFoods {
public:
Wuxiangjipai() = default;
double getPrice() {
return 2.0;
}
string getMethod() {
return " 加入五香鸡排 ";
}
};
class Wuxiangzhupai :public ShandongFoods {
public:
Wuxiangzhupai() = default;
double getPrice() {
return 1.0;
}
string getMethod() {
return " 加入五香猪排 ";
}
};
class Shandongbing :public ShandongFoods {
public:
Shandongbing() = default;
double getPrice() {
return 5.0;
}
string getMethod() {
return " 加一份饼 ";
}
};
//Sichuan
class SichuanFoods :public FoodBase {
public:
SichuanFoods() = default;
};
class Malahuotui :public SichuanFoods {
public:
Malahuotui() = default;
double getPrice() {
return 1.2;
}
string getMethod() {
return " 加入麻辣火腿 ";
}
};
class Malajipai :public SichuanFoods {
public:
Malajipai() = default;
double getPrice() {
return 2.3;
}
string getMethod() {
return " 加入麻辣鸡排 ";
}
};
class Sichuanbing :public SichuanFoods {
public:
Sichuanbing() = default;
double getPrice() {
return 5.0;
}
string getMethod() {
return " 加一份饼 ";
}
};
class Xianglazhupai :public SichuanFoods {
public:
Xianglazhupai() = default;
double getPrice() {
return 1.0;
}
string getMethod() {
return " 加入香辣猪排 ";
}
};
class StoreBase {
public:
StoreBase() = default;
virtual string getMethods() = 0;
virtual double getPrice() = 0;
protected:
string first_step, last_step;
vector<int> foods_list;
private:
void setSecret() {
first_step = "此处利用小浩的手抓饼制作工艺来制作(连锁店不得更改)";
last_step = "加入小浩独家配方(连锁店不得更改)";
}
};
class ShandongStore :public StoreBase {
public:
ShandongStore(vector<int>foods) {
foods_list = foods;
}
string getMethods() {
string method = "";
method += first_step + "\n";
ShandongFoods *shandong_foods = nullptr;
for (size_t i = 0; i < foods_list.size(); i++) {
if (foods_list[i] == 0) {
shandong_foods = new Shandongbing;
method += shandong_foods->getMethod();
}
if (foods_list[i] == 1) {
shandong_foods = new Wuxianghuotui;
method += shandong_foods->getMethod();
}
if (foods_list[i] == 2) {
shandong_foods = new Wuxiangjipai;
method += shandong_foods->getMethod();
}
if (foods_list[i] == 3) {
shandong_foods = new Wuxiangzhupai;
method += shandong_foods->getMethod();
}
}
delete shandong_foods;
method += "\n"+last_step+"\n";
return method;
}
double getPrice() {
double price = 0.0;
ShandongFoods *shandong_foods = nullptr;
for (size_t i = 0; i < foods_list.size(); i++) {
if (foods_list[i] == 0) {
shandong_foods = new Shandongbing;
price += shandong_foods->getPrice();
}
if (foods_list[i] == 1) {
shandong_foods = new Wuxianghuotui;
price += shandong_foods->getPrice();
}
if (foods_list[i] == 2) {
shandong_foods = new Wuxiangjipai;
price += shandong_foods->getPrice();
}
if (foods_list[i] == 3) {
shandong_foods = new Wuxiangzhupai;
price += shandong_foods->getPrice();
}
}
delete shandong_foods;
return price;
}
};
class SichuanStore :public StoreBase {
public:
SichuanStore(vector<int>foods) {
foods_list = foods;
}
string getMethods() {
string method = "";
method += first_step + "\n";
SichuanFoods *sichuan_foods = nullptr;
for (size_t i = 0; i < foods_list.size(); i++) {
if (foods_list[i] == 0) {
sichuan_foods = new Sichuanbing;
method += sichuan_foods->getMethod();
}
if (foods_list[i] == 1) {
sichuan_foods = new Malahuotui;
method += sichuan_foods->getMethod();
}
if (foods_list[i] == 2) {
sichuan_foods = new Malajipai;
method += sichuan_foods->getMethod();
}
if (foods_list[i] == 3) {
sichuan_foods = new Xianglazhupai;
method += sichuan_foods->getMethod();
}
}
delete sichuan_foods;
method += "\n" + last_step + "\n";
return method;
}
double getPrice() {
double price = 0.0;
SichuanFoods *sichuan_foods = nullptr;
for (size_t i = 0; i < foods_list.size(); i++) {
if (foods_list[i] == 0) {
sichuan_foods = new Sichuanbing;
price += sichuan_foods->getPrice();
}
if (foods_list[i] == 1) {
sichuan_foods = new Malahuotui;
price += sichuan_foods->getPrice();
}
if (foods_list[i] == 2) {
sichuan_foods = new Malajipai;
price += sichuan_foods->getPrice();
}
if (foods_list[i] == 3) {
sichuan_foods = new Xianglazhupai;
price += sichuan_foods->getPrice();
}
}
delete sichuan_foods;
return price;
}
};
(以上例子皆为虚构)
面向对象优势
模块化
面向过程编程里大量函数无逻辑地存放在一起,当函数变多时不易于管理。
面向对象编程中的函数被封装,具有一定逻辑的函数放在一起,并且当几个利用相同参数的函数封装在一个类中时,不需要重复传参。(如上图所示getMethods、getPrice函数都需要foods变量,当被封装在类中时,可以直接用构造函数接收变量值,之后便无需传参了)
保护机制
在小浩的要求中,第一步的手抓饼制作以及独家秘方的添加是必须按照总店要求做的,连锁店不能自己修改。
在面向过程编程中,这两个直接写到跟其他佐料制作中,保护机制很差。
对于面向对象方法,必须强制执行且具有机密性的两个方法写在基类的private里,这样子类就无法修改这些方法了。
易于扩展
对于面向对象编程,如果想要新增菜单或者其他地区的连锁店时,只需扩展相应类即可,控制类中改动不大。
但对于面向过程编程,控制类中仍需要更改新增的信息。
易于维护
上图中,左图是面向过程编程,右图是面向对象编程。
如果想要更改五香火腿的制作方法,对于面向对象方法,我们直接去五香火腿对应的类里修改他的制作函数即可。但对于面向过程方法,我们却需要去整个流程的制作函数中修改。
复用性强
可观察多个实现的共性以及个性,将共性封装成方法,个性则以传参的方式来适配不同业务的需求。从而提高了功能的内聚性,降低与业务代码之间的耦合性,履行封装的单一责任原则。