C++常见面向对象法则
1 开闭法则: 允许扩展代码 不允许修改源代码。
2 迪米特法则: 又叫最少知识原则,当各自类对象直接访问类内部结构时,不符合最少知识原则,应使用一个接口类来访问,避免直接访问内部 。
下面先给出不符合迪米特法则的例子:
//迪米特法则 又叫最少知识原则
//抽象建筑
class AbstractBuild{
public:
virtual void sale()=0; // 应该在继承的类重写,抽象类避免做太多的工作
public:
string m_quality; // 也可在各自类写
};
//具体建筑楼盘A
class BuildingA : public AbstractBuild{
public:
BuildingA(){
m_quality="高品质";
}
virtual void sale(){
cout<<"楼盘A:"<<m_quality<<"已售出"<<endl;
}
};
//具体建筑楼盘B
class BuildingB : public AbstractBuild{
public:
BuildingB(){
m_quality="低品质";
}
virtual void sale(){
cout<<"楼盘B:"<<m_quality<<"已售出"<<endl;
}
};
//客户端
void test01(){
//各自类对象直接对接类内部结构 不符合最少知识原则 应使用一个接口类来访问 避免直接访问内部
BuildingA *a=new BuildingA;
if (a->m_quality == "低品质"){
a->sale();
}
BuildingB *b=new BuildingB;
if (b->m_quality == "低品质"){
b->sale();
}
delete a;
delete b;
}
所以下面给出一个中介类,使对象间接访问类内部。
//中介类 用于管理楼盘
class Mediator{
public:
//所以初始化构造时将两个楼盘放进去给中介管理
//如果想动态增加楼盘 可以自行增加一个Push方法动态接收new出来的具体楼盘放进中介容器成员中
Mediator(){
AbstractBuild *a=new BuildingA;
AbstractBuild *b=new BuildingB;
m_abv.push_back(a);
m_abv.push_back(b);
}
//对外提供接口 寻找有无合适的楼盘 所以返回值为楼盘
AbstractBuild* findBuilding(string quality){
for(vector<AbstractBuild*>::iterator it=m_abv.begin();it!=m_abv.end();it++){
if((*it)->m_quality==quality){
return *it; //找到就返回楼盘
}
}
return NULL;
}
//将中介释放
~Mediator(){
for(vector<AbstractBuild*>::iterator it=m_abv.begin();it!=m_abv.end();it++){
if((*it)!=NULL){ //*it就是<AbstractBuild*>尖括号里的值
delete *it;
}
}
}
public:
vector<AbstractBuild*> m_abv;
};
//客户端
void test02(){
//利用中介取找符合客户的楼盘
Mediator med;
AbstractBuild* building = med.findBuilding("高品质");
if (building != NULL){
building->sale();
}
else{
cout << "没有符合您条件的楼盘!" << endl;
}
}
int main(){
//test01();
test02();
return 0;
}
总结迪米特法则:当各自类对象直接访问类内部结构时,不符合最少知识原则,应使用一个接口类来访问,避免直接访问内部 。例子,楼盘通过中介类构造对象完毕后,提供装有楼盘信息的容器,别人就可以通过中介了解楼盘类内部结构。
3 合成复用法则:继承和组合,优先使用组合。
照样先给出不符合合成复用的例子先:
//抽象车
class AbstractCar{
public:
virtual void run() = 0;
};
//大众车
class Dazhong :public AbstractCar{
public:
virtual void run(){
cout << "大众车启动..." << endl;
}
};
//拖拉机
class Tuolaji :public AbstractCar{
public:
virtual void run(){
cout << "拖拉机启动..." << endl;
}
};
//如果想要开某种车 必须重新定义类 继承具体的车类 这样使得代码冗余度高 不符合合成复用法则
class Person : public Tuolaji{
public:
void Doufeng(){
run();
}
};
class PersonB : public Dazhong{
public:
void Doufeng(){
run();
}
};
以上说明人如果想开车,必须重新定义人继承具体 的车,导致已经写了的具体车类没能重复利用。使用合法复用法则后:
class Person{
public:
void setCar(AbstractCar* car){ //设置完要开什么车后就可以直接调用下面的开车函数了
this->car = car;
}
void Doufeng(){
this->car->run();
if (this->car != NULL){
delete this->car;
this->car = NULL;
}
}
public:
AbstractCar* car; //使用组合 使抽象类作为其成员 继而人可以开各种车 人相当于楼盘的中介类
};
void test02(){
Person* p = new Person;
//设置使用大众车
p->setCar(new Dazhong);
p->Doufeng();
//设置使用拖拉机
p->setCar(new Tuolaji);
p->Doufeng();
delete p;
}
//继承和组合 优先使用组合
int main(void){
test02();
return 0;
}
这样,当我们将抽象车类与人组合后,只需要告诉人Set使用什么车,人就能兜风了。
总结合成复用法则:继承和组合,优先使用组合。例子,人使用哪种车兜风。人将车作为成员组合在一起,重复的使用了具体车类的代码。 此例它也符合迪米特法则,实际上它们有点相似,都将楼盘、中介和车、人组合在了一起。
4 依赖倒转法则:依赖抽象类 不依靠具体类。
传统的依赖调用:底层依赖中层,中层依赖上层,一层一层依赖。
//传统的从上往下依赖
//上层
class BankWorker{
public:
void payService(){
cout<<"支付服务"<<endl;
}
void saveService(){
cout<<"存款服务"<<endl;
}
void transferService(){
cout<<"转账服务"<<endl;
}
};
#if 0
//中层 用普通函数写好很多 否则这样写也会继承基类所有的函数 还不如直接调用基类的
class payWorker:public BankWorker{
public:
void service(BankWorker *b){
b->payService();
}
};
class saveWorker:public BankWorker{
public:
void service(BankWorker *b){
b->saveService();
}
};
class transferWorker:public BankWorker{
public:
void service(BankWorker *b){
b->transferService();
}
};
#endif
//中层模块--相当于具体类
void doSaveBussiness(BankWorker* worker){
worker->saveService();
}
void doPayBussiness(BankWorker* worker){
worker->payService();
}
void doTransferBussiness(BankWorker* worker){
worker->transferService();
}
//底层
void test01(){
BankWorker *b=new BankWorker;
//传统依赖原则调用 该层依赖中层实现 中层依赖上层实现
doSaveBussiness(b);
delete b;
}
依赖倒转法则:中层依赖抽象层,底层依赖抽象层。
// 抽象层 可以看做上层
class abstractBankWorker{
public:
virtual void service()=0;
};
//实现层 依赖抽象层 可以看做中层
class payWorker:public abstractBankWorker{
public:
virtual void service(){
cout<<"支付服务"<<endl;
}
};
class saveWorker:public abstractBankWorker{
public:
virtual void service(){
cout<<"存款服务"<<endl;
}
};
class transferWorker:public abstractBankWorker{
public:
virtual void service(){
cout<<"转账服务"<<endl;
}
};
//底层 同样依赖抽象层 --这样就将传统需要三个底层的普通函数转成了一个万能的可扩展的函数
void doBankWork(abstractBankWorker *work){
work->service();
delete work;
}
void test02(){
//想要存款服务
doBankWork(new saveWorker);
//想要转账服务
doBankWork(new transferWorker);
//想要支付服务
doBankWork(new payWorker);
}
int main(){
//test01();
test02();
return 0;
}
总结依赖导致原则:依赖抽象类,不依靠具体类。例子,银行工作人员提供的三种服务。具体类员工分别继承依赖抽象类提供一种服务,最后底层利用普通函数依赖抽象类实现相应服务。
最后对比加强记忆:
1 开闭法则: 允许扩展代码 不允许修改源代码。
2 总结迪米特法则:当各自类对象直接访问类内部结构时 不符合最少知识原则 应使用一个接口类来访问 避免直接访问内部 。例子,楼盘通过中介类构造对象完毕后,提供装有楼盘信息的容器,别人就可以通过中介了解楼盘类内部结构。
3 总结合成复用法则:继承和组合,优先使用组合。例子,人使用哪种车兜风。人将车作为成员组合在一起,重复的使用了具体车类的代码。 此例它也符合迪米特法则,实际上它们有点相似,都将楼盘、中介和车、人组合在了一起。
4 总结依赖导致原则:依赖抽象类,不依靠具体类。例子,银行工作人员提供的三种服务。具体类员工分别继承依赖抽象类提供一种服务,最后底层利用普通函数依赖抽象类实现相应服务。
面试时按照以上来答大概意思就基本没问题了。