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

オブジェクト指向の設計と実装の学び方のコツ
オブジェクト指向の設計と実装の学び方のコツオブジェクト指向の設計と実装の学び方のコツ
オブジェクト指向の設計と実装の学び方のコツ増田 亨
 
プログラムの処方箋~健康なコードと病んだコード
プログラムの処方箋~健康なコードと病んだコードプログラムの処方箋~健康なコードと病んだコード
プログラムの処方箋~健康なコードと病んだコードShigenori Sagawa
 
最新C++事情 C++14-C++20 (2018年10月)
最新C++事情 C++14-C++20 (2018年10月)最新C++事情 C++14-C++20 (2018年10月)
最新C++事情 C++14-C++20 (2018年10月)Akihiko Matuura
 
Gitはじめの一歩
Gitはじめの一歩Gitはじめの一歩
Gitはじめの一歩Ayana Yokota
 
関数プログラミング入門
関数プログラミング入門関数プログラミング入門
関数プログラミング入門Hideyuki Tanaka
 
Hello, DirectCompute
Hello, DirectComputeHello, DirectCompute
Hello, DirectComputedasyprocta
 
不遇の標準ライブラリ - valarray
不遇の標準ライブラリ - valarray不遇の標準ライブラリ - valarray
不遇の標準ライブラリ - valarrayRyosuke839
 
ARM CPUにおけるSIMDを用いた高速計算入門
ARM CPUにおけるSIMDを用いた高速計算入門ARM CPUにおけるSIMDを用いた高速計算入門
ARM CPUにおけるSIMDを用いた高速計算入門Fixstars Corporation
 
代数方程式とガロア理論
代数方程式とガロア理論代数方程式とガロア理論
代数方程式とガロア理論Junpei Tsuji
 
Pythonによる黒魔術入門
Pythonによる黒魔術入門Pythonによる黒魔術入門
Pythonによる黒魔術入門大樹 小倉
 
はじめてのGit forデザイナー&コーダー
はじめてのGit forデザイナー&コーダーはじめてのGit forデザイナー&コーダー
はじめてのGit forデザイナー&コーダーSaeko Yamamoto
 
Domain Driven Design with the F# type System -- F#unctional Londoners 2014
Domain Driven Design with the F# type System -- F#unctional Londoners 2014Domain Driven Design with the F# type System -- F#unctional Londoners 2014
Domain Driven Design with the F# type System -- F#unctional Londoners 2014Scott Wlaschin
 
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考えるGoのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考えるpospome
 
オブジェクト指向エクササイズのススメ
オブジェクト指向エクササイズのススメオブジェクト指向エクササイズのススメ
オブジェクト指向エクササイズのススメYoji Kanno
 
Intro to SVE 富岳のA64FXを触ってみた
Intro to SVE 富岳のA64FXを触ってみたIntro to SVE 富岳のA64FXを触ってみた
Intro to SVE 富岳のA64FXを触ってみたMITSUNARI Shigeo
 
Deep dream 機械が見た夢
Deep dream   機械が見た夢Deep dream   機械が見た夢
Deep dream 機械が見た夢Harumitsu Nobuta
 

What's hot (20)

いつやるの?Git入門
いつやるの?Git入門いつやるの?Git入門
いつやるの?Git入門
 
オブジェクト指向の設計と実装の学び方のコツ
オブジェクト指向の設計と実装の学び方のコツオブジェクト指向の設計と実装の学び方のコツ
オブジェクト指向の設計と実装の学び方のコツ
 
プログラムの処方箋~健康なコードと病んだコード
プログラムの処方箋~健康なコードと病んだコードプログラムの処方箋~健康なコードと病んだコード
プログラムの処方箋~健康なコードと病んだコード
 
最新C++事情 C++14-C++20 (2018年10月)
最新C++事情 C++14-C++20 (2018年10月)最新C++事情 C++14-C++20 (2018年10月)
最新C++事情 C++14-C++20 (2018年10月)
 
Gitはじめの一歩
Gitはじめの一歩Gitはじめの一歩
Gitはじめの一歩
 
関数プログラミング入門
関数プログラミング入門関数プログラミング入門
関数プログラミング入門
 
Hello, DirectCompute
Hello, DirectComputeHello, DirectCompute
Hello, DirectCompute
 
不遇の標準ライブラリ - valarray
不遇の標準ライブラリ - valarray不遇の標準ライブラリ - valarray
不遇の標準ライブラリ - valarray
 
SOLID Principles
SOLID PrinciplesSOLID Principles
SOLID Principles
 
ARM CPUにおけるSIMDを用いた高速計算入門
ARM CPUにおけるSIMDを用いた高速計算入門ARM CPUにおけるSIMDを用いた高速計算入門
ARM CPUにおけるSIMDを用いた高速計算入門
 
代数方程式とガロア理論
代数方程式とガロア理論代数方程式とガロア理論
代数方程式とガロア理論
 
Pythonによる黒魔術入門
Pythonによる黒魔術入門Pythonによる黒魔術入門
Pythonによる黒魔術入門
 
はじめてのGit forデザイナー&コーダー
はじめてのGit forデザイナー&コーダーはじめてのGit forデザイナー&コーダー
はじめてのGit forデザイナー&コーダー
 
Domain Driven Design with the F# type System -- F#unctional Londoners 2014
Domain Driven Design with the F# type System -- F#unctional Londoners 2014Domain Driven Design with the F# type System -- F#unctional Londoners 2014
Domain Driven Design with the F# type System -- F#unctional Londoners 2014
 
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考えるGoのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
 
オブジェクト指向エクササイズのススメ
オブジェクト指向エクササイズのススメオブジェクト指向エクササイズのススメ
オブジェクト指向エクササイズのススメ
 
Intro to SVE 富岳のA64FXを触ってみた
Intro to SVE 富岳のA64FXを触ってみたIntro to SVE 富岳のA64FXを触ってみた
Intro to SVE 富岳のA64FXを触ってみた
 
Deep dream 機械が見た夢
Deep dream   機械が見た夢Deep dream   機械が見た夢
Deep dream 機械が見た夢
 
TaPL9
TaPL9TaPL9
TaPL9
 
Vim Rocks!
Vim Rocks!Vim Rocks!
Vim Rocks!
 

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; ); ...