先了解問題所在,再思考解決方法

撰寫程式時最好先了解問題所在,再思考解決方法, 例如在編輯記錄時,究竟是用formview or gridview比較好呢? 它並沒有絕對的答案, 除非您了解問題所在

上一篇我提到了 Coding 時, 要有想法, 事後才能將您的想法去比對您的 code, 如果彼此是相符的, 那麼我們可以說您已實現了您的設計概念 (在此暫時稱為 '想法' )至於您的 '想法' 是否被客戶欣賞或達到您的目的,則是另一回事了, 我們不討論它

如果您會講台語, 不妨用台語唸 '設計' 這二個字, 當用台語說這二個字時, 有一點 "想害別人" 的味道, 是的, 當您有想法後, 如何達成目的, 就是靠 '設計'。

  • 如果您想抓老鼠, 可以買捕鼠夾, 捕鼠板
  • 如果您想抓蟑螂, 捕鼠板或許可以順便抓到一些, 但捕鼠夾肯定沒有用
  • 如果您在山裡想抓山鷄, 可能要自己做一個陷阱, 並將它隱藏起來
  • 如果您想抓老虎, 可能要挖一個極深的洞, 並在上面舖草(沒有什麼老虎看到洞還會往裡面跳的,對吧)

從上面的例子可以了解您若在地方隨便挖個洞, 並無法因此抓到所有的動物, 必需先了解您想抓的對象是誰, 才能決定做什麼陷阱來抓它; coding 時也類似, 當您想達成客戶的要求前, 最好先對您遇到的問題, 做一點深入的了解, 再來思考對策, 會比較容易達到目標。例如客戶想快速地輸入多筆data, 您的 UI 可以設計成 grid ,讓使用者一次insert/edit 多筆, 但如果客戶沒這需要,或者一筆的欄位多達一百個, 用grid 一次編輯多筆可能會讓客戶因此不想使用這系統。

回到正題, 您在 coding 時, 您遇到了什麼問題呢? 我們先從一個小例子看起, 假設您要寫一支最基本的線上活動報名程式, 網頁已由美工做好了, 您目前要做的是寫點程式, 讓使用者 submit 後能將資訊存到 database 裡。 那麼, 您覺得您遇到了什麼問題?

如果您連語法都不熟, 連database 都不會建置, 請先不要看這篇了, 先回頭學語法再說, 因為這篇文章並不打算提供最詳盡的程式碼, 只是討論一些想法

如果您覺得沒什麼問題啊! 那麼, 您要做的事, 或許就只是寫點 code, 連結到 database, 利用 ADO.NET , SQL Statement 等方式將記錄建檔就交差了

 

接下來我來談談我覺得我遇到了什麼問題供您參考, 希望能幫助您了解我想傳達的意思

由於我日後可能還是會遇到這類的案子, 因此我希望下次又有線上活動報名程式時, 能寫得更快一些, 而我要解決的問題有:

  1. 每次案子的 database 類型不同, 我希望能支援 SQL 2000, SQL 2005, 以及 Access
  2. 每次案子的 資料表名稱都不同, 欄位名稱及數量也會不同
  3. 每次都需要做些欄位檢查,例如姓名,email 必填
  4. 希望能有防灌水的機制, 防止使用者重覆送出, 不過如何防 ,要不要防, 仍要看客戶的意思( 有的客戶希望多多益善, 灌水沒關係, 總票數高一點比較好看 )

接下來就是針對我遇到的問題, 設計一下解決方法:

1.
由於需要支援多種 database, 因此我決定將它寫在 web.config 中, 例如寫成

 <appSettings>
  <add key="dbType" value="SQL2000" />
  <add key="connectionString" value="...這裡寫連線字串...." />
 </appSettings>

接下來為了讓程式能存取同一類型的物件, 我設計了幾個 class

abstract class DB : 裡面有ExecuteNonQuery(string sql) method
class AccessDB : 繼承 DB class
class SqlDB : 繼承 DB class

最後再寫一支 class DBFactory : 裡面有 public static DB GetDB(string dbType, string connectionString) 這個 method

將上述的 class 包成一支 dll file , 以後各案子都將它加入參考, 就大功告成了, 日後我要寫程式存取 db 時, 就可以寫成
string dbType = ....
string connString = ...
DB db = DBFactory.GetDB(dbType, connString);
db.ExecuteNonQuery("insert ....");
就可以 insert record了, 上述四行,差不多是每個案子都要寫的, 您可以看到不論 db 是什麼類型, 只需要在 web.config 裡修改設定值即可
到此為止, 算是達到了第一個目標

