github地址
課程目標
- 理解松耦合設計思想
- 掌握面向對象設計原則
- 掌握重構技巧法改善設計
- 掌握GOF核心設計模式
什么是設計模式
“每一個模式描述了一個在我們周圍不斷重復發(fā)生的問題,以及該問題的解決方案的核心。這樣,你就能一次又一次地使用該方案而不必做重復勞動”
——Christopher Alexan
GOF設計模式
“可復用”才是設計模式的目標。
- 歷史性著作《設計模式:可復用面向對象軟件的基礎》一書中描述了23種經典面向對象設計模式,創(chuàng)立了模式在軟件設計中的地位。
- 由于《設計模式》一書確定了設計模式的地位,通常所說的設計模式隱含的表示“面向對象設計模式”。但并不意味著“設計模式”就等于“面向對象設計模式”。
從面向對象談起
底層思維:向下,如何把握機器底層從微觀理解對象構造
- 語言構造
- 編譯轉換
- 內存模型
- 運行機制
程序員:與上下兩者溝通,起到連接、轉化的作用
抽象思維:向上,如何將我們的周圍世界抽象為程序代碼
- 面向對象
- 組件封裝
- 設計模式
- 架構模式
深入理解面向對象
向下:深入理解三大面向對象機制
封裝:隱藏內部實現
繼承:復用現有代碼
多態(tài):改寫對象行為
向上:深刻把握面向對象機制所帶來的抽象意義,理解如何使用這些機制來表達現實世界,掌握什么是“好的面向對象設計”
軟件設計固有的復雜性
建筑商從來不會去想給一棟已建好的100層高的樓房底下再新修一個小地下室——這樣做花費極大而且注定要失敗。然而令人驚奇的是,軟件系統(tǒng)的用戶在要求作出類似改變時卻不會仔細考慮,而且他們認為這只是需要簡單編程的事
——Grady Booch in 《Object-Oriented Analysis and Design with Applications》
軟件設計復雜的根本原因
變化:
- 客戶需求的變化
- 技術平臺的變化
- 開發(fā)團隊的變化
- 市場環(huán)境的變化
如何解決復雜性?
分解:
人們面對復雜性有一個常見的做法:即分而治之,將大問題分解為多個小問題,將復雜問題分解為多個簡單問題。
抽象:
更高層次來講,人們處理復雜性有一個通用的技術,即抽象。
由于不能掌握全部的復雜對象,我們選擇忽視它的非本質細節(jié),而去處理泛化和理想化了的對象模型。
結構化VS.面向對象demo
分解示例:
//Shape1.h
class Point{
public:
int x;
int y;
};
class Line{
public:
Point start;
Point end;
Line(const Point& start, const Point& end){
this->start = start;
this->end = end;
}
};
class Rect{
public:
Point leftUp;
int width;
int height;
Rect(const Point& leftUp, int width, int height){
this->leftUp = leftUp;
this->width = width;
this->height = height;
}
};
//增加
class Circle{
};
//MainForm1.cpp
class MainForm : public Form {
private:
Point p1;
Point p2;
vector<Line> lineVector;
vector<Rect> rectVector;
//改變
vector<Circle> circleVector;
public:
MainForm(){
//...
}
protected:
virtual void OnMouseDown(const MouseEventArgs& e);
virtual void OnMouseUp(const MouseEventArgs& e);
virtual void OnPaint(const PaintEventArgs& e);
};
void MainForm::OnMouseDown(const MouseEventArgs& e){
p1.x = e.X;
p1.y = e.Y;
//...
Form::OnMouseDown(e);
}
void MainForm::OnMouseUp(const MouseEventArgs& e){
p2.x = e.X;
p2.y = e.Y;
if (rdoLine.Checked){
Line line(p1, p2);
lineVector.push_back(line);
}
else if (rdoRect.Checked){
int width = abs(p2.x - p1.x);
int height = abs(p2.y - p1.y);
Rect rect(p1, width, height);
rectVector.push_back(rect);
}
//改變
else if (...){
//...
circleVector.push_back(circle);
}
//...
this->Refresh();
Form::OnMouseUp(e);
}
void MainForm::OnPaint(const PaintEventArgs& e){
//針對直線
for (int i = 0; i < lineVector.size(); i++){
e.Graphics.DrawLine(Pens.Red,
lineVector[i].start.x,
lineVector[i].start.y,
lineVector[i].end.x,
lineVector[i].end.y);
}
//針對矩形
for (int i = 0; i < rectVector.size(); i++){
e.Graphics.DrawRectangle(Pens.Red,
rectVector[i].leftUp,
rectVector[i].width,
rectVector[i].height);
}
//改變
//針對圓形
for (int i = 0; i < circleVector.size(); i++){
e.Graphics.DrawCircle(Pens.Red,
circleVector[i]);
}
//...
Form::OnPaint(e);
}
抽象示例:
//Shape2.h
class Shape{
public:
virtual void Draw(const Graphics& g)=0;
virtual ~Shape() { }
};
class Point{
public:
int x;
int y;
};
class Line: public Shape{
public:
Point start;
Point end;
Line(const Point& start, const Point& end){
this->start = start;
this->end = end;
}
//實現自己的Draw,負責畫自己
virtual void Draw(const Graphics& g){
g.DrawLine(Pens.Red,
start.x, start.y,end.x, end.y);
}
};
class Rect: public Shape{
public:
Point leftUp;
int width;
int height;
Rect(const Point& leftUp, int width, int height){
this->leftUp = leftUp;
this->width = width;
this->height = height;
}
//實現自己的Draw,負責畫自己
virtual void Draw(const Graphics& g){
g.DrawRectangle(Pens.Red,
leftUp,width,height);
}
};
//增加
class Circle : public Shape{
public:
//實現自己的Draw,負責畫自己
virtual void Draw(const Graphics& g){
g.DrawCircle(Pens.Red,
...);
}
};
//MainForm2.cpp
class MainForm : public Form {
private:
Point p1;
Point p2;
//針對所有形狀
vector<Shape*> shapeVector;
public:
MainForm(){
//...
}
protected:
virtual void OnMouseDown(const MouseEventArgs& e);
virtual void OnMouseUp(const MouseEventArgs& e);
virtual void OnPaint(const PaintEventArgs& e);
};
void MainForm::OnMouseDown(const MouseEventArgs& e){
p1.x = e.X;
p1.y = e.Y;
//...
Form::OnMouseDown(e);
}
void MainForm::OnMouseUp(const MouseEventArgs& e){
p2.x = e.X;
p2.y = e.Y;
if (rdoLine.Checked){
shapeVector.push_back(new Line(p1,p2));
}
else if (rdoRect.Checked){
int width = abs(p2.x - p1.x);
int height = abs(p2.y - p1.y);
shapeVector.push_back(new Rect(p1, width, height));
}
//改變
else if (...){
//...
shapeVector.push_back(circle);
}
//...
this->Refresh();
Form::OnMouseUp(e);
}
void MainForm::OnPaint(const PaintEventArgs& e){
//針對所有形狀
for (int i = 0; i < shapeVector.size(); i++){
shapeVector[i]->Draw(e.Graphics); //多態(tài)調用,各負其責
}
//...
Form::OnPaint(e);
}
結論:當出現變化時,如需求變更,第二種設計方法需要改變的地方很少,復用性很好,而第一種設計方法的復用性很差
軟件設計的目標
什么是好軟件設計?軟件設計的金科玉律:
復用!