SlideShare a Scribd company logo
1 of 107
如何寫好程式?v2.6
要說
如何寫好軟體
也可以!
Chris
適合有程式碼作品的coder看
本授權條款允許使用者重製、散布、傳輸著作,但不得為商業目的之使用,亦不得
修改該著作。使用時必須按照著作人指定的方式表彰其姓名。
Why do this?
被看
的時間
被寫+被修改
的時間
??
一行程式碼
被看
的時間
被寫+被修改
的時間>>
不
是
你
的
問
是程式設計師的問題
看不懂
別人的程式碼
或者是….
英文不好
程式碼是
設計作品
《What is Software Design?》-Jack W. Reeves ©C++ Journal - 1992
不是藝術品
軟體設計
軟體 + 設計
這不是廢話嗎?
設計限制+錯誤→演化
沒有一步登天
這回事
軟體
管理複雜性
《人月神話》的《沒有銀彈》
首要技術使命
管理複雜性
管理 + 複雜性
本質性問題:「複雜性」
無法改變也沒有殺手級工具可以解決的問題。
附屬性問題:「管理」
自C語言發明之後,軟體技術都在解決的事。
《人月神話》的《沒有銀彈》
沒救了?
學不完?
先用物件導向看軟體設計
物件導向
不是銀彈
只用來解決
附屬性問題
先用物件導向看軟體設計
//main.c
#include ”stdio.h”
int main()
{
printf(“Hello! Worldn”);
return 0;
}
先用物件導向看軟體設計
//main.c
#include ”stdio.h”
void Show()
{
printf(“Hello! Worldn”);
}
int main()
{
Show();
return 0;
}
某特定功能
設定成函數
先用物件導向看軟體設計
//main.cpp
#include ”cstdio”
#include ”X.h”
int main()
{
X one;
one.Show();
return 0;
}
//X.h
#include ”cstdio”
class X
{
void Show();
}
//X.cpp
#include ”X.h”
void X::Show()
{
printf(“Hello! Worldn”);
}
用一個類別
(實現成一個物件)
來做這個動作
先用物件導向看軟體設計
優點
– 建立物件,管理程式碼
– 模組化呼叫
缺點
– 使用了大量的記憶體空間
– 呼叫之間,浪費時間成本
先用物件導向看軟體設計
物件導向
提供一套設計工具,讓程式設計師方便管理程式碼。
其實像是…心智圖
明明就複雜了
怎麼還會是更好的管理
應該是簡潔才對呀
哪有比較「好」?
main
Obj
Obj
int
float
char
把程式寫好
good Manage is good Design
附屬性問題處理好
就是「比較好」
實作前須知
好的方向
注重整體概念性
隱藏細節
集中高頻率修改的部份
保持高內聚、低耦合
-《人月神話》
-《Design Pattern》
實作後須知
好的code
可讀性高
好維護
效率高
隱藏部份細節
接下來
就是
How to do
好!
你說的我都同意
怎麼做呢?
電腦時代 初期 1970~1980 20世紀
思考程式 陳述式 函式 設計的概念
語言表示 (算式) (副程式) (物件導向)
「資料」+「運用資料的操作」
不管時代的演進
本質上都是這樣
抽
象
資
料
型
別
跳出關鍵字
的限制
第一步
資料
+
運用資料的操作
(ADT)
建立抽象概念,用程式語言表達。
而不是用程式碼來建構流程(而已)
http://zh.wikipedia.org/zh-tw抽象資料型別
“the distinction between programming in a language vs. programming into a language.”
《code complete2》
程式碼的函數(動詞)中
找出物件(主詞)
S+V
Obj.Fun()
簡單的說
就文法來看
抽象資料型別
如果沒有ADT也可以呀!(是嗎?)
(直接存取資料也可以呀!是嗎?)
(見招拆招法)
問題: 設定字型大小
在此,是假設宣告了一個struct,直接存取成員
currentFont.size = 16;
//單位是??
currentFont.size = PointsToPixels(12);
//若有建一組程式庫副程式
//12的單位是px, size的單位是?
currentFont.sizeInPixels = PointsToPixels(12);
//提供更具意義的樣式名稱
//這樣一來,沒有ADT
介面的好處
抽象資料型別
那要設定12px和12pt呢?
(pt: point, px: pixel)
currentFont.sizeInPixels = SetPixels(12);
currentFont.sizeInPixels = PointsToPixels(12);
currentFont.sizeInPoints = SetPoint(12);
currentFont.sizeInPoints = PixelToPoint(12);
介面的好處
直接這樣寫
如何整合程式??
抽象資料型別
問題: 把字型設定成粗體
currentFont.attribute = currentFont.attribute | 0x02;
currentFont.attribute = currentFont.attribute | BOLD;
//簡單狀況,寫出比上面的例子更清楚
currentFont.bold = true;
介面的好處
直接控制資料成員,限
制了currentFont的使
用方式
抽象資料型別
使用ADT
currentFont.attribute = currentFont.attribule | 0x02
//檢查這種程式碼是煩人
//可能出現錯誤的結構名稱、欄位名稱、運算子、屬性值...
currentFont.SetBold (true)
//檢查這種程式碼的正確性,是小菜一碟
//可能出現錯誤的常式名稱
0x02改成BOLD
但是都不會比SetBold(true)好讀
(相差30%的錯誤發生率)
抽象資料型別
使用ADT
currentFont.SetSizeInPoints(sizeInPoints);
currentFont.SetSizeInPixels(sizeInPixels);
currentFont.SetBoldOn();
currentFont.SetBoldOff();
currentFont.SetItalicOn();
currentFont.SetItalicOff();
currentFont.SetTypeFace( faceName );
和見招拆招法的差別之處,將字型操作隔離在一組常式之中。
(用文字取代算式之後,看不見算式就是「隔離」)
ADT用起來的感覺…
不是在程式中把資料傳來傳去
操弄現實世界的實體
非僅止於低階的程式實作結構
沒有物件導向
也可以ADT
介面的好處
抽象資料型別
回到ADT(抽象資料型別)
懂得抽象
用文字表達想法
抽象:抽出足以表達物件的特徵,象徵物件本身和運作
無關程式技巧,這是一種表達能力
參考書目:《C基礎講座》-村山公保
抽象資料型別
currentFont.SetSize(sizeInPoints);
currentFont.SetBoldOn();
currentFont.SetBoldOff();
currentFont.SetItalicOn();
currentFont.SetItalicOff();
currentFont.SetTypeFace(faceName);
SetCurrentFontSize(sizeInPoints);
SetCurrentFontBoldOn();
SetCurrentFontBoldOff();
SetCurrentFontItalicOn();
SetCurrentFontItalicOff();
SetCurrentFontFontTypeFace(faceName);
有物件導向
(以C++為範例)
無物件導向
(以C為範例)
OO vs unOO
抽象資料型別
currentFont.SetSize(sizeInPoints);
currentFont.SetBoldOn();
currentFont.SetBoldOff();
currentFont.SetItalicOn();
currentFont.SetItalicOff();
currentFont.SetTypeFace(faceName);
SetCurrentFontSize(sizeInPoints);
SetCurrentFontBoldOn();
SetCurrentFontBoldOff();
SetCurrentFontItalicOn();
SetCurrentFontItalicOff();
SetCurrentFontFontTypeFace(faceName);
有物件導向
(以C++為範例)
無物件導向
(以C為範例)
OO vs unOO
主詞
建立良好
的
介
面
第二步
維持
整體概念性
介面
函數宣告
函數名稱+參數
先說
建立良好的介面,我們先來看看什麼是介面,它可以做什麼
好的程式碼,看得快?
因為容易猜中程式要做的行為
程式語言用function表達行為
http://zh.wikipedia.org/zh-tw抽象資料型別
Why?
好猜的動詞
簡單的說
就文法來看
別再幫函數取這樣的名字
void doSomething();
用介面名稱
當作註解
要怎麼
善用介面?
看個例子
猜猜看這要做什麼?
.raw的圖檔,做smooth
程式碼當註解#include <iostream>
#include <fstream>
using namespace std;
#define frameH 1920
#define frameV 1080
int main()
{
unsigned char *fp
= new unsigned char [frameH*frameV];
fstream file;
file.open("W:a.raw",
fstream::in | fstream::binary);
if( !file.good())
cout << “File Open Error!" << endl;
file.read((char*) fp, frameH*frameV);
//轉態unsigned char* 轉態成 char* 放入函式
file.close();
int *ifp = new int [frameH*frameV];
for ( int i = 0 ; i < frameH ; ++i ){
for ( int j = 0 ; j < frameV ; ++j ){
*(ifp+i+j*frameH) = *(fp+i+j*frameH);
}}
int kernel;
for ( int i = 1 ; i < frameH-1 ; ++i ){
for ( int j = 1 ; j < frameV-1 ; ++j ){
kernel =
*(ifp + (i-1) + (j-1)* frameH) +
*(ifp + i + (j-1)* frameH) +
*(ifp + (i+1) + (j-1)* frameH) +
*(ifp + (i-1) + j * frameH) +
*(ifp + i + j * frameH) +
*(ifp + (i+1) + j * frameH) +
*(ifp + (i-1) + (j+1)* frameH) +
*(ifp + i + (j+1)* frameH) +
*(ifp + (i+1) + (j+1)* frameH);
*(ifp+i+j*frameH) = (temp /9);
}}
for ( int i = 0 ; i < frameH ; ++i ){
for ( int j = 0 ; j < frameV ; ++j ){
*(fp+i+j*frameH) = *(ifp+i+j*frameH);
}}
fstream ofile;
ofile.open("W:a_smooth.raw",
fstream::out | fstream::binary);
if( !ofile.good())
cout << “File Save Error!" << endl;
ofile.write((char*) fp, frameH*frameV);
//轉態unsigned char* 轉態成 char* 放入函式
ofile.close();
delete [] fp;
delete [] ifp;
system("PAUSE");
return 0;
}
#include <iostream>
#include <fstream>
using namespace std;
#define frameH 1920
#define frameV 1080
int main()
{
unsigned char *fp
= new unsigned char [frameH*frameV];
if( !openRawImage(fp, "W:a.raw") )
cout << “File Open Error!" << endl;
smoothImage(fp);
if( !saveRawImage(fp, "W:a_smooth.raw") )
cout << “File Save Error" << endl;
delete [] fp;
system("PAUSE");
return 0;
}
程式碼當註解
程式碼當註解#include <iostream>
#include <fstream>
using namespace std;
#define frameH 1920
#define frameV 1080
int main()
{
unsigned char *fp
= new unsigned char [frameH*frameV];
fstream file;
file.open("W:a.raw",
fstream::in | fstream::binary);
if( !file.good())
cout << “File Open Error!" << endl;
file.read((char*) fp, frameH*frameV);
//轉態unsigned char* 轉態成 char* 放入函式
file.close();
int *ifp = new int [frameH*frameV];
for ( int i = 0 ; i < frameH ; ++i ){
for ( int j = 0 ; j < frameV ; ++j ){
*(ifp+i+j*frameH) = *(fp+i+j*frameH);
}}
int temp;
for ( int i = 1 ; i < frameH-1 ; ++i ){
for ( int j = 1 ; j < frameV-1 ; ++j ){
temp =
*(ifp + (i-1) + (j-1)* frameH) +
*(ifp + i + (j-1)* frameH) +
*(ifp + (i+1) + (j-1)* frameH) +
*(ifp + (i-1) + j * frameH) +
*(ifp + i + j * frameH) +
*(ifp + (i+1) + j * frameH) +
*(ifp + (i-1) + (j+1)* frameH) +
*(ifp + i + (j+1)* frameH) +
*(ifp + (i+1) + (j+1)* frameH);
*(ifp+i+j*frameH) = (temp /9);
}}
for ( int i = 0 ; i < frameH ; ++i ){
for ( int j = 0 ; j < frameV ; ++j ){
*(fp+i+j*frameH) = *(ifp+i+j*frameH);
}}
fstream ofile;
ofile.open("W:a_smooth.raw",
fstream::out | fstream::binary);
if( !ofile.good())
cout << “File Save Error!" << endl;
ofile.write((char*) fp, frameH*frameV);
//轉態unsigned char* 轉態成 char* 放入函式
ofile.close();
delete [] fp;
delete [] ifp;
system("PAUSE");
return 0;
}
程式碼當註解#include <iostream>
#include <fstream>
using namespace std;
#define frameH 1920
#define frameV 1080
int main()
{
unsigned char *fp
= new unsigned char [frameH*frameV];
fstream file;
file.open("W:a.raw",
fstream::in | fstream::binary);
if( !file.good())
cout << “File Open Error!" << endl;
file.read((char*) fp, frameH*frameV);
//轉態unsigned char* 轉態成 char* 放入函式
file.close();
int *ifp = new int [frameH*frameV];
for ( int i = 0 ; i < frameH ; ++i ){
for ( int j = 0 ; j < frameV ; ++j ){
*(ifp+i+j*frameH) = *(fp+i+j*frameH);
}}
int temp;
for ( int i = 1 ; i < frameH-1 ; ++i ){
for ( int j = 1 ; j < frameV-1 ; ++j ){
temp =
*(ifp + (i-1) + (j-1)* frameH) +
*(ifp + i + (j-1)* frameH) +
*(ifp + (i+1) + (j-1)* frameH) +
*(ifp + (i-1) + j * frameH) +
*(ifp + i + j * frameH) +
*(ifp + (i+1) + j * frameH) +
*(ifp + (i-1) + (j+1)* frameH) +
*(ifp + i + (j+1)* frameH) +
*(ifp + (i+1) + (j+1)* frameH);
*(ifp+i+j*frameH) = (temp /9);
}}
for ( int i = 0 ; i < frameH ; ++i ){
for ( int j = 0 ; j < frameV ; ++j ){
*(fp+i+j*frameH) = *(ifp+i+j*frameH);
}}
fstream ofile;
ofile.open("W:a_smooth.raw",
fstream::out | fstream::binary);
if( !ofile.good())
cout << “File Save Error!" << endl;
ofile.write((char*) fp, frameH*frameV);
//轉態unsigned char* 轉態成 char* 放入函式
ofile.close();
delete [] fp;
delete [] ifp;
system("PAUSE");
return 0;
}
讀取檔案
寫入檔案
Smooth演算法
Smooth演算法
檔案寫入緩衝區
檔案寫入緩衝區
#include <iostream>
#include <fstream>
using namespace std;
#define frameH 1920
#define frameV 1080
int main()
{
unsigned char *fp
= new unsigned char [frameH*frameV];
if( !openRawImage(fp, "W:a.raw") )
cout << “File Open Error!" << endl;
smoothImage(fp);
if( !saveRawImage(fp, "W:a_smooth.raw") )
cout << “File Save Error" << endl;
delete [] fp;
system("PAUSE");
return 0;
}
程式碼當註解
剛剛的例子
猜到了嗎?
.raw的圖檔,做smooth
以
概
念
為
核
心
設
計
程
式
注
重
整體概念性
介面隱藏
複雜的程式碼
增加安全性
要怎麼
善用介面?
看個例子
產生ID(員工編號)的功能
一個建立ID的程式
int main()
{
int id; //告訴別人,可以對ID做四則運算。
id = ++g_maxId; //告訴別人,我們是透過累加來產生ID
return 0;
}
資訊隱藏
一個建立ID的程式
int main()
{
int id; //告訴別人,可以對ID做四則運算。
id = ++g_maxId; //告訴別人,我們是透過累加來產生ID
return 0;
}
資訊隱藏
int NewId()
{
return ++g_maxId;
}
int main()
{
int id; //告訴別人,可以對ID做四則運算。
id = NewId();
return 0;
}
資訊隱藏
typedef IdType int;
IdType NewId()
{
return ++g_maxId;
}
int main()
{
IdType id;
id = NewId();
return 0;
}
資訊隱藏
聊完了
再看一次什麼叫介面
介面
函數名稱 + 參數
這等一下講
除了好還要
才算真正好 濃、醇、香
好程式的開始
高品質+介面
好程式的開始 = 高品質 + 函數名稱 + 函數參數
降低重覆的程式碼
(別再「複製→貼上→改一下」了)
維持整體概念性
資訊隱藏(降低複雜度)
順序、指標操作、其它的一團泥
看完了介面,來看看高品質
你對軟體的了解,決定你寫程式的表現
高品質+介面
||
內聚力+介面名稱+函數參數
(ㄗㄟ)
細蝦米?
高品質=高內聚力
看個例子
內聚力??
高品質常式
去大便..你要先..
脫外褲();
脫內褲();
拉大便();
擦屁股();
穿內褲();
穿外褲();
軟體建構之道 (Code Complete) 第二版 第一章到第七章讀書心得 - 小黑宅
高品質常式
去大便..你要先..
脫外褲();
脫內褲();
拉大便();
擦屁股();
穿內褲();
穿外褲();
軟體建構之道 (Code Complete) 第二版 第一章到第七章讀書心得 - 小黑宅
順序
內聚力
高品質常式
去大便..你要先..
脫外褲();
脫內褲();
拉大便();
擦屁股();
穿內褲();
穿外褲();
軟體建構之道 (Code Complete) 第二版 第一章到第七章讀書心得 - 小黑宅
大便();
可以這
樣解決
看個例子
剛剛的例子太髒了?
那來說個笑話好了
如何把長頸鹿放進冰箱
如何把大象放進冰箱
如何用一般般的程式碼表達
如何用高品質的程式碼表達
int main
{
把長頸鹿放進冰箱();
把大象放進冰箱();
return 0;
}
void 把長頸鹿放進冰箱()
{
把冰箱打開();
把長頸鹿放進去();
把冰箱關起來();
}
void 把大象放進冰箱()
{
把冰箱打開();
把大象放進去();
把冰箱關起來();
}
暫時內聚力
聽過這笑話嗎?這其實是程式設計師的笑話
用一個函數
包起來
int main
{
把長頸鹿放進冰箱();
把大象放進冰箱();
return 0;
}
void 把長頸鹿放進冰箱()
{
把冰箱打開();
把長頸鹿放進去();
把冰箱關起來();
}
void 把大象放進冰箱()
{
把冰箱打開();
把大象放進去();
把冰箱關起來();
}
忘記把長頸鹿拿出來!
int main
{
把長頸鹿放進冰箱();
把大象放進冰箱();
return 0;
}
void 把長頸鹿放進冰箱()
{
把冰箱打開();
把長頸鹿放進去();
把冰箱關起來();
}
void 把大象放進冰箱()
{
把冰箱打開();
把長頸鹿拿出來();
把大象放進去();
把冰箱關起來();
}
int main
{
把長頸鹿放進冰箱();
把大象放進冰箱();
return 0;
}
void 把長頸鹿放進冰箱()
{
把冰箱打開();
把長頸鹿放進去();
把冰箱關起來();
}
void 把大象放進冰箱()
{
把冰箱打開();
把長頸鹿拿出來();
把大象放進去();
把冰箱關起來();
}
溝通內聚力
還要看實
作細節
處理大象放進冰箱時,還要將長頸廘拿出來,整體概念性弱
int main
{
把動物放進冰箱(長頸鹿);
把動物放進冰箱(大象);
return 0;
}
void 把動物放進冰箱(animal 動物)
{
把冰箱打開();
if (!冰箱是否清空())
清空冰箱();
放進去冰箱(動物);
把冰箱關起來();
}
高內聚
低耦合
這才是真正高內聚、低耦合
概念表達 > 程式太短浪費篇幅
別因副程式太小
而不建立小程式會長大
易於閱讀、自我文件化、重覆使用
困擾很久?
每次都猶豫
老半天?
你對軟體的了解,決定你寫程式的表現
高品質+介面
||
內聚力+介面名稱+函數參數
接著來看看參數
剩下
參數
– 形式參數
又稱為「虛擬參數」
常式宣告的變數。
– 實際參數
實際呼叫的常式中用到的變數、常數或運算式。
引數
實際使用(呼叫)函數時丟進去的值
先弄懂參數與引數的差別
專有名詞定義
看個例子
形式參數?實際參數?引數?
#include <stdio.h>
void show(char* showMe);
int main()
{
char* sampleHello = “Hello~”;
show(sampleHello);
return 0;
}
void show(char* str)
{
printf(“%s”, str);
}
#include <stdio.h>
void show(char* showMe);
int main()
{
char* sampleHello = “Hello~”;
show(sampleHello);
return 0;
}
void show(char* str)
{
printf(“%s”, str);
}
形式參數
實際參數
引數
(verilog)使用指令引數對應參數
functionA(.iClk(iClk),.iData(iData),.oData(oData));
functionA(.iData(iData),.iClk(iClk),.oData(oData));
這兩個呼叫同義。
void funName(CONTROLS, INPUT, OUTPUT, ERROR);
順序
隱喻功能,參數好記憶
加上前綴字,即可以看懂參數的作用
ex: iClk, iData, oData, oError, oStatus
看個例子
傳物件好?還是傳值好?
ex: 兩點畫出一條線
struct point
{
int x;
int y;
}
void drawLine(int startX, int startY, int endX, int endY){};
void drawLine(point start, point end){};
傳物件好?還是傳值好?
struct point
{
int x;
int y;
int z;
}
void drawLine(int startX, int startY, int startZ, int endX, int endY, int endZ){};
void drawLine(point start, point end){};
struct point
{
int x;
int y;
}
void drawLine(int startX, int startY, int endX, int endY){};
void drawLine(point start, point end){};
傳物件好?還是傳值好?
struct point
{
int x;
int y;
int z;
}
void drawLine(int startX, int startY, int startZ, int endX, int endY, int endZ){};
void drawLine(point start, point end){};
傳參數
傳參數
A做法:function( obj.getMem1(), obj.getMem2(), obj.getMem3() );
優點:
維持最少的常式間聯結數
減少藕合,常式易於重覆使用
缺點:
公開常式使用的成員項目→有違封裝
struct point
{
int x;
int y;
}
void drawLine(int startX, int startY, int endX, int endY){};
void drawLine(point start, point end){};
傳物件好?還是傳值好?
struct point
{
int x;
int y;
int z;
}
void drawLine(int startX, int startY, int startZ, int endX, int endY, int endZ){};
void drawLine(point start, point end){};
傳物件
傳物件
B做法:function( obj );
優點:
增加使用額外物件成員的彈性
(因為介面穩定)
缺點:
十個公開的存取常式,在函式內有存取的可能性→有違封裝
再看個例子
傳物件好?還是傳值好?
ex: 計算員工節日假
LookupVacationBenefit ( Employee _E );
//otherClass 就不可以丟進去當參數了。
LookupVacationBenefit ( date _D, level _L );
//兩個類別都可以丟進去執行了。
eD = Employee.GetDate();
eL = Employee.GetLevel();
oD = otherClass.GetDate();
oL = otherClass.GetLevel();
LookupVacationBenefit ( eD, oL );
LookupVacationBenefit ( oD, oL );
傳物件好?還是傳值好?
傳物件
傳參數
正職員工?
派遣員工?
作業員?
以
概
念
為
核
心
設
計
程
式
注
重
整體概念性
還是這一頁
return
內聚力+介面名稱+函數參數+
通通void
無煩惱
建構函式有兩種情況
1. 有return值的函式
2. 無return值的函式
這問題有
問題嗎?
用return?
什
麼
時
候
要
看個例子
ex: 編碼RGB
if (encode(R, G, B) > 128)
;
這一行有
問題嗎?
R
G
B
Y
U
V
R
G
B
maxPixel
minPixel
R
G
B
Gray
void encode(char iR, char iG, char iB, char* oY, char* oU, char* oV)
void encode(char iR, char iG, char iB, char* maxSubPixel, char* minSubPixel)
char encode(char iR, char iG, char iB)
void encode(char iR, char iG, char iB, char* Gray)
編碼RGB
有幾種編法?
?
R
G
B
Y
U
V
R
G
B
maxPixel
minPixel
R
G
B
Gray
void encode(char iR, char iG, char iB, char* oY, char* oU, char* oV)
void encode(char iR, char iG, char iB, char* maxSubPixel, char* minSubPixel)
char encode(char iR, char iG, char iB)
void encode(char iR, char iG, char iB, char* Gray)
唯一輸出
有兩種寫法
char encode(char iR, char iG, char iB)
void encode(char iR, char iG, char iB, char* oGray)
char R=255, G=255, B=255, Gray;
if (encode(R, G, B) > 128)
;
宣告
編碼?
編什麼碼?
char encode(char iR, char iG, char iB)
void encode(char iR, char iG, char iB, char* oGray)
char R=255, G=255, B=255, Gray;
Gray = encode(R, G, B);
encode(R, G, B, Gray);
if (Gray > 128)
;
宣告
1
2
這樣寫
好一點
char encode(char iR, char iG, char iB)
void encode(char iR, char iG, char iB, char* oGray)
char R=255, G=255, B=255, Gray;
Gray = encode(R, G, B);
encode(R, G, B, Gray);
if (Gray > 128)
;
這個例子
使用return
會讓程式進行命案似的推理
宣告
1
2
char encode(char iR, char iG, char iB)
void encode(char iR, char iG, char iB, char* oGray)
char R=255, G=255, B=255, Gray;
if (rgb2gray(R, B, G) > 128)
;
因為用return和函數名字的問題有關係
(程式設計師尚未確定抽象概念就開始寫程式)
宣告
什麼時候要用return?
檢查整體概念性。
「等號左邊的名字」==「等號右邊」
Ex: y=sin(x);
除此之外,不要使用return
防禦性
程式設計
開車有防衛駕駛
程式有防禦性程式設計
防禦性程式設計
Assert()
error code
Exception
#ifndef…#endif
….
《軟體建構之道2》
assert(0)
→hi light
防禦性程式設計
程式內部
程式介面
人的操作
字元
int
float
防禦性程式設計
程式內部
定義域
值域
運算
解不符合值域
=答案有問題
給參數不符合定義域
=參數有問題
防禦性程式設計
assert()
防禦性程式設計
用在「絕對不可以讓它發生」的條件
//C++ Example of a Assertion Macro
#define Assert( condition, message )
{
if ( !(condition) )
{
LogError ("Assertion failed: ", #condition, message);
exit( EXIT_FAILURE );
}
}
ex:
void SetPixelR(long color)
{
Assert( color < 255 );
Assert( color > 0 );
//…
}
檢查顏色(輸入值)
是否在0~255
防禦性程式設計
int doSomething(int a){
if (a > 5)
return 0;
else
return 1;
}
if(doSomething()){
}
else{
cout << “Error!!” << endl;
}
void doSomething(){
if (a > 5)
throw “Error!!”;
}
try{
doSomething();
}
catch(const char* message){
cout << message << endl;
}
ExceptionunException
防禦性程式設計
條件編譯 #ifndef…#endif
快速改變達行檔編譯之後的樣子。
可自訂debug mode
#ifndef _debug
//…debug code
#endif
防禦性程式設計
條件編譯 #ifndef…#endif
快速改變達行檔編譯之後的樣子。
可自訂debug mode
#if define( DEBUG )
#define DebugCode( code_fragment ) { /*debug code*/ }
#else
#define DebugCode( code_fragment )
#endif
加工過
的用法
防禦性程式設計
const BOOL Bullet::IsEmpty() const
{
return m_isEmptyObj;
}
防禦性程式設計
const BOOL Bullet::IsEmpty() const
{
#ifdef _DEBUG
BOOL Empty(TRUE);
for (int i = 0; i < 10; ++i)
{
if ( data[i] != 0.0 )
Empty = FALSE;
}
ASSERT(Empty == m_isEmptyObj);
#endif
return m_isEmptyObj;
}
防禦性程式設計
const BOOL Bullet::IsEmpty() const
{
#ifdef _DEBUG
BOOL Empty(TRUE);
for (int i = 0; i < 10; ++i)
{
if ( data[i] != 0.0 )
Empty = FALSE;
}
ASSERT(Empty == m_isEmptyObj);
#endif
return m_isEmptyObj;
}
debug mode時存在的code
check所有的data是否為0
用一個bool來記錄
是有曾經有寫入動作
有寫入的動作
m_isEmptyObj = 1
初始化為=0
防禦性程式設計
開發時,讓程式容易出錯
發行後,讓程式隱藏出錯
最極端的做法
自殺式程式設計
自殺式程式設計(原意:攻擊式程式設計)
Use Offensive Programming
1. 應該以這麼一種方式來處理異常情況:
開發階段將它突顯出來
產品代碼時它可以自我修復。←就是Offensive Programming
2. 確定以assert()中止程式
3. 完全填滿記憶體,偵測記憶體配置問題
4. 完全填滿分配的檔案,偵測出檔案格式的錯誤
5. 確定每個「case陳述式的default」或「else子句」的程式
碼能產生錯誤(ex: 中止程式)或無法被忽略。
6. 刪除物件前,填入垃圾值,確定它再也用不到。
7. 設定程式將錯誤記錄檔以mail的方式寄給自己,以了解
release後的軟體中發生的錯誤種類。
投影片結束了
寫程式的路還要繼續…

More Related Content

What's hot

Шаблоны разработки ПО. Часть 3. Шаблоны GoF
Шаблоны разработки ПО. Часть 3. Шаблоны GoFШаблоны разработки ПО. Часть 3. Шаблоны GoF
Шаблоны разработки ПО. Часть 3. Шаблоны GoFSergey Nemchinsky
 
如何培養架構性思考(談軟體架構師必經之路)
如何培養架構性思考(談軟體架構師必經之路)如何培養架構性思考(談軟體架構師必經之路)
如何培養架構性思考(談軟體架構師必經之路)Gelis Wu
 
Testing with JUnit 5 and Spring
Testing with JUnit 5 and SpringTesting with JUnit 5 and Spring
Testing with JUnit 5 and SpringVMware Tanzu
 
やはりお前らのMVCは間違っている
やはりお前らのMVCは間違っているやはりお前らのMVCは間違っている
やはりお前らのMVCは間違っているKoichi Tanaka
 
決済サービスのSpring Bootのバージョンを2系に上げた話
決済サービスのSpring Bootのバージョンを2系に上げた話決済サービスのSpring Bootのバージョンを2系に上げた話
決済サービスのSpring Bootのバージョンを2系に上げた話Ryosuke Uchitate
 
Basics of Model/View Qt programming
Basics of Model/View Qt programmingBasics of Model/View Qt programming
Basics of Model/View Qt programmingICS
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modelingMario Fusco
 
Dependency injection presentation
Dependency injection presentationDependency injection presentation
Dependency injection presentationAhasanul Kalam Akib
 
The aggregate is dead! Long live the aggregate! - SpringIO.pdf
The aggregate is dead! Long live the aggregate! - SpringIO.pdfThe aggregate is dead! Long live the aggregate! - SpringIO.pdf
The aggregate is dead! Long live the aggregate! - SpringIO.pdfSara Pellegrini
 
Jetpack Compose a new way to implement UI on Android
Jetpack Compose a new way to implement UI on AndroidJetpack Compose a new way to implement UI on Android
Jetpack Compose a new way to implement UI on AndroidNelson Glauber Leal
 
Guide to testing Jetpack Compose
Guide to testing Jetpack ComposeGuide to testing Jetpack Compose
Guide to testing Jetpack ComposeDmytroShuba
 
Classes abstratas
Classes abstratasClasses abstratas
Classes abstratasPedro Neto
 
Introduction to SOLID Principles
Introduction to SOLID PrinciplesIntroduction to SOLID Principles
Introduction to SOLID PrinciplesGanesh Samarthyam
 
Spring Boot + React + Gradle in VSCode
Spring Boot + React + Gradle in VSCodeSpring Boot + React + Gradle in VSCode
Spring Boot + React + Gradle in VSCodedpTablo
 
Testing Spring Boot Applications
Testing Spring Boot ApplicationsTesting Spring Boot Applications
Testing Spring Boot ApplicationsVMware Tanzu
 

What's hot (20)

Шаблоны разработки ПО. Часть 3. Шаблоны GoF
Шаблоны разработки ПО. Часть 3. Шаблоны GoFШаблоны разработки ПО. Часть 3. Шаблоны GoF
Шаблоны разработки ПО. Часть 3. Шаблоны GoF
 
如何培養架構性思考(談軟體架構師必經之路)
如何培養架構性思考(談軟體架構師必經之路)如何培養架構性思考(談軟體架構師必經之路)
如何培養架構性思考(談軟體架構師必經之路)
 
Testing with JUnit 5 and Spring
Testing with JUnit 5 and SpringTesting with JUnit 5 and Spring
Testing with JUnit 5 and Spring
 
やはりお前らのMVCは間違っている
やはりお前らのMVCは間違っているやはりお前らのMVCは間違っている
やはりお前らのMVCは間違っている
 
Solid principles
Solid principlesSolid principles
Solid principles
 
Applicative Functor
Applicative FunctorApplicative Functor
Applicative Functor
 
決済サービスのSpring Bootのバージョンを2系に上げた話
決済サービスのSpring Bootのバージョンを2系に上げた話決済サービスのSpring Bootのバージョンを2系に上げた話
決済サービスのSpring Bootのバージョンを2系に上げた話
 
Solid Principles
Solid PrinciplesSolid Principles
Solid Principles
 
Basics of Model/View Qt programming
Basics of Model/View Qt programmingBasics of Model/View Qt programming
Basics of Model/View Qt programming
 
Solid Principle
Solid PrincipleSolid Principle
Solid Principle
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modeling
 
Dependency injection presentation
Dependency injection presentationDependency injection presentation
Dependency injection presentation
 
C# Basics
C# BasicsC# Basics
C# Basics
 
The aggregate is dead! Long live the aggregate! - SpringIO.pdf
The aggregate is dead! Long live the aggregate! - SpringIO.pdfThe aggregate is dead! Long live the aggregate! - SpringIO.pdf
The aggregate is dead! Long live the aggregate! - SpringIO.pdf
 
Jetpack Compose a new way to implement UI on Android
Jetpack Compose a new way to implement UI on AndroidJetpack Compose a new way to implement UI on Android
Jetpack Compose a new way to implement UI on Android
 
Guide to testing Jetpack Compose
Guide to testing Jetpack ComposeGuide to testing Jetpack Compose
Guide to testing Jetpack Compose
 
Classes abstratas
Classes abstratasClasses abstratas
Classes abstratas
 
Introduction to SOLID Principles
Introduction to SOLID PrinciplesIntroduction to SOLID Principles
Introduction to SOLID Principles
 
Spring Boot + React + Gradle in VSCode
Spring Boot + React + Gradle in VSCodeSpring Boot + React + Gradle in VSCode
Spring Boot + React + Gradle in VSCode
 
Testing Spring Boot Applications
Testing Spring Boot ApplicationsTesting Spring Boot Applications
Testing Spring Boot Applications
 

More from Chris Wang

屏東縣政府112年度大專青年公部門暑期工讀計畫簡章.pdf
屏東縣政府112年度大專青年公部門暑期工讀計畫簡章.pdf屏東縣政府112年度大專青年公部門暑期工讀計畫簡章.pdf
屏東縣政府112年度大專青年公部門暑期工讀計畫簡章.pdfChris Wang
 
「設計」在工程師職涯中 扮演的角色
「設計」在工程師職涯中 扮演的角色「設計」在工程師職涯中 扮演的角色
「設計」在工程師職涯中 扮演的角色Chris Wang
 
歡迎加入軟體構築行列
歡迎加入軟體構築行列歡迎加入軟體構築行列
歡迎加入軟體構築行列Chris Wang
 
自我探索的資訊教育
自我探索的資訊教育自我探索的資訊教育
自我探索的資訊教育Chris Wang
 
完美 camp 進化論
完美 camp 進化論完美 camp 進化論
完美 camp 進化論Chris Wang
 
Dm create message old
Dm create message oldDm create message old
Dm create message oldChris Wang
 
Dm create message new
Dm create message newDm create message new
Dm create message newChris Wang
 
用 jenkins 實戰 CD/CI
用 jenkins 實戰 CD/CI用 jenkins 實戰 CD/CI
用 jenkins 實戰 CD/CIChris Wang
 
MVC Design in Web backend Server
MVC Design in Web backend ServerMVC Design in Web backend Server
MVC Design in Web backend ServerChris Wang
 
Bug afx ini-line122
Bug afx ini-line122Bug afx ini-line122
Bug afx ini-line122Chris Wang
 
物件的值莫名奇妙被改掉
物件的值莫名奇妙被改掉物件的值莫名奇妙被改掉
物件的值莫名奇妙被改掉Chris Wang
 
VC6 font setup tips
VC6 font setup tipsVC6 font setup tips
VC6 font setup tipsChris Wang
 
MFC tips for single document
MFC tips for single documentMFC tips for single document
MFC tips for single documentChris Wang
 
CString of MFC skills
CString of MFC skillsCString of MFC skills
CString of MFC skillsChris Wang
 
應用於液晶電視中動態背光調光技術之演算法與系統整合實現 論文口試投影片
應用於液晶電視中動態背光調光技術之演算法與系統整合實現 論文口試投影片應用於液晶電視中動態背光調光技術之演算法與系統整合實現 論文口試投影片
應用於液晶電視中動態背光調光技術之演算法與系統整合實現 論文口試投影片Chris Wang
 
偷偷學習 Python3
偷偷學習 Python3偷偷學習 Python3
偷偷學習 Python3Chris Wang
 
思考 Vuex 發送 API 的架構
思考 Vuex 發送 API 的架構思考 Vuex 發送 API 的架構
思考 Vuex 發送 API 的架構Chris Wang
 
從 Flux 認識 vuex
從 Flux 認識 vuex從 Flux 認識 vuex
從 Flux 認識 vuexChris Wang
 
Information architecture reading ch7
Information architecture reading ch7Information architecture reading ch7
Information architecture reading ch7Chris Wang
 

More from Chris Wang (20)

屏東縣政府112年度大專青年公部門暑期工讀計畫簡章.pdf
屏東縣政府112年度大專青年公部門暑期工讀計畫簡章.pdf屏東縣政府112年度大專青年公部門暑期工讀計畫簡章.pdf
屏東縣政府112年度大專青年公部門暑期工讀計畫簡章.pdf
 
「設計」在工程師職涯中 扮演的角色
「設計」在工程師職涯中 扮演的角色「設計」在工程師職涯中 扮演的角色
「設計」在工程師職涯中 扮演的角色
 
歡迎加入軟體構築行列
歡迎加入軟體構築行列歡迎加入軟體構築行列
歡迎加入軟體構築行列
 
自我探索的資訊教育
自我探索的資訊教育自我探索的資訊教育
自我探索的資訊教育
 
完美 camp 進化論
完美 camp 進化論完美 camp 進化論
完美 camp 進化論
 
Dm create message old
Dm create message oldDm create message old
Dm create message old
 
Dm create message new
Dm create message newDm create message new
Dm create message new
 
用 jenkins 實戰 CD/CI
用 jenkins 實戰 CD/CI用 jenkins 實戰 CD/CI
用 jenkins 實戰 CD/CI
 
MVC Design in Web backend Server
MVC Design in Web backend ServerMVC Design in Web backend Server
MVC Design in Web backend Server
 
Bug afx ini-line122
Bug afx ini-line122Bug afx ini-line122
Bug afx ini-line122
 
物件的值莫名奇妙被改掉
物件的值莫名奇妙被改掉物件的值莫名奇妙被改掉
物件的值莫名奇妙被改掉
 
VC6 font setup tips
VC6 font setup tipsVC6 font setup tips
VC6 font setup tips
 
MFC tips for single document
MFC tips for single documentMFC tips for single document
MFC tips for single document
 
CString of MFC skills
CString of MFC skillsCString of MFC skills
CString of MFC skills
 
應用於液晶電視中動態背光調光技術之演算法與系統整合實現 論文口試投影片
應用於液晶電視中動態背光調光技術之演算法與系統整合實現 論文口試投影片應用於液晶電視中動態背光調光技術之演算法與系統整合實現 論文口試投影片
應用於液晶電視中動態背光調光技術之演算法與系統整合實現 論文口試投影片
 
偷偷學習 Python3
偷偷學習 Python3偷偷學習 Python3
偷偷學習 Python3
 
思考 Vuex 發送 API 的架構
思考 Vuex 發送 API 的架構思考 Vuex 發送 API 的架構
思考 Vuex 發送 API 的架構
 
從 Flux 認識 vuex
從 Flux 認識 vuex從 Flux 認識 vuex
從 Flux 認識 vuex
 
Information architecture reading ch7
Information architecture reading ch7Information architecture reading ch7
Information architecture reading ch7
 
用Vue改dom
用Vue改dom用Vue改dom
用Vue改dom
 

如何寫好程式

Editor's Notes

  1. ... DebugCode( statement 1; statement 2; statement 3; ); ...