2.
由於資料表名稱, 欄位名稱, 欄位數量每次都不同, 如果我每次都花時間寫

string name = this.txtName.Text;
string sex = this.ddlSex.SelectedValue;
..... //隨著欄位愈多, 這裡就要寫愈多行
string tableName = "報名表";
string SQL = "insert into " + tableName + "(姓名, 性別,.....)VALUES('" + name.Replace("'", "''") + "','" + sex.Replace("'", "''") + "',....)";
string dbType = ....
string connString = ...
DB db = DBFactory.GetDB(dbType, connString);
db.ExecuteNonQuery(SQL);

那麼雖然灰色的那四行 code 算短, 但藍色的 code 卻仍嫌太長, 於是我寫了一些 class, 專門用來解析表單裡有哪些控制項, 並且能取得它們的值, 為了能順便反應欄位型態, 我也做了一組的控制項, 最後我的表單裡, 控制項都不再使用內建控制項了, 大約會變成
<allen:TextBox id="txtName" runat="server"  fieldName="姓名" fieldType="NVarchar" />
其中 fieldName 用來指定欄位名稱, fieldType用來指定欄位型別, 花了一些時間將這些程式寫好之後, 我的程式碼, 變成了
string tableName =... //(將資料表名稱移到 web.config 裡)
List<allenControl> data = FormHelper.ParseForm(); //只需要這一行,就可以解析表單裡有哪些欄位, 並將它們一一存放在 List<allenControl>裡
string SQL = DBHelper.GenerateInsertSQLStatement(tableName, data); //根據tableName及蒐集到的欄位值, 欄位名稱, 欄位型態, 組合出一段 insert into.... 的 SQL 語法
string dbType = ....
string connString = ...
DB db = DBFactory.GetDB(dbType, connString);
db.ExecuteNonQuery(SQL);

您可以看到, 以後再有線上報名的案子, 我只需要在 web.config 裡指定dbType, connectionString, 以及 tableName, 並將美工做好的 HTML file 貼入 VS.NET 裡, 拉入我自訂的控制項, 設好 fieldName, fieldType 就差不多了, 需要寫的code, 只剩 7 行, 更好的是, 這 7 行完全不必做修改, 或許您會說 "拉入控制項, 設好 fieldName, fieldType 也要時間啊", 但這對我比較不是問題, 畢竟找個會拉入控制項的人, 比找個會寫出沒 bug 的工程師實在容易多了, 對吧, 到此為止, 算是達到了第二個目標

3.
欄位驗證, 目前我的決定是用 ASP.NET 提供的驗證控制項

4.
呼~~ 寫到這裡, 挺累的了, 防灌水的部份, 難度又高了些, 我的想法, 是平時就先寫好一些防灌水的機制, 包成一個個獨立的 dll file, 各案子需要什麼防灌水邏輯, 就將它們 '掛' 到 web.config 裡,而程式就變成
string tableName =...
List<allenControl> data = FormHelper.ParseForm();
if( ! MessageHelper.AllowInsert( data ) ) return; //若觸犯了某條防灌水的規則, 就不 insert, 但也不明白地告訴使用者, 免得使用者不高興, 或根據訊息來多方嘗試
string SQL = DBHelper.GenerateInsertSQLStatement(tableName, data);
string dbType = ....
string connString = ...
DB db = DBFactory.GetDB(dbType, connString);
db.ExecuteNonQuery(SQL); 

最後的成品, 就是 8 行, 而且日後每個線上報名的案子, 都大約不必改, 那麼除了美工需要花時間之外, 最花時間的, 似乎只剩拉控制項了, 因為程式碼實在不長, 對吧; 如果您還行有餘力, 不妨寫一支程式碼產生器, 指定 connectionstring, tablename 後, 由程式去讀資料表裡有哪些欄位及其欄位型態, 欄位長度, 然後由程式自動生成那坨自訂控制項的 code(以及驗證控制項), 如此一來,連剛才您可能正在暗地吐槽我的拉入自訂控制項, 設定 fieldName , fieldType, MaxLength 的事都省下來了, 只需要將那些生成的 code 一段段貼到正確的位置即可; 這支程式碼產生器我也有寫哦 !! 但不知何時, 被我從硬碟裡刪除了  , 豬頭吧!! 年紀大了, 什麼怪事都做得出來... 呃~~ 我是指 coding 方面啦, 不包括溜鳥那類的...

 

您遇到的問題或許與我不同, 或許針對相同的問題您的設計手法也會與我不同, 我希望傳達的重點是在 coding 時, 應該想想如何真正地解決問題 , 如果您覺得我的解決方法太爛了, 您覺得回歸到最原始的寫法才是最簡單大方, 全公司同事都很容易維護, 我也尊重您的想法, 只要您有想法, 又有適當的手法來達到目標, 都是 OK 的

 

