(李林老師linux環(huán)境高級編程課后筆記)
架構的核心就是做到代碼封閉性黄橘,即來了新的需求不用改老代碼亥至,只用增加新代碼
核心要素:來一個變化點就新建一個體系結構
---李林老師
背景是實現一個加法器悼沈,對象中有一個數據成員作被加數贱迟,一個成員函數函數傳入加數姐扮,返回和。
此加法器類實現起來很簡單衣吠,如下代碼
#include <iostream>
using namespace std;
class CLAdder
{
public:
explicit CLAdder(int iAugend)
{
m_iAugend = iAugend;
}
int Add(int iAddend)
{
return m_iAugend + iAddend;
}
private:
int m_iAugend;
};
int main()
{
CLAdder adder(2);
cout << adder.Add(4) << endl;
return 0;
}
現在來了一個變化點(新的需求):需要實現帶權重的加法器的功能茶敏。
即輸出 被加數*權重+加數
此時用 來一個變化點就新建一個繼承體系 的思想,用c++面向對象思想來實現缚俏。
-
面向對象的思想(繼承+多態(tài))
#include <iostream>
using namespace std;
class CLAdder
{
public:
explicit CLAdder(int iAugend)
{
m_iAugend = iAugend;
}
virtual ~CLAdder()
{
}
virtual int Add(int iAddend)
{
return m_iAugend + iAddend;
}
protected:
int m_iAugend;
};
class CLWeightingAdder : public CLAdder
{
public:
CLWeightingAdder(int iAugend, int iWeight) : CLAdder(iAugend)
{
m_iWeight = iWeight;
}
virtual ~CLWeightingAdder()
{
}
virtual int Add(int iAddend)
{
return m_iAugend * m_iWeight + iAddend;
}
protected:
int m_iWeight;
};
int main()
{
//基類對象(不帶權重的加法器)
CLAdder adder(2);
//基類指針指向基類對象
CLAdder * p = &adder;
cout << "CLAdder:" << p->Add(4) << endl;
//派生類對象(帶權重的加法器)
CLWeightingAdder wadder(3, 4);
//基類指針指向派生類對象
p = &wadder;
cout << "CLWeightAdder:" << p->Add(4) << endl;
return 0;
}
c++中的多態(tài)思想惊搏,核心就是 基類指針指向派生類對象 ,c++的這個多態(tài)性質是虛函數機制支撐的忧换。具體可以看另一個筆記:虛函數和虛表初步
- 這種程序設計思想在架構中的好處
在測試代碼中恬惯,用戶都是對基類指針進行操作,來了變化點也是對p進行操作亚茬,在復雜的業(yè)務環(huán)境中如
fun(CLAdder * p)
{
...
}
中酪耳,對fun的使用,即使來了變化點(加了權重)刹缝,也不用對原本的fun函數進行修改碗暗,照樣可以用,只要傳進去的是派生類對象就可以梢夯,即做到了代碼的封閉性言疗,只加新代碼不改老代碼。
但是颂砸,這種設計方法也有不足的情況和更好的方案及基于接口的思想
此時又來了一個變化點噪奄,若基本的加法器是只能由兩個無符號整數相加,這時實現一個有符號整數的人乓、帶權重的加法器勤篮。誠然,依據來一個變化點就加一個繼承體系的思想撒蟀,可以再接著設計一個派生類叙谨,加一個有符號數的數據成員,和自己的加法函數保屯。但是手负,這樣的話涤垫,派生類就繼承了不必要的數據成員,產生了很多不必要的浪費竟终。這樣的設計卻是犯了過度設計的缺點蝠猬,并不是所有的變化點都是需求,此時有更好的方案统捶。即基于接口的思想
-
基于接口的思想
#include <iostream>
using namespace std;
class ILAdder
{
public:
ILAdder()
{
}
virtual ~ILAdder()
{
}
virtual int Add(int iAddend) = 0;
};
class CLAdder : public ILAdder
{
public:
explicit CLAdder(unsigned int iAugend)
{
m_iAugend = iAugend;
}
virtual ~CLAdder()
{
}
virtual int Add(int iAddend)
{
return m_iAugend + iAddend;
}
private:
unsigned int m_iAugend;
};
class CLWeightingAdder : public ILAdder
{
public:
CLWeightingAdder(int iAugend, int iWeight)
{
m_iWeight = iWeight;
m_iAugend = iAugend;
}
virtual ~CLWeightingAdder()
{
}
virtual int Add(int iAddend)
{
return m_iAugend * m_iWeight + iAddend;
}
private:
int m_iAugend;
int m_iWeight;
};
void f(ILAdder *pAdder)
{
cout << pAdder->Add(4) << endl;
}
int main()
{
CLAdder adder(2);
f(&adder);
CLWeightingAdder wadder(3, 4);
f(&wadder);
return 0;
}
在此思想中用到了c++中的另一個機制榆芦,虛基類和純虛函數。虛基類的實現為空喘鸟,成員函數都為純虛函數匆绣,就是要給子類重寫用的。
-
這種設計思想的好處
在測試代碼和原來的程序中什黑,f()函數的參數類型是虛基類即接口的指針崎淳,根據傳入的對象類型不同,調用的是不同的加法器的函數愕把。并沒有在來了需求后改老的代碼( f() )拣凹,也做到了代碼的封閉性。
對比和收獲
- 本質上都是多態(tài)的思想恨豁,面向對象的思想我理解其實是繼承的思想嚣镜,基于接口的思想是把原來的父子關系變成了兄弟關系。
- 面向對象的思想是拿著基類指針來干活橘蜜,基于接口的思想是拿著虛基類即接口的指針來干活菊匿。
- 繼承的思想是一種強耦合關系,無論需不需要扮匠,子類都完全繼承了基類的數據成員捧请,基類變了,所有的派生類都要變棒搜,耦合與接口(函數)疹蛉,耦合與實現(數據成員)。所以能少用繼承就少用繼承力麸。
- 想起以前設計類的時候可款,只會用繼承關系,當時就覺得基類有些數據成員和函數其實派生類并不需要克蚂,但是又覺得派生類確實是要跟基類有聯系的闺鲸,就無腦用了繼承,現在覺得耦合度確實太強了埃叭,并且會造成不必要的資源消耗摸恍。以后可以考慮接口的思想。
架構本質上就是解耦的過程,但是會增加很多的中間結構立镶,可能會降低性能壁袄,需要在設計的時候來權衡。