論:如何成為有思想、能創(chuàng)新的程序員
寫這篇文章也源于我和新員工的一些談話心得,一些基礎(chǔ)比較薄弱的技術(shù)人員,看起來有點像沒有思想和靈魂的程序員。你可能也會覺得國內(nèi)有很多小企業(yè)出來的人或者剛畢業(yè)的人,會的最多也是CRUD和拖拉控件。我也接觸過一些技術(shù)人員,他們告訴我他們再也不想搞技術(shù)了,因為技術(shù)是在太無聊了,特別年紀稍大一點的,想的最多的就是轉(zhuǎn)行。曾經(jīng)我非常驚訝于這樣的狀況,事實上,寫程序是一件很有創(chuàng)造力的事情,但為何很多人都會覺得無聊呢。
隨著年紀的增長,這些問題的答案慢慢變得清晰一些。在這里,我不敢說,我說的都是正確的,我只是在一直不停的探索。在探索之后,我對我的新員工說了以下的話:“進入我們公司,雖然我們也是很不起眼的剛創(chuàng)業(yè)的小公司,但是,你在這里需要做一些改變了。我知道你們以前的工作性質(zhì)可能是上司給你交代任務(wù),告訴你怎么做,然后你管也不管就照章辦事,拉拉控件,以完成項目功能為首要任務(wù)。在我們這里,你需要成為一個有思想的程序員。有思想的程序員需要懂得如何使用聰明的腦袋瓜。事實上,很多人都不知道我們的腦袋瓜到底能做多少事情,不過,一旦你嘗試了,你就會體會到‘不是做不到,而是想不到’。需要記住這些話,從思想上改變,從今天開始。首先,我們是做軟件產(chǎn)品的公司,質(zhì)量是產(chǎn)品生存的首要標(biāo)準,產(chǎn)品質(zhì)量的最低要求就是易用性;其次,我們要保證產(chǎn)品的質(zhì)量,代碼的質(zhì)量首先要過關(guān),標(biāo)準編碼方式、異常處理方式、代碼的生命周期管理、編碼的完整性都需要兼顧;第三,避免寫一些垃圾代碼和重復(fù)的代碼,這需要動用你聰明的腦袋,我曾經(jīng)寫了10幾個的CRUD產(chǎn)品,從而自主創(chuàng)新了控件關(guān)系映射、對象-對象映射、通用窗體框架,乃至我們現(xiàn)在的OSGi.NET產(chǎn)品和云計算SaaS商店平臺,都是從這些重復(fù)的勞作中不斷思索發(fā)明的。我看到設(shè)計模式的書時,可以驕傲的向同學(xué)們吹牛,我也設(shè)計過幾個‘模式’;第四,學(xué)會發(fā)現(xiàn)問題,探索問題,積極詢問,避免把問題遺留下來或者拖機取巧。浪費一個發(fā)現(xiàn)問題和解決問題的機會,相當(dāng)于浪費提高自己的機會。最后,你要有信心成為一流有思想和靈魂的技術(shù)人員,別哪一天你離開尤埃時,丟我們的臉,:)。”
我不敢說,我現(xiàn)在多有思想,但是,我隱隱約約感覺到一些這樣的有意思的東西。我崇拜“道法自然”,它告訴我違反規(guī)律就會受到懲罰,因此,我會時刻反省我是否有做錯的事情,包括在平時編碼、設(shè)計和架構(gòu)的時候,以及平時生活上的為人處事。接下來,我介紹一下,我如何來發(fā)明我曾經(jīng)的產(chǎn)品,希望能夠給人一些啟發(fā)。
1 我是如何發(fā)明了控件關(guān)系映射組件
控件關(guān)系映射的發(fā)明源自于我在參與一款MIS系統(tǒng)的設(shè)計,該系統(tǒng)是一個鋼管管理系統(tǒng),每一個鋼管的信息有很多很多的屬性,我記得鋼管廠給我們的數(shù)據(jù)說明書里面,一個管子的信息有驚人的380多列。因此,我們在查詢、修改、添加記錄的時候,總是會有類似以下成片成片的代碼。
- var add***Sql = "insert into Test(a1,a2,....aN) values(@a1,@a2,....@aN)";
- ......
- var para1 = new SqlParameter("@a1", SqlDbType.String, a1.Text.Trim();
- var para2 = new SqlParameter("@a2", SqlDbType.String, a1.Text.Trim();
- ......
- var paraN = new SqlParameter("@aN", SqlDbType.String, a1.Text.Trim();
(忽略中間的N-3行代碼,以及查詢、修改和刪除的代碼)
我記得,我們一起做的另一個小伙拿了一個CRUD一千多個字段的表來向我們顯耀說:“我他媽的把這功能實現(xiàn)了!”。我不知道大家是否反感這樣的代碼,反正我是厭倦了。當(dāng)我想到這是一件很痛苦的事情的時候,我考慮了如何來解決它。經(jīng)過一些思考,我驚訝的發(fā)現(xiàn),所有的CRUD以及界面的流程都可以抽象為“輸入-處理-輸出-輸入-處理-輸出......”的過程,處理的過程實際上是獲取輸入,然后組裝成SQL語句,最后在響應(yīng)到界面。這個過程是以SQL語句為中心,SQL語句的參數(shù)來源于界面的控件或者界面類的其它成員,SQL語句執(zhí)行的結(jié)果可能是跑到另一個頁面、執(zhí)行DataGrid綁定、執(zhí)行下拉列表綁定、給控件賦值。因此,我想到一個方法,可以設(shè)計一個SQL映射的配置,即利用這個配置,直接將界面控件映射到數(shù)據(jù)庫,并且也可以執(zhí)行反向映射。以下是映射SQL的配置:
- <?xml version="1.0" encoding="utf-8"?>
- <CrmMappings Class="HumanDispSolution.login" >
- <MappingSQL GenType="None" Name="Login" Value="select UID,Name,Sys_User.RID,Role from Sys_User,Sys_Role where Sys_User.RID=Sys_Role.RID AND UID=@UID AND Password = @PWD" SqlOpType="SELECT" CmdType="Text" >
- <SqlParams >
- <SqlParam Name="@UID" ControlID="UID" ParamType="String" IsFile="False" >
- </SqlParam>
- <SqlParam Name="@PWD" ControlID="PWD" ParamType="String" IsFile="False" >
- </SqlParam>
- </SqlParams>
- <SqlResults >
- <SqlResult Field="Name" MemberID="UserName" IsStatic="True" AssemblyName="HumanDispSolution" StaticTypeName="HumanDispSolution.UserConfig" >
- </SqlResult>
- <SqlResult Field="UID" MemberID="UserID" IsStatic="True" AssemblyName="HumanDispSolution" StaticTypeName="HumanDispSolution.UserConfig" >
- </SqlResult>
- <SqlResult Field="RID" MemberID="RID" IsStatic="True" AssemblyName="HumanDispSolution" StaticTypeName="HumanDispSolution.UserConfig" >
- </SqlResult>
- <SqlResult Field="Role" MemberID="Role" IsStatic="True" AssemblyName="HumanDispSolution" StaticTypeName="HumanDispSolution.UserConfig" >
- </SqlResult>
- <SqlResult InvokeMethod="Log" IsStatic="True" AssemblyName="HumanDispSolution" StaticTypeName="HumanDispSolution.Logger" >
- <InvokeParam Value="登入系統(tǒng)" >
- </InvokeParam>
- </SqlResult>
- </SqlResults>
- </MappingSQL>
- </CrmMappings>
以下是調(diào)用映射SQL語句實現(xiàn)CRUD中的一個操作。
- namespace HumanDispSolution
- {
- public class login : CrmPage
- {
- private void btnLogin_Click(object sender, System.EventArgs e)
- {
- DataSet ds = this.ExecuteMapping("Login") as DataSet;
- if(ds.Tables[0].Rows.Count > 0) //登入
- {System.Web.Security.FormsAuthentication.RedirectFromLoginPage(UID.Text,false);
- }
- else
- this.lAlert.Text = "<script language='javascript'>alert('登錄失敗,請重新輸入帳戶信息!');</script>";
- }
- }
- }
另外,我還編寫了一個工具來自動生成這樣的配置文件,從此以后,關(guān)于數(shù)據(jù)庫的CRUD,我爽了!!
2 我是如何發(fā)明了通用窗體框架
控件關(guān)系映射的發(fā)明也是源于上面提到的鋼管系統(tǒng)。當(dāng)超過2個人一起參與一個復(fù)雜項目時,可能他們都需要操作主界面,在主界面加上各自模塊需要的菜單、需要的界面元素,此外兩個人設(shè)計的東西也完全不一致。這就造成一些問題了,因為如何實現(xiàn)兩個人的集成就有一些麻煩,而且經(jīng)常出現(xiàn)意外。于是我就發(fā)明了一個通用窗體框架,這個框架提供了以下功能:
(1)集成用戶權(quán)限;
(2)集成數(shù)據(jù)訪問;
(3)插件式支持,每一個人都可以并行開發(fā),集成時僅需要將配置文件集成一起就形成一個組裝起來的軟件了。
每一個開發(fā)人員只需要編寫類似以下的配置文件就可以集成了:
- <?xml version="1.0" encoding="utf-8" ?>
- <MainForm>
- <Menus Name="菜單">
- <Menu Name="系統(tǒng)(S)" LeftIndex="3" TopIndex="1" Command="" Class="">
- <Menu Name="登錄管理" LeftIndex="1" TopIndex="1" Command="" Class=""/>
- <Menu Name="歡迎" LeftIndex="2" TopIndex="2" Command="" Class="CZB.Framework.WelcomeForm"/>
- <Menu Name="退出" LeftIndex="3" TopIndex="3" Command="Close" Class=""/>
- </Menu>
- <Menu Name="數(shù)據(jù)導(dǎo)出(B)" LeftIndex="2" TopIndex="3" Command="" Class="">
- <Menu Name="導(dǎo)出Excel" LeftIndex="2" TopIndex="2" Command="" Class="SalaryManagement.UI.frmExport"/>
- </Menu>
- </Menus>
- <ToolButtons Name="工具欄">
- <ToolButton Name="工具欄名稱" Index="1" ImageIndex="1" Visible="true" Roles="" Command="HideOrShow" Class="工具欄名稱" />
- <ToolButton Name="工具欄名稱1" Index="2" ImageIndex="2" Visible="false" Roles="" Command="" Class="工具欄名稱1" />
- </ToolButtons>
- </MainForm>
3 我是如何設(shè)計了對象-對象關(guān)系映射
ORM對于一些小型應(yīng)用感覺有點龐大,但是對于大型應(yīng)用,我想是一個比較總要的組件了。在我們使用ORM組件時,也經(jīng)常會寫以下代碼。
- var user = new User();
- user.Name = NameTextBox.Text.Trim();
- user.Password = PasswordTextBox.Text.Trim();
- ......
- OrmFactory.Save(user);
- ----------------------------------------------
- var user = OrmFactory.QueryScalar(...);
- NameTextBox.Text = user.Name;
- ......
如果一個MIS系統(tǒng)充斥了大量這樣的代碼,估計你也會膩味,從而喪失對編程的興趣了。記得我剛才說什么來了,“有問題,意味著升華”,“做一個有思想的程序員”。因此,接下來的問題就是,我們?nèi)绾蝸斫鉀Q類似這樣重復(fù)的勞動。我在2006年時想到的辦法就是實現(xiàn)一個對象-對象的映射。首先,設(shè)計如下實體類:
- public class UserEntity
- {
- ……
- [Member]
- public int Age;
- [Control]
- public string Name
- {
- get { return this._Name; }
- set { this._Name = value; }
- }
- [Control("CardNo.Text")]
- public string CardNo
- {
- get { return this._CardNo; }
- set { this._CardNo = value; }
- }
- ……
- }
- public class EmployeeEntity
- {
- ……
- [Reference(typeof(UserEntity))]
- public UserEntity User
- {
- get { return this._User; }
- set { this._User = value; }
- }
- [Control]
- public float PostSalary
- {
- get { return this._PostSalary; }
- set { this._PostSalary = value; }
- }
- ……
- }
其次,調(diào)用ObjectEngine實現(xiàn)OO映射。
A 實現(xiàn)表單類與實體類映射
- private void Map_Click(object sender, System.EventArgs e)
- {
- this.o = CZB.ObjectMapper.ObjectEngine.Map(this,typeof(EmployeeEntity)) as EmployeeEntity;
- }
B 實現(xiàn)實體類與表單類的映射
- private void InverseMap_Click(object sender, System.EventArgs e)
- {
- this.o.User.Name = "c.z.b in";
- this.o.User.Age = 19;
- this.o.CompoInsurance = 0;
- CZB.ObjectMapper.ObjectEngine.InverseMap(this,o);
- }
4 我是如何設(shè)計OSGi.NET和SaaS商店產(chǎn)品
至于OSGi.NET和SaaS商店是我在不斷思索通用窗體框架以及對現(xiàn)有科技的趨勢的把握下,由幾個很有創(chuàng)造力的編程人員,在建立了完善的產(chǎn)品保障體系下,構(gòu)建起來的。這兩個產(chǎn)品我會在后面介紹如何設(shè)計的。他們的設(shè)計我用了很長的時間。
我不是什么老鳥,希望我們在如此多的技術(shù)的世界中能夠多多交流,共同進步。解決這些問題,不僅增加了編程的樂趣,更是增加了自己的見識,從而避免自己成為一個沒有思想的程序員!我也知道,我們可以找到很多理由來反駁文中提到的做法和觀點,但是,提高自己才是最重要的,不要去著急的否定一些什么,并給自己找借口。
原文鏈接:http://www.cnblogs.com/baihmpgy/archive/2010/12/14/1905144.html
【編輯推薦】