寫一些心得或看法,是基於分享, 當然不是想打筆仗, 但由於我原本的文章標題及內容用了Concept 這個字眼, 它似乎造成了認知上的不同, 因此我於 2008/2/27 將原始文章改寫, 希望上述的文字表達方式能減少一些主觀認知而造成的不必要誤會, 我將原始文章的內容放在下方, 供各位查看2008/2/27之前幾天的回應時不致於一頭霧水,

======================

2008/2/27以前的文章原始內容

=====================

標題:Concept – 設計概念(二)

上一篇我提到了 Coding 時, 要有想法, 事後才能將您的想法去比對您的 code, 如果彼此是相符的, 那麼我們可以說您已實現了您的設計概念 (至於您的 Concept 是否被客戶欣賞或達到您的目的,則是另一回事了, 我們不討論它)

如果您會講台語, 不妨用台語唸 '設計' 這二個字, 當用台語說這二個字時, 有一點 "想害別人" 的味道, 是的, 當您有想法後, 如何達成目的, 就是靠 '設計'。

  • 如果您想抓老鼠, 可以買捕鼠夾, 捕鼠板
  • 如果您想抓蟑螂, 捕鼠板或許可以順便抓到一些, 但捕鼠夾肯定沒有用
  • 如果您在山裡想抓山鷄, 可能要自己做一個陷阱, 並將它隱藏起來
  • 如果您想抓老虎, 可能要挖一個極深的洞, 並在上面舖草(沒有什麼老虎看到洞還會往裡面跳的,對吧)

從上面的例子可以了解當您想達到目的前, 最好先對您遇到的問題, 做一點深入的了解, 再來思考對策, 會比較容易達到目標。

回到正題, 您在 coding 時, 您遇到了什麼問題呢? 我們先從一個小例子看起, 假設您要寫一支最基本的線上活動報名程式, 網頁已由美工做好了, 您目前要做的是寫點程式, 讓使用者 submit 後能將資訊存到 database 裡。 那麼, 您覺得您遇到了什麼問題?

  • 如果您連語法都不熟, 連database 都不會建置, 請先不要看這篇了, 先回頭學語法再說
  • 如果您覺得沒什麼問題啊! 那麼, 您要做的事, 或許就只是寫點 code, 連結到 database, 利用 ADO.NET , SQL Statement 等方式將記錄建檔就交差了

接下來我來談談我覺得我遇到了什麼問題供您參考

由於我日後可能還是會遇到這類的案子, 因此我希望下次又有線上活動報名程式時, 能寫得更快一些, 而我要解決的問題有:

  1. 每次案子的 database 類型不同, 我希望能支援 SQL 2000, SQL 2005, 以及 Access
  2. 每次案子的 資料表名稱都不同, 欄位名稱及數量也會不同
  3. 每次都需要做些欄位檢查,例如姓名,email 必填
  4. 希望能有防灌水的機制, 防止使用者重覆送出, 不過如何防 ,要不要防, 仍要看客戶的意思( 有的客戶希望多多益善, 灌水沒關係, 總票數高一點比較好看 )

接下來就是針對我遇到的問題, 設計一下解決方法:

1.
由於需要支援多種 database, 因此我決定將它寫在 web.config 中, 例如寫成

 <appSettings>
  <add key="dbType" value="SQL2000" />
  <add key="connectionString" value="...這裡寫連線字串...." />
 </appSettings>

接下來為了讓程式能存取同一類型的物件, 我設計了幾個 class

abstract class DB : 裡面有ExecuteNonQuery(string sql) method
class AccessDB : 繼承 DB class
class SqlDB : 繼承 DB class

最後再寫一支 class DBFactory : 裡面有 public static DB GetDB(string dbType, string connectionString) 這個 method

將上述的 class 包成一支 dll file , 以後各案子都將它加入參考, 就大功告成了, 日後我要寫程式存取 db 時, 就可以寫成
string dbType = ....
string connString = ...
DB db = DBFactory.GetDB(dbType, connString);
db.ExecuteNonQuery("insert ....");
就可以 insert record了, 上述四行,差不多是每個案子都要寫的, 您可以看到不論 db 是什麼類型, 只需要在 web.config 裡修改設定值即可
到此為止, 算是達到了第一個目標

2.
由於資料表名稱, 欄位名稱, 欄位數量每次都不同, 如果我每次都花時間寫

string name = this.txtName.Text;
string sex = this.ddlSex.SelectedValue;
..... //隨著欄位愈多, 這裡就要寫愈多行
string tableName = "報名表";
string SQL = "insert into " + tableName + "(姓名, 性別,.....)VALUES('" + name.Replace("'", "''") + "','" + sex.Replace("'", "''") + "',....)";
string dbType = ....
string connString = ...
DB db = DBFactory.GetDB(dbType, connString);
db.ExecuteNonQuery(SQL);

那麼雖然灰色的那四行 code 算短, 但藍色的 code 卻仍嫌太長, 於是我寫了一些 class, 專門用來解析表單裡有哪些控制項, 並且能取得它們的值, 為了能順便反應欄位型態, 我也做了一組的控制項, 最後我的表單裡, 控制項都不再使用內建控制項了, 大約會變成
<allen:TextBox id="txtName" runat="server"  fieldName="姓名" fieldType="NVarchar" />
其中 fieldName 用來指定欄位名稱, fieldType用來指定欄位型別, 花了一些時間將這些程式寫好之後, 我的程式碼, 變成了
string tableName =... //(將資料表名稱移到 web.config 裡)
List<allenControl> data = FormHelper.ParseForm(); //只需要這一行,就可以解析表單裡有哪些欄位, 並將它們一一存放在 List<allenControl>裡
string SQL = DBHelper.GenerateInsertSQLStatement(tableName, data); //根據tableName及蒐集到的欄位值, 欄位名稱, 欄位型態, 組合出一段 insert into.... 的 SQL 語法
string dbType = ....
string connString = ...
DB db = DBFactory.GetDB(dbType, connString);
db.ExecuteNonQuery(SQL);

您可以看到, 以後再有線上報名的案子, 我只需要在 web.config 裡指定dbType, connectionString, 以及 tableName, 並將美工做好的 HTML file 貼入 VS.NET 裡, 拉入我自訂的控制項, 設好 fieldName, fieldType 就差不多了, 需要寫的code, 只剩 7 行, 更好的是, 這 7 行完全不必做修改, 或許您會說 "拉入控制項, 設好 fieldName, fieldType 也要時間啊", 但這對我比較不是問題, 畢竟找個會拉入控制項的人, 比找個會寫出沒 bug 的工程師實在容易多了, 對吧, 到此為止, 算是達到了第二個目標

3.
欄位驗證, 目前我的決定是用 ASP.NET 提供的驗證控制項

4.
呼~~ 寫到這裡, 挺累的了, 防灌水的部份, 難度又高了些, 我的想法, 是平時就先寫好一些防灌水的機制, 包成一個個獨立的 dll file, 各案子需要什麼防灌水邏輯, 就將它們 '掛' 到 web.config 裡,而程式就變成
string tableName =...
List<allenControl> data = FormHelper.ParseForm();
if( ! MessageHelper.AllowInsert( data ) ) return; //若觸犯了某條防灌水的規則, 就不 insert, 但也不明白地告訴使用者, 免得使用者不高興, 或根據訊息來多方嘗試
string SQL = DBHelper.GenerateInsertSQLStatement(tableName, data);
string dbType = ....
string connString = ...
DB db = DBFactory.GetDB(dbType, connString);
db.ExecuteNonQuery(SQL); 

最後的成品, 就是 8 行, 而且日後每個線上報名的案子, 都大約不必改, 那麼除了美工需要花時間之外, 最花時間的, 似乎只剩拉控制項了, 因為程式碼實在不長, 對吧; 如果您還行有餘力, 不妨寫一支程式碼產生器, 指定 connectionstring, tablename 後, 由程式去讀資料表裡有哪些欄位及其欄位型態, 欄位長度, 然後由程式自動生成那坨自訂控制項的 code(以及驗證控制項), 如此一來,連剛才您可能正在暗地吐槽我的拉入自訂控制項, 設定 fieldName , fieldType, MaxLength 的事都省下來了, 只需要將那些生成的 code 一段段貼到正確的位置即可; 這支程式碼產生器我也有寫哦 !! 但不知何時, 被我從硬碟裡刪除了  , 豬頭吧!! 年紀大了, 什麼怪事都做得出來... 呃~~ 我是指 coding 方面啦, 不包括溜鳥那類的...

 

您遇到的問題或許與我不同, 或許針對相同的問題您的設計手法也會與我不同, 我希望傳達的重點是在 coding 時, 應該想想如何真正地解決問題 , 如果您覺得我的解決方法太爛了, 您覺得回歸到最原始的寫法才是最簡單大方, 全公司同事都很容易維護, 我也尊重您的想法, 只要您有想法, 又有適當的手法來達到目標, 都是 OK 的