[原创文章Q{载请保留或注明出处:http://www.regexlab.com/zh/encoding.htm]
U别Q中U?/p>
摘要Q本文介l了字符与编码的发展q程Q相x늚正确理解。D例说明了一些实际应用中Q编码的实现Ҏ。然后,本文讲述了通常对字W与~码的几U误解,׃q些误解而导致ؕ码生的原因Q以及消除ؕ码的办法。本文的内容늛了“中文问题”,“ؕ码问题”?/p>
掌握~码问题的关键是正确地理解相x念,~码所涉及的技术其实是很简单的。因此,阅读本文旉要慢d惻I多思考?/p>
“字W与~码”是一个被l常讨论的话题。即使这P时常出现的ؕ码仍然困扰着大家。虽然我们有很多的办法可以用来消除ؕ码,但我们ƈ不一定理解这些办法的内在原理。而有的ؕ码生的原因Q实际上׃底层代码本n有问题所D的。因此,不仅是初学者会对字W编码感到模p,有的底层开发h员同样对字符~码~Z准确的理解?/p>
![]() |
|||
|
从计机对多国语a的支持角度看Q大致可以分Z个阶D:
pȝ内码 | 说明 | pȝ | |
阶段一 | ASCII | 计算机刚开始只支持pQ其它语a不能够在计算Z存储和显C?/td> | 英文 DOS |
阶段?/td> | ANSI~码 Q本地化Q?/td> | Z计算机支持更多语aQ通常使用 0x80~0xFF 范围?2 个字节来表示 1 个字W。比如:汉字 '? 在中文操作系l中Q?[0xD6,0xD0] q两个字节存储?br /> 不同的国家和地区制定了不同的标准Q由此生了 GB2312, BIG5, JIS {各自的~码标准。这些?2 个字节来代表一个字W的各种汉字延~码方式Q称?b> ANSI ~码。在体中文系l下QANSI ~码代表 GB2312 ~码Q在日文操作pȝ下,ANSI ~码代表 JIS ~码?br /> 不同 ANSI ~码之间互不兼容Q当信息在国际间交流Ӟ无法属于两U语a的文字,存储在同一D?b> ANSI ~码的文本中?/td> | 中文 DOSQ中?Windows 95/98Q日?Windows 95/98 |
阶段?/td> | UNICODE Q国际化Q?/td> | Z使国际间信息交流更加方便Q国际组l制定了 UNICODE 字符?/b>Qؓ各种语言中的每一个字W设定了l一q且唯一的数字编P以满语言、跨q_q行文本转换、处理的要求?/td> | Windows NT/2000/XPQLinuxQJava |
字符串在内存中的存放ҎQ?/p>
?ASCII 阶段Q?b>单字节字W串使用一个字节存放一个字W(SBCSQ。比如,"Bob123" 在内存中为:
42 | 6F | 62 | 31 | 32 | 33 | 00 |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
B | o | b | 1 | 2 | 3 | \0 |
在?ANSI ~码支持多种语言阶段Q每个字W用一个字节或多个字节来表C(MBCSQ,因此Q这U方式存攄字符也被UC多字节字W?/b>。比如,"中文123" 在中?Windows 95 内存中ؓ7个字节,每个汉字?个字节,每个英文和数字字W占1个字节:
D6 | D0 | CE | C4 | 31 | 32 | 33 | 00 |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
||
?/td> | ?/td> | 1 | 2 | 3 | \0 |
?UNICODE 被采用之后,计算机存攑֭W串Ӟ改ؓ存放每个字符?UNICODE 字符集中的序受目前计机一般?2 个字节(16 位)来存放一个序PDBCSQ,因此Q这U方式存攄字符也被UC宽字节字W?/b>。比如,字符?"中文123" ?Windows 2000 下,内存中实际存攄?5 个序P
2D | 4E | 87 | 65 | 31 | 00 | 32 | 00 | 33 | 00 | 00 | 00 | ??x86 CPU 中,低字节在?/font> |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
||||||
?/td> | ?/td> | 1 | 2 | 3 | \0 |
一共占 10 个字节?/p>
![]() |
|||
|
理解~码的关键,是要把字W的概念和字节的概念理解准确。这两个概念ҎhQ我们在此做一下区分:
概念描述 | 举例 | |
字符 | Z使用的记P抽象意义上的一个符受?/td> | '1', '?, 'a', '$', 'K?, …?/td> |
字节 | 计算Z存储数据的单元,一?位的二进制数Q是一个很具体的存储空间?/td> | 0x01, 0x45, 0xFA, …?/td> |
ANSI 字符?/td> | 在内存中Q如果“字W”是?ANSI ~码形式存在的,一个字W可能用一个字节或多个字节来表C,那么我们U这U字W串?ANSI 字符?/b>或?b>多字节字W串?/td> | "中文123" Q占7字节Q?/font> |
UNICODE 字符?/td> | 在内存中Q如果“字W”是以在 UNICODE 中的序号存在的,那么我们U这U字W串?UNICODE 字符?/b>或?b>宽字节字W串?/td> | L"中文123" Q占10字节Q?/font> |
׃不同 ANSI ~码所规定的标准是不相同的Q因此,对于一个给定的多字节字W串Q我们必ȝ道它采用的是哪一U编码规则,才能够知道它包含了哪些“字W”。而对?UNICODE 字符?/b>来说Q不在什么环境下Q它所代表的“字W”内ҎL不变的?/p>
1.3 字符集与~码
各个国家和地区所制定的不?ANSI ~码标准中,都只规定了各自语a所需的“字W”。比如:汉字标准QGB2312Q中没有规定韩国语字W怎样存储。这?ANSI ~码标准所规定的内容包含两层含义:
各个国家和地区在制定~码标准的时候,“字W的集合”和“编码”一般都是同时制定的。因此,q_我们所说的“字W集”,比如QGB2312, GBK, JIS {,除了有“字W的集合”这层含义外Q同时也包含了“编码”的含义?/p>
?b>UNICODE 字符?/b>”包含了各种语言中用到的所有“字W”。用来给 UNICODE 字符集编码的标准有很多种Q比如:UTF-8, UTF-7, UTF-16, UnicodeLittle, UnicodeBig {?/p>
![]() |
|||
|
单介l一下常用的~码规则Qؓ后边的章节做一个准备。在q里Q我们根据编码规则的特点Q把所有的~码分成三类Q?/p>
分类 | ~码标准 | 说明 |
单字节字W编?/td> | ISO-8859-1 | 最单的~码规则Q每一个字节直接作Z?UNICODE 字符。比如,[0xD6, 0xD0] q两个字节,通过 iso-8859-1 转化为字W串Ӟ直接得?[0x00D6, 0x00D0] 两个 UNICODE 字符Q即 "?D"?br /> 反之Q将 UNICODE 字符串通过 iso-8859-1 转化为字节串Ӟ只能正常转化 0~255 范围的字W?/td> |
ANSI ~码 | GB2312, BIG5, Shift_JIS, ISO-8859-2 …?/td> | ?UNICODE 字符串通过 ANSI ~码转化为“字节串”时Q根据各自编码的规定Q一?UNICODE 字符可能转化成一个字节或多个字节?br /> 反之Q将字节串{化成字符串时Q也可能多个字节转化成一个字W。比如,[0xD6, 0xD0] q两个字节,通过 GB2312 转化为字W串Ӟ得?[0x4E2D] 一个字W,?'? 字?br /> “ANSI ~码”的特点Q?br />1. q些“ANSI ~码标准”都只能处理各自语言范围之内?UNICODE 字符?br />2. “UNICODE 字符”与“{换出来的字节”之间的关系是h定的?/td> |
UNICODE ~码 | UTF-8, UTF-16, UnicodeBig …?/td> | 与“ANSI ~码”类似的Q把字符串通过 UNICODE ~码转化成“字节串”时Q一?UNICODE 字符可能转化成一个字节或多个字节?br /> 与“ANSI ~码”不同的是: 1. q些“UNICODE ~码”能够处理所有的 UNICODE 字符?br />2. “UNICODE 字符”与“{换出来的字节”之间是可以通过计算得到的?/td> |
我们实际上没有必要去q每一U编码具体把某一个字W编码成了哪几个字节Q我们只需要知道“编码”的概念是把“字W”{化成“字节”就可以了。对于“UNICODE ~码”,׃它们是可以通过计算得到的,因此Q在Ҏ的场合,我们可以M解某一U“UNICODE ~码”是怎样的规则?/p>
![]() |
|||
|
?C++ ?Java 中,用来代表“字W”和“字节”的数据cdQ以及进行编码的ҎQ?/p>
cd或操?/b> | C++ | Java |
字符 | wchar_t | char |
字节 | char | byte |
ANSI 字符?/td> | char[] | byte[] |
UNICODE 字符?/td> | wchar_t[] | String |
字节东y字符?/td> | mbstowcs(), MultiByteToWideChar() | string = new String(bytes, "encoding") |
字符东y字节?/td> | wcstombs(), WideCharToMultiByte() | bytes = string.getBytes("encoding") |
以上需要注意几点:
![]() |
|||
|
声明一D字W串帔RQ?/p>
// ANSI 字符Ԍ内容长度 7 字节
char sz[20] = "中文123"; // UNICODE 字符Ԍ内容长度 5 ?wchar_tQ?0 字节Q?/span> wchar_t wsz[20] = L"\x4E2D\x6587\x0031\x0032\x0033"; |
UNICODE 字符串的 I/O 操作Q字W与字节的{换操作:
// q行时设定当?ANSI ~码QVC 格式 setlocale(LC_ALL, ".936"); // GCC 中格?/span> setlocale(LC_ALL, "zh_CN.GBK"); // Visual C++ 中用小?%sQ按?setlocale 指定~码输出到文?br />// GCC 中用大?%S fwprintf(fp, L"%s\n", wsz); // ?UNICODE 字符串按?setlocale 指定的编码{换成字节 wcstombs(sz, wsz, 20); // 把字节串按照 setlocale 指定的编码{换成 UNICODE 字符?br />mbstowcs(wsz, sz, 20); |
?Visual C++ 中,UNICODE 字符串常量有更简单的表示Ҏ。如果源E序的编码与当前默认 ANSI ~码不符Q则需要?#pragma setlocaleQ告诉编译器源程序用的~码Q?/p>
// 如果源程序的~码与当前默?ANSI ~码不一_ // 则需要此行,~译时用来指明当前源E序使用的编?/font> #pragma setlocale (".936") // UNICODE 字符串常量,内容长度 10 字节 wchar_t wsz[20] = L"中文123"; |
以上需要注?#pragma setlocale ?setlocale(LC_ALL, "") 的作用是不同的,#pragma setlocale 在编译时起作用,setlocale() 在运行时起作用?/p>
![]() |
|||
|
字符串类 String 中的内容?UNICODE 字符Ԍ
// Java 代码Q直接写中文
String string = "中文123"; // 得到长度?5Q因为是 5 个字W?/span> System.out.println(string.length()); |
字符?I/O 操作Q字W与字节转换操作。在 Java ?java.io.* 中,以“Stream”结cM般是用来操作“字节串”的c,以“Reader”,“Writer”结cM般是用来操作“字W串”的cR?/p>
// 字符串与字节串间怺转化 // 按照 GB2312 得到字节Q得到多字节字符Ԍ byte [] bytes = string.getBytes("GB2312"); // 从字节按?GB2312 得到 UNICODE 字符?/span> string = newString(bytes, "GB2312"); // 要将 String 按照某种~码写入文本文gQ有两种ҎQ?br /> // W一U办法:?Stream cd入已l按照指定编码{化好的字节串 OutputStream os = new FileOutputStream("1.txt"); os.write(bytes); os.close(); // W二U办法:构造指定编码的 Writer 来写入字W串 Writer ow = new OutputStreamWriter(new FileOutputStream("2.txt"), "GB2312"); ow.write(string); ow.close(); /* 最后得到的 1.txt ?2.txt 都是 7 个字?*/ |
如果 java 的源E序~码与当前默?ANSI ~码不符Q则在编译的时候,需要指明一下源E序的编码。比如:
E:\>javac -encoding BIG5 Hello.java |
以上需要注意区分源E序的编码与 I/O 操作的编码,前者是在编译时起作用,后者是在运行时起作用?/p>
![]() |
|||
|
对编码的误解 | |
误解一 | 在将“字节串”{化成“UNICODE 字符东y时Q比如在d文本文gӞ或者通过|络传输文本ӞҎ“字节串”简单地作ؓ单字节字W串Q采用每“一个字节”就是“一个字W”的Ҏq行转化?br /> 而实际上Q在非英文的环境中,应该“字节串”作?ANSI 字符Ԍ采用适当的编码来得到 UNICODE 字符Ԍ有可能“多个字节”才能得到“一个字W”?br /> 通常Q一直在英文环境下做开发的E序员们Q容易有q种误解?/td> |
误解?/td> | ?DOSQWindows 98 {非 UNICODE 环境下,字符串都是以 ANSI ~码的字节Ş式存在的。这U以字节形式存在的字W串Q必ȝ道是哪种~码才能被正地使用。这使我们Ş成了一个惯性思维Q“字W串的编码”?br /> ?UNICODE 被支持后QJava 中的 String 是以字符的“序号”来存储的,不是以“某U编码的字节”来存储的,因此已经不存在“字W串的编码”这个概念了。只有在“字W串”与“字节串”{化时Q或者,一个“字节串”当成一?ANSI 字符串时Q才有编码的概念?br /> 不少的h都有q个误解?/td> |
W一U误解,往往是导致ؕ码生的原因。第二种误解Q往往D本来ҎU正的ؕ码问题变得更复杂?/p>
在这里,我们可以看到Q其中所讲的“误解一”,即采用每“一个字节”就是“一个字W”的转化ҎQ实际上也就{同于采?iso-8859-1 q行转化。因此,我们常常使用 bytes = string.getBytes("iso-8859-1") 来进行逆向操作Q得到原始的“字节串”。然后再使用正确?ANSI ~码Q比?string = new String(bytes, "GB2312")Q来得到正确的“UNICODE 字符东y?/p>
![]() |
|||
|
?UNICODE E序中的字符Ԍ都是以某U?ANSI ~码形式存在的。如果程序运行时的语a环境与开发时的语a环境不同Q将会导?ANSI 字符串的昄p|?/p>
比如Q在日文环境下开发的?UNICODE 的日文程序界面,拿到中文环境下运行时Q界面上显CZؕ码。如果这个日文程序界面改为采?UNICODE 来记录字W串Q那么当在中文环境下q行Ӟ界面上将可以昄正常的日文?/p>
׃客观原因Q有时候我们必d中文操作pȝ下运行非 UNICODE 的日文YӞq时我们可以采用一些工P比如Q南极星QAppLocale {,暂时的模拟不同的语言环境?/p>
![]() |
|||
|
当页面中的表单提交字W串Ӟ首先把字W串按照当前面的编码,转化成字节串。然后再每个字节{化成 "%XX" 的格式提交到 Web 服务器。比如,一个编码ؓ GB2312 的页面,提交 "? q个字符串时Q提交给服务器的内容?"%D6%D0"?/p>
在服务器端,Web 服务器把收到?"%D6%D0" 转化?[0xD6, 0xD0] 两个字节Q然后再Ҏ GB2312 ~码规则得到 "? 字?/p>
?Tomcat 服务器中Qrequest.getParameter() 得到qӞ常常是因为前面提到的“误解一”造成的。默认情况下Q当提交 "%D6%D0" l?Tomcat 服务器时Qrequest.getParameter() 返?[0x00D6, 0x00D0] 两个 UNICODE 字符Q而不是返回一?"? 字符。因此,我们需要?bytes = string.getBytes("iso-8859-1") 得到原始的字节串Q再?string = new String(bytes, "GB2312") 重新得到正确的字W串 "??/p>
![]() |
|||
|
通过数据库客LQ比?ODBC ?JDBCQ从数据库服务器中读取字W串Ӟ客户端需要从服务器获知所使用?ANSI ~码。当数据库服务器发送字节流l客LӞ客户端负责将字节按照正的~码转化?UNICODE 字符丌Ӏ?/p>
如果从数据库d字符串时得到qQ而数据库中存攄数据又是正确的,那么往往q是因ؓ前面提到的“误解一”造成的。解决的办法q是通过 string = new String( string.getBytes("iso-8859-1"), "GB2312") 的方法,重新得到原始的字节串Q再重新使用正确的编码{化成字符丌Ӏ?/p>
![]() |
|||
|
当一D?Text 或?HTML 通过电子邮g传送时Q发送的内容首先通过一U指定的字符~码转化成“字节串”,然后再把“字节串”通过一U指定的传输~码QContent-Transfer-EncodingQ进行{化得到另一东y字节串”。比如,打开一电子邮件源代码Q可以看到类似的内容Q?/p>
Content-Type: text/plain; charset="gb2312" Content-Transfer-Encoding: base64 sbG+qcrQuqO17cf4yee74bGjz9W7+b3wudzA7dbQ0MQNCg0KvPKzxqO6uqO17cnnsaPW0NDEDQoNCg== |
最常用?Content-Transfer-Encoding ?Base64 ?Quoted-Printable 两种。在对二q制文g或者中文文本进行{化时QBase64 得到的“字节串”比 Quoted-Printable 更短。在对英文文本进行{化时QQuoted-Printable 得到的“字节串”比 Base64 更短?/p>
邮g的标题,用了一U更短的格式来标注“字W编码”和“传输编码”。比如,标题内容?"?Q则在邮件源代码中表CZؓQ?/p>
// 正确的标题格?/span>
Subject: =?GB2312?B?1tA=?= |
其中Q?/p>
如果“传输编码”改?Quoted-PrintableQ同P如果标题内容?"?Q?/p>
// 正确的标题格?/span>
Subject: =?GB2312?Q?=D6=D0?= |
如果阅读邮g时出Cؕ码,一般是因ؓ“字W编码”或“传输编码”指定有误,或者是没有指定。比如,有的发邮件组件在发送邮件时Q标?"?Q?/p>
// 错误的标题格?/span>
Subject: =?ISO-8859-1?Q?=D6=D0?= |
q样的表C,实际上是明确指明了标题ؓ [0x00D6, 0x00D0]Q即 "?D"Q而不?"??/p>
![]() |
|||
|
非也。iso-8859-1 只是单字节字W集中最单的一U,也就是“字节编号”与“UNICODE 字符~号”一致的那种~码规则。当我们要把一个“字节串”{化成“字W串”,而又不知道它是哪一U?ANSI ~码Ӟ先暂时地把“每一个字节”作为“一个字W”进行{化,不会造成信息丢失。然后再使用 bytes = string.getBytes("iso-8859-1") 的方法可恢复到原始的字节丌Ӏ?/p>
Java 中,字符串类 java.lang.String 处理的是 UNICODE 字符Ԍ不是 ANSI 字符丌Ӏ我们只需要把字符串作为“抽象的W号的串”来看待。因此不存在字符串的内码的问题?/p>
作者:飞云侠 来自QCSDN
Zl朋友同事一些设计问题上的指|Ҏ写此文,很多观点都是从别人的文章中获取,有些观点肯定也有偏颇Q有些观点也仅仅是提出ƈ没有做详l论qͼ请多拍砖Q以便改正?
?strong>概述?/p>
在工作中Q作Z个程序员或者一个设计师QL要设计一些函数库或者一个框Ӟ当然最l常的还是做目Q即使是一个项目,也会被经常改动,甚至交给别h改动?br /> 当你做这些工作的时候,你的q些成果都是要给别h了解使用的,或者说l以后的你用的Qؓ了别人的方便或者ؓ了自q方便Q我们要可能做好设计?br />
?strong>放正心态,M东西都是不断发展?/strong>?/p>
技术是日新月异的,每一天都有新的技术出来,正所?山外有山Qh外有?Q每一个新的轮子出来,都可能比你要设计的轮子好Q所以在设计的时候,应该了解一下是否已l有了类似的轮子Q是否要设计一个新的轮子?/p>
即你的轮子已经设计好了Q也不好认ؓ自己的轮子一定比别h的轮子好Q虽然你的轮子可能更适合你的实际使用?/p>
技术在不断的发展中Q你以及你的朋友/同事都在不断q步Q?士别三日Q当刮目相看"Q所以不要认Z的水q一定比别h高,"有所短,寸有所?Q所以别人对你的函数?框架提出意见Q提出疑问的时候,请不要惊奇,不要反感Q不要认为别人在"挑刺"Q也怽的函数库/框架早就不适合当前的发展了?br />
态度军_一切。你的领导或许更重视q一炏V?br />
?strong>必要的组成部?单元试Q文档,实例Q手册etc?/p>
单元试Q文档,API DocQ手册,演示E序QChange LogQReadmeQbuild。xml{等
有一天别Z用了你设计的函数?框架Q当你升U后Q原来的目却不能工作了Q经q一天的调试Q你l于扑ֈ了原因,原来是不心写错了一个东ѝ?/p>
你肯定不希望上述的事情发生,那么请你写单元测试吧Q这h不浪费自q旉Q也不耽误别h的工作,何乐而不为。你花在写单元测试的旉/带来的乐和你升U后Ҏ莫名其妙的错误的旉和苦恼相比,肯定更有价倹{你看到单元试的绿条,N不感到高兴吗?!
如果你不能保证你的程序修Ҏ有错误,不要指望你的同事认ؓ你的错误是可以容忍的Q他们在心里早就开始骂你了Q呵c写单元试?br />
看看M一个知名的框架Q都包含完善的文档,单元试Q示例程序,用户手册Q那么请你也包含q些吧。哦Q对了,误l地写好JavaDocQ它很重要?br />
使用你的框架/函数库的人如果到处去找用方法,L某个c?但是他不知道是否有这个类)Q那么说明你的文档没有到位。如果你希望别h使用你的q个cL者功能,那么请写好文档,不要指望别h去读你的源码然后p理解它是q什么用的?br />
如果你做到这些,那么你的函数?框架也有?知名"的前提,N不是?如果没有Q我x没法让别人更好地使用的?br />
对了Q有了这些东西,q要有一个良好的目录l织Q这个也可以参考别的框架的l织方式?/p>
?strong>借鉴成熟的设计,参考已有的目?/p>
1. 要做一个新的东西,没有x。不要惊Ӟ我肯定先找一个现有的东西来借鉴?br />
当然前提是不要重新发明轮子,或者是你有充分条g要重新发明一个轮子?br /> StrutsQWebWorkQ?a target="_blank">Spring{等都是成熟的框Ӟ不管你用v来是否符合你的习惯?br /> 在你成ؓ大师之前Q你的设计思想估计前h都已l提出ƈ实践q了Q所以要勇敢地去借鉴?站在巨h的肩膀?我们能更q一步?br />
例如我们厌倦了在访问数据库时用如下的代码:
try
{
//your code here
}
catch(Exception e)
{
//catch Exception
}
finally
{
//must do something
}
我们可以借鉴Spring框架的JdbcTemplatec,看看它是如何利用回调函数来处理的?
我们使用hibernate时是不是也会使用cM上面的代码,那么可以参考Spring框架的HibernateTemplate?br />
借鉴也是一U捷径?br />
警告:借鉴但不要抄袭,借鉴代码要注明来源,重他h也是重自己?br />
2. 在实际的目中,往往可以参考已l有的项目来做自q设计?br />
例如做一个网站,我不知道如何讉K数据库,如何布局Q如何分层,那么我们可以参考已l有的网站程序,看看别h是如何利用SiteMesh或者tiles布局Q如何用Hibernate来访问数据库或者用已l封装好的JDBCcL讉K数据库,如何利用StrutsQWebWork或者其他访问来分层?/p>
?strong>遵守U定俗成的一些做?/strong>?
Z使别人更方便C用你的东西,那么在设计一些通用的函数或者类的时候,请遵守通用的做法,不要与众不同Q除非你的内部实现确实与众不同?/p>
例如实现一个类似ArrayList的类Q那么请不要q样?
public int count()
{
return list.size();
}
public Item getItem(int i)
{
return list.get(i);
}
而应该这?
public int size()
{
return list.size();
}
public Item get(int i)
{
return list.get(i);
}
当然每个人都有自qxQ如果你非常认ؓ你原来的方式比普通的好,那么h?套方式供别h选择。它不会l你带来ȝQ只是一个一看就懂的做法Q不用怀疑,q样做有好处?br />
很多cȝ设计都有一些约定俗成的做法Q那么在你设计一个新cȝ时候,先借鉴一下吧Q多看看JDK的源?文档Q看看别人是怎么实现的。这更有助于推广你的成果?br />
?strong>不要q信权威?
在用已有的框架或者函数库Ӟ不要认ؓ所有的东西都是正确的或者是最好的最好,肯定不是。没有完的东西Q已l存在的东西在设计的时候因为种U局限或者因Z者的水^Q对现在来说肯定存在不合理的设计Q或者过于理惛_的设计,而不能满_际情c?br />
不迷信权威,才能到达新的境界?/p>
?strong>不要L排斥Q不了解׃要草率发表意见,要严?/strong>?/p>
在网上经常看到。Net和Java的比?火拼Q或者是Struts VS Webwork或者是其他{等Q非怹多。经常看到的是一方对Ҏ的东西不甚了解,开始批评,l果说不到点子上Q反而被嘲笑一番?br /> 几种技术的比较有时候是必要的,例如技术选型的时候。但是如果一些对q些技术根本不了解的h来选型Q来评判Q你能对l果信服?
存在是合理QQ何技术都有其存在的理由,虽然有些东西早就q时了,但是在当时它也是应运而生的?br /> 几种技术,都是来解军_L问题Q但是问题也有很多方面,解决方式也有很多U,每个人的x也都不一P思\也不一P所以没有绝对符合要求的技术,但是应该有符合你的技术,不符合你的技术不{于也不满别h的要求。所以不要轻易排斥别的东ѝ?br />
在做技术比较的时候,如果你不了解Q那么请不要L发表意见Q至你可以亲自M解,d践之后在发表你的意见岂不是更好?br />
在发表意见的时候,也要严}Q不要轻易下l论Q要l过求证Q否则一旦错误只会让ҎW话Q让你的同事看不起你。例如你说Hibernate3不支持jdk1?Q那么最好去好好扑ֈ你的证据Q否则就会成为错误?Hibernate3支持jdk1?)
作ؓ一个技术h员,严}应该是我们的习惯之一Q无论做开发还是做设计?/p>
?strong>处理好你的异?/strong>?/p>
异常处理是Java~程中非帔R要的一个部分。徏议在使用异常之前阅读或者?br />
下面从书中摘出几条徏?
* l对不要忽略异常
* 千万不要隐藏异常
* 仅在不正常的情况下用异?br /> * 对可恢复的情况用可查异常,对程序错误用运行时异常(RunTimeException)
* l方法引发的异常做文?br /> * 在详l信息里面包括失败捕获信?br /> * 使用finally避免资源泄漏
* ....
在这里特别提出的是,在开发中要特别处理NULL的情况,否则l常引发NullPointException异常Q在Java里这是一个最令h头疼的异怺?br /> 如果你的E序因ؓ一个NULL|而报了几十个NullPointException的话Q不但得让h烦死Q而且q非帔R以找到错误所在。所以在Java中一定要注意q个问题?br /> 如果你的函数不允许Null|那么可以截获它,抛出一个异常,或者给客户更友好的提示Q难道不好吗?
让我们来看一个例?
public String getName(User aUser)
{
//如果aUser为NullQ会发生什么情?br /> return aUser.getName();
}
很明显,如果参数为NullQ就会抛出异常。应该改?
public String getName(User aUser)
{
if(null=aUser)
{
return "";
}
else
{
return aUser.getName();
}
}
或者你要求参数不能为空Q还可以抛出一个异常,强制使用者不能传入空倹{?br />
q有l常被忽略的是RunTimeException和普通异常的区别Q在Java中,q是一个特D的异常c,E序中如果遇到这个异常,用户可以不截获它Q而如果是其他的普通异常,׃许要截获它。我们的代码l常q么?
try
{
//your code here
}
catch(Exception e)
{
//do warn
}
q样写的话,截获了所有异常,当然也包括了RunTimeException?在很多情况下Q这是不合适的处理方式Q我们只应截获必要的异常Q而应该忽略RuntimeException?br />
关于RunTimeExceptionQ在Spring中还有更好的利用方式Q徏议阅读Spring框架中在事务中对异常的处理代码,例如对Jdbc抛出的SqlException的{换?br />
关于异常处理Q我提出几点:
* 捕获异常而且再次抛出时要包含原来的异怿?br /> * 不要忘了RunTimeExceptionQ除非必要,否则不要用catch(Exception e)的方式捕h有异常?br /> * 不要用异常做程控制Q异常的性能代h比较高昂?ҎQ可能有Z同意。此处不详细讨论)
* 不要把异常处理都抛给别hQ本函数有能力处理的׃要抛出?br />
在此读者详l阅L者?br />
?strong>q度依赖?/p>
在定位错误的时候,l常遇到览了七 八个文gq是没有扑ֈ什么地Ҏ行了真正需要的函数Q这个时候就非常郁闷。A调用了BQB调用了CQC调用了D。。。。。。让人找不到?br />
面对q样的程序,存在的问题不仅仅是定位错误麻烦,而且如果需要维护这L函数?框架Q恐怕你的有非常高的lM能力才行Q否则打L也不ȝ护?br />
那么我们自己最好不要写q样的程序出来给人用?/p>
?strong>滥用接口?/p>
现在行"面对接口~程"Q这本n本来是不错,但是滥用接口的现象却l常发生?br /> "面向接口"Q于是所有的c都有一个对应的接口Q接口的函数声明和类一模一P而且一个接口只有一个类来实现它。这L面向接口有什么意义哪? (Z用Spring的事务的情况除外)
Ҏ"q比Ҏ?Law of Demter)"Q一个对象应当对其他对象有尽可能的了解。一个接口内应该只定义对Ҏ需要的ҎQ而不要把一些没用的Ҏ声明攑֜接口里面?br />
例如如下一个类:
public class MyCounter
{
private int n1;
private int n2;
public MyCounter(int n1Qint n2)
{
this。n1=n1;
this。n2=n2;
}
public void setN1(int n1)
{
return this.n1 = n1;
}
public void setN2(int n2)
{
return this.n2 = n2;
}
public int getN1()
{
return n1;
}
public int getN2()
{
return n2;
}
public int getResult()
{
return n1 + n2;
}
}
我们可以看到Q这个类的主要目的是得到计算l果Q所以正的接口应该cM:
public interface Counter
{
int getResult();
}
但是很多情况下,l常是这L接口:
public interface Counter
{
int getResult();
int getN1();
int getN2();
void setN1(int n1);
void setN2(int n2);
}
我们想一惻Iq样做有2个后?
1. 除了getResult之外Q其他的函数我们Ҏ用不刎ͼ所以是多余的?br /> 2. 如果我们要自己实C个CounterQ如果接口中仅仅定义了getResultQ我们仅仅需要实现它可以了。我们自qcd能是多个数运,有乘除加减等{各U运,参数也有可能是一些数l。但是如果按照第二种Ҏ声明接口的话Q我们就必须实现后面的四个方法,如果q样的话Q实现这样东西不仅没用,而且费旉。我们恐怕要大声骂娘了吧?br />
所以,接口有好的作用,但是不要滥用?br /> ?如果你的接口永远只有一个类实现Q那么可能就没有必要用接口?br /> ?你的接口只需要声明别人用到的函数卛_?/p>
【空接口的用?/strong>
在接口用的时候,I接口有2U情?
1. cMCloneableQSerializableQ他们往往是做一个标讎ͼ表示需要某个功能。当然你也可以这么用Q来表示你的cd有某个功能,实现了你的某个接口?br /> 2. 你的接口l承了别的接?非空)Q你的接口本w没有声明函数。这U情况一般是你不希望用户使用父接口来作ؓ参数cdQ因Z们的用途可能不同,此时可以用I接口来实现?br />
W一U情冉|们不再多_搜烦一下关于CloneableQSerializable的文章就会了解很多?br /> 我们来看下面的代?
public interface Text
{
String getText();
}
public interface SqlText extends Text
{
}
可以看到QText接口是用于返回一个字W串。而SqlText是一个空接口Q它l承了Text接口。也是说SqlText也是一UText。但是我们可以知道,M一个字W串不一定是Sql字符Ԍ所以此时声明了一个SqlText接口来用于表名当前的字符串是一个Sql字符丌Ӏ你的函数可以这样声?
public void doQuery(SqlText aSqlText)
而不是这?br />
public void doQuery(Text aText)
避免用户产生歧义的想法,一眼看去,明白应该传入一个Sql字符丌Ӏ?br />
【承层ơ过多?/strong>
一般来_l承的层ơ不要过多,否则使用者可能会讨厌Q找一个函C很麻烦。很多Java语言查工具都你的l承层次不要过3层?br />
【Has A QIs AQ不要滥用ѝ?/strong>
"我是一个Mp3"Q?我有一个Mp3"Q其实很Ҏ分L。但是在实际应用中,往往存在?我有一个Mp3"的情况当?我是一个Mp3"Q或者是Zh方便而放松了对自q要求Q甚臌沾沾自喜Q感觉找C个捷径?scud以前也干q这U事??br />
以前我曾l这样干q?我的逻辑cȝ接承了我的数据库访问类Q这h可以直接在逻辑c里面访?
public MyLogic extends MyDBA
aLogic.getInt("click");
aLogic.getString("name");
看v来是非常方便Q但是你的逻辑cd牢牢l在了DBA上,是一U非怸好的做法。现在我q样声明:
public MyLogic
MyDBA adba;
adba.getInt("click");
adba.getString("name");
其实代码改动不大Q但是你的逻辑cM在牢牢绑在DBAw上了,何乐而不为?br />
其实q种现象在开发h员中间可能经常见刎ͼ我们要尽量避免。下面再来看一个例?
//一个保存分信息的c?br />
public class PageInfo
{
private int page;
private int pageCount;
private int recPerPage;
private int recCount;
//getQset method list...
}
一般的情况是,在Dao中进行分|询,计算总记录,总页数等{,所以需要把PageInfo传给Dao。而在逻辑cMQ把传回来的分页信息数据推到FormBean或者是Action中?br /> 也许你会q么惻I如果我的Action或者FormBeanl承了PageInfoQ岂不是要省很多事?br />
千万别这么干。ƈ不是所有的动作都需要分信息,你的FormBean和PageInfo没有l承的关pR也是说FormBean Has A PageInfoQ但是不是Is A PageInfo?br />
【保持外?行ؓ一致?/strong>
外观一致其实很Ҏ理解Q例如你用size()表示得到一个List的大,那么在所有的ListcM你都用size()得到它的大小Q这是外观一致?br /> 外观一致让用户更方便用你的函数库Q不用记住几个不同的表示同一个功能的函数名字。或者几个名字相同功能却不同的函数。那很p糕了?br />
行ؓ一致相对外观一致就相对比较隑ց刎ͼ但是优秀的设计师肯定会让他的成果行ؓ一_而不是出人意料的行ؓQ也不是一套强行规定的行ؓ?br />
我们来看下面的代?
import java.util.HashMap;
import java.util.Map;
class UserInfo
{
private String realname;
public UserInfo(String sName)
{
this.realname = sName;
}
public void setName(String sName)
{
this.realname = sName;
}
public String getName()
{
return this.realname;
}
}
public class MyTest
{
Map userInfoMap = new HashMap();
public void setUserInfo(String sName,UserInfo aInfo)
{
userInfoMap.put(sName,aInfo);
userInfoMap.put(aInfo.getName(),aInfo);
}
public UserInfo getUserInfo(String sName)
{
return (UserInfo)userInfoMap.get(sName);
}
public static void main(String args[])
{
MyTest aTest = new MyTest();
UserInfo aUserInfo = new UserInfo("王小?);
aTest.setUserInfo("儿童团团?,aUserInfo);
aTest.setUserInfo("三班班长",aUserInfo);
UserInfo 儿童团团?= aTest.getUserInfo("儿童团团?);
if(null!=儿童团团?
{
System.out.println(儿童团团?getName());
}
else
{
System.out.println("儿童团团?Not Found");
}
UserInfo 王小?= aTest.getUserInfo("王小?);
if(null!=王小?
{
System.out.println(王小?getName());
}
else
{
System.out.println("王小?Not Found");
}
}
}
可以看到Q上面的代码q行l果?王小?Q也是说儿童团团长是王二Q王二本n也是王小二,q一切正常?br />
现在我们把setUserInfo里面的第一句注释掉:
public void setUserInfo(String sName,UserInfo aInfo)
{
//userInfoMap.put(sName,aInfo);
userInfoMap.put(aInfo.getName(),aInfo);
}
再次q行上面的代码,我们发现儿童团团长不存在了,但是王小二还在。还可以看出Q如果找"三班班长"的话Q肯定也找不刎ͼ也就是说只有依据王小二的真名才能扑ֈ王小二,其他Ҏ׃行了?br />
从上面的setUserInfo和getUserInfo分析Q如果采用修改后的代码,我们的程序就出现了行C一_而这是o惑不解的Q我们set了半天,却找不到Q岂不是令h恼火!
当然上面的代码比较简单,通过单的修改p做到行ؓ一_但在实际~程中,往往因ؓ复杂的行为操作,l常会造成行ؓ不一_从而给开发h员带来困惑?/p>
【MVCQMVC2QWEB设计~程的分层?/strong>
请阅L?http://forum.javaeye.com/viewtopic.php?t=11712&postdays=0&postorder=asc&start=0
【可扩展不等于功能强大,不要夸大其辞?/strong>
现在的系l,因ؓ接口或者其他方法的使用Q都h很大的扩展性。但是扩展性不{于功能强大?br /> 存在一个接口,用户可以实现自己的接口,实非常方便。但是如果你的系l本w只实现了一个接口或者根本没有实玎ͼ那么对用h说就谈不上方ѝ?br />
例如WebWork的validatorsQ本w是一个接口,但是实际上本w实现的具体cd,而且功能很差Q这个时候如果你说WebWork的校验器很厉宻I那么可能不太恰当了。当然扩展Webwork的Validatorq是非常方便的?br />
当然Q可扩展性还是需要的Q但是不要吹嘘,在这个Qw的q代Q让我们多干点实事?:)
?0/80原则?/strong>
在工作中Q我l常惛_20/80原则Q也是"巴雷多原?。例如我们可以看?
旉Q我?0%的时间会产生成果?0%
产品Q品的20%带来利润?0%
阅读Q?0%的书幅包括了内容的80%
工作Q?0%的工作给我们80%的满?br />
演讲Q?0%的演讲生媄响的80%
领导Q?0%的h作出80%的决?/p>
从上面可以看出,很多时候它都很有说服力?br /> 在这里我x到几点,但是和上面的可能出发Ҏ所不同:
1、程序的80%都是在处理特D情况,所以我们一定要对特D情况重视,不要因ؓ是特D情况,׃很重视?0%的客户对Ҏ情况都很重视?br /> 文档对特D情况也要详l描qͼ因ؓ开发h?0%的时候在查找q些东西Q而对那些l常用到的用法却很少查阅文档?br />
2、优化问?80%的瓶颈都出在20%的代码上Q所以在优化代码的时候不需要优化所有代码,只需要优?0%的关键代码就够了。当然追求完的人我们就不多说了?
记得有一条优化的原则?不要优化!不要优化"Q是非常有道理的?br />
3、如果你20%的事情做怺Q往往会导?0%的事情都怺Q或者是D别h认ؓ你把事情几乎都做怺?br /> 如果你对一些事情发表了一些很不严谨的看法Q那么别Z认ؓ你在别的事情上也很不严}?br /> 依此cLQ代码质量,文档完整性等{,都会让h产生cM的推理?br />
(当然一个代码写的很q人,往往文档也很乱?
【强制绑定是不受Ƣ迎的?/strong>
不要在程序中强制l定一些额外的功能?br />
有的框架往往功能很多Q是"大型计算?Q有很多功能Q但是在我需要打字的时候,l我打字的功能即可,不要强制我用网l功能,打印功能Q负载均衡功能等{?br />
一般来_如果一个东西有很多功能Q那么做好做成可配置Q可插拔的,q样用户使用你的东西Q没必要在不使用高功能的时候,费用户的内存,盘。开发h员还得多copy好多lib文gQ占用调试时_岂不是很ȝ?br />
不要C送一Q我不想要就别给我?:)
【有时候也得考虑兼容性?/strong>
一般来_一个公司的客户会有很多Q用Lq行环境是各U各L。jdk1.3Qjdk1.4甚至q有jdk1.2。这h们在~程的时候就必须做一些妥协,有些函数库就不能使用?br /> 如果q些用户的jdk不能升(一般来说都需要购买新的品才能升U?Q或者我们必dq些情况妥协Q那么我们就要在开发中考虑q些问题?br />
例如以前Q在Servlet 2.2的时候,因ؓ没有setCharacterEncodingQ我们必L动对各种字符q行转换。当Servlet2.3的时候,可以使用q个函数了。但是ؓ了客戯虑Q我们只好没有升U还是用原来的Ҏ?当然后来大多数用户都使用了新的App ServerQ我们就可以使用filter来处理编码问题了)?br />
向下兼容性确实让人头|JDK1.5也发布好久了Q不q我们现在也不能使用Q只能自己没事测试测试?br />
在编E的时候,一定要讄好IDE的兼Ҏ设|,防止我们使用了不能用的Ҏ。JbuilderQEclipse都有cM的设|?br />
【成本与现实Q给用户以选择余地?/strong>
全文索,luceneQlike是三U对大文本字D|索的Ҏ。那么你采用哪一U呢?
也许你会毫不犹U的说"全文? (我看你像TRS公司的托 :P)?br />
正如"强制l定是不受欢q的"里面所说的一P我还是觉得应该给用户以选择的余地?br />
全文索是要花q或者需要配|,而且一般来说数据库专用的全文检索都是不通用的,lucene是需要开发h员开发的Q只有like最单了Q但是太单了Q而且性能也差?br />
q个时候,也许我们应该提供几U方式供用户选择了,用户如何选择那就看他们了。。?br />
【结束语?/strong>
实际开发设计中肯定q存在很多其他的问题Q本文不可能一一。到此ؓ止?:)
希望各位在开发设计中成ؓ高水q的设计师?:)
作者:happy_forever 来自Q?/font> http://www.javafan.net
以下文章都是l典Q看不看随你的便Q我只希望知识掌握在更多中国人的手里Q?/font>
中国有很多小朋友Q他?8,9岁或21,2岁,通过自学也写了不代码,他们有的代码写的很漂亮,一些技术细节相当出众,也很有钻研精,但是他们被一些错误的认识和观点左叻I~Z对系l,对程序的整体理解能力Q这些hQ一个网上的朋友说得很好Q他们实际上只是一些Coding fansQ压Ҏ有资格称为程序员Q但是据我所知,不少网l公司的CTO是q样的coding fans,拿着吓h的工资,做着吓h的项目,目的结局通常也很吓h?
E序员基本素质:
作一个真正合格的E序员,或者说是可以真正合格完成一些代码工作的E序员,应该h的素质?
1Q团队精和协作能力
把它作ؓ基本素质Qƈ不是不重要,恰恰相反Q这是程序员应该具备的最基本的,也是最重要的安w立命之本。把高水q程序员说成独行侠的都是在呓语,M个h的力量都是有限的Q即便如linusq样的天才,也需要通过l成强大的团队来创造奇q,那些遍布全球的ؓlinux写核心的高手们,没有协作_是不可想象的。独行侠可以作一些赚qY件发点小财,但是一旦进入一些大pȝ的研发团队,q入商业化和产品化的开发Q务,~Zq种素质的h完全不合格了?
2Q文档习?
说高水^E序员从来不写文档的肯定是^臭未q的毛孩子,良好的文档是正规研发程中非帔R要的环节Q作Z码程序员Q?0Q的工作旉写技术文档是很正常的Q而作为高U程序员和系l分析员Q这个比例还要高很多。缺乏文档,一个Y件系l就~Z生命力,在未来的查错Q升U以及模块的复用时就都会遇到极大的麻烦?
3Q规范化Q标准化的代码编写习?/strong>
作ؓ一些外国知名Y件公司的规矩Q代码的变量命名Q代码内注释格式Q甚臛_套中行羃q的长度和函数间的空行数字都有明规定,良好的编写习惯,不但有助于代码的UL和纠错,也有助于不同技术h员之间的协作?
有些coding fans叫嚣高水q程序员写的代码旁h从来看不懂,q种叫嚣只能证明他们自己压根不配自称E序员。代码具有良好的可读性,是程序员基本的素质需求?
再看看整个linux的搭建,没有规范化和标准化的代码习惯Q全球的研发协作是绝对不可想象的?
4Q需求理解能?/strong>
E序员需要理解一个模块的需求,很多朋友写E序往往只关注一个功能需求,他们把性能指标全部归结到硬Ӟ操作pȝ和开发环境上Q而忽视了本n代码的性能考虑Q有人曾l放a说写一个广告交换程序很单,q种Z来不知道在百万甚臛_万数量的访问情况下的性能指标是如何实现的Q对于这LE序员,你给他深蓝那套系l,他也做不出太极链的ƈ访能力。性能需求指标中Q稳定性,q访支撑能力以及安全性都很重要,作ؓE序员需要评估该模块在系l运营中所处的环境Q将要受到的负荷压力以及各种潜在的危险和恶意d的可能性。就q一点,一个成熟的E序员至需??q的目研发和跟t经验才有可能有心得?
5Q复用性,模块化思维能力
l常可以听到一些程序员有这L抱怨,写了几年E序Q变成了熟练工,每天都是重复写一些没有Q何新意的代码Q这其实是中国Y件h才最大浪费的地方Q一些重复性工作变成了熟练E序员的主要工作Q而这些,其实是完全可以避免的?
复用性设计,模块化思维是要程序员在完成Q何一个功能模块或函数的时候,要多想一些,不要局限在完成当前d的简单思\上,x看该模块是否可以qq个pȝ存在Q是否可以通过单的修改参数的方式在其他pȝ和应用环境下直接引用Q这样就能极大避免重复性的开发工作,如果一个Y件研发单位和工作l能够在每一ơ研发过E中都考虑到这些问题,那么E序员就不会在重复性的工作中耽误太多旉Q就会有更多旉和精力投入到创新的代码工作中厅R?
一些好的程序模块代码,即便?0q代写成的,拿到现在攑ֈ一些系l里面作为功能模块都能适合的很好,而现在我看到的是Q很多小公司软g一升或改q就动辄全部代码重写Q大部分重复性工作无谓的费了时间和_֊?
6Q测试习?
作ؓ一些商业化正规化的开发而言Q专职的试工程师是不可的Q但是ƈ不是说有了专职的试工程师程序员可以不q行自测QY件研发作Z工E而言Q一个很重要的特点就是问题发现的早Q解决的代hp低,E序员在每段代码Q每个子模块完成后进行认真的试Q就可以量一些潜在的问题最早的发现和解冻Iq样Ҏ体系l徏讄效率和可靠性就有了最大的保证?
试工作实际上需要考虑两方面,一斚w是正常调用的试Q也是看程序是否能在正常调用下完成基本功能Q这是最基本的测试职责,可惜在很多公司这成了唯一的测试Q务,实际上还差的q那Q第二方面就是异常调用的试Q比如高压力负荷下的E_性测试,用户潜在的异常输入情况下的测试,整体pȝ局部故障情况下该模块受影响状况的测试,频发的异常请求阻塞资源时的模块稳定测试等{。当然ƈ不是E序员要对自q每段代码都需要进行这U完整测试,但是E序员必L醒认识自q代码d在整体项目中的地位和各种性能需求,有针Ҏ的q行相关试q尽早发现和解决问题Q当然这需要上面提到的需求理解能力?
7Q学习和ȝ的能?/strong>
E序员是人才很容易被淘汰Q很Ҏ落伍的职业,因ؓ一U技术可能仅仅在三两q内h领先性,E序员如果想安n立命Q就必须不断跟进新的技术,学习新的技能?
善于学习Q对于Q何职业而言Q都是前q所必需的动力,对于E序员,q种要求更加高了。但是学习也要找对目标,一些小coding fans们,他们也|z乐道于他们的学习能力,一会学会了aspQ一会儿学会了phpQ一会儿学会了jspQ他们把q个作ؓ炫耀的资本,盲目的追逐一些肤的Q表面的东西和名词,做网l程序不懂通讯传输协议Q做应用E序不懂中断向量处理Q这L技术h员,不管掌握了多所谓的新语aQ永q不会有质的提高?
善于ȝQ也是学习能力的一U体玎ͼ每次完成一个研发Q务,完成一D代码,都应当有目的的跟t该E序的应用状况和用户反馈Q随时ȝQ找到自q不Q这样逐步提高Q一个程序员才可能成长v来?
一个不具备成长性的E序员,即便眼前看是个高手,也不要选用Q因Z落伍的时候马上就C?
具备以上全部素质的hQ应当说是够格的E序员了Q请注意以上的各U素质都不是由IQ军_的,也不是大学某些课本里可以学习到的Q需要的仅仅是程序员对自己工作的认识Q是一U意识上的问题?
那么作ؓ高E序员,以至于系l分析员Q也是对于一个程序项目的设计者而言Q除了应该具备上q全部素质之外,q需要具备以下素质:
W一Q需求分析能?/strong>
对于E序员而言Q理解需求就可以完成合格的代码,但是对于研发目的组l和理者,他们不但要理解客户需求,更多时候还要自行制定一些需求,Z么这么说呢?
一般而言Q进行研发Q务,也许是客h出需求,也许是市场和营销部门提出的需求,q时候对于研发部门,他们看到的不是一个完整的需求,通常而言Q该需求仅仅是一些功能上的要求,或者更正规些,可能获得一个完整的用户视图Q但是这都不够,因ؓ客户׃非技术因素多一些,他们可能很难提出完整和清晎ͼ或者说专业性的性能需求,但是对于目l织者和规划者,他必能够清醒认识到q些需求的存在q在完成需求分析报告的时候适当的提出,同时要完整和清晰的体现在设计说明书里面,以便于程序员~码时不会失去这些准则?
E序设计者必L理解用户需求所处的环境Qƈ针对性做出需求的分析QD例而言Q同样一个Y仉过ASPU用方式发布和通过License方式发布Q性能需求可能就是有区别的,前者强调的是更好的支撑能力和稳定性,而后者则可能更强调在各种q_下的普适性和安装使用的简h?
W二Q项目设计方法和程处理能力
E序设计者必能够掌握不于两到三种的项目设计方法(比如自顶至下的设计方法,比如快速原型法{等Q,q能够根据项目需求和资源搭配来选择合适的设计Ҏq行目的整体设计。设计方法上选择不当Q就会耽误研发周期Q浪费研发资源,甚至影响研发效果?
一个程序设计者还需要把很多功夫用在程囄设计和处理上Q他需要做数据图以确立数据词典;他需要加工逻辑图以Ş成整体的pȝ处理程。一个流E有问题的系l,q代码多漂亮,每个模块多精_也不会成Z个好的系l。当Ӟ做好程分析q择好项目设计方法,都需要在需求分析能力上h_的把握?
W三Q复用设计和模块化分解能?/strong>
q个g又是老调重谈Q前面基本素质上不是已经说明了这个问题吗Q?
作ؓ一个从事模块Q务的E序员,他需要对他所面对的特定功能模块的复用性进行考虑Q而作Z个系l分析h员,他要面对的问题复杂的多,需要对整体pȝ按照一U模块化的分析能力分解ؓ很多可复用的功能模块和函敎ͼqҎ一模块形成一个独立的设计需求。D个例子,好比是汽车生产,最早每辆汽车都是独立安装的Q每个部仉是量w定做的Q但是后来不一样了Q机器化大生产了Q一个汽车厂开始通过水U来生汽RQ独立部件开始具有一定的复用性,在后来标准化成ؓ大趋势,不同型号Q品牌甚至不同厂商的汽R部g也可以进行方便的换装和升U,q时候,汽R生的效率达到最大化。Y件工E也是同L道理Q一个成熟的软g行业Q在一些相关项目和pȝ中,不同的部件是可以随意换装的,比如微Y的许多桌面YӞ在很多操作模块(如打开文gQ保存文件等{)都是复用的同一套功能模块,而这些接口又通过一些类库提供给了桌面应用程序开发者方便挂接,q就是复用化的模块设计明昄一个佐证?
一个大型的Q错l复杂的应用pȝ分解成一些相对独立的Q具有高度复用性的Qƈ能仅仅依靠几个参数完成数据联pȝ模块l合Q是作ؓ高E序员和pȝ分析员一Ҏ重要的工作,合适的目设计ҎQ清晰的程图,是实现这一目标的重要保证?
W四Q整体项目评估能?/strong>
作ؓpȝ设计人员Q必能够从全局出发Q对目又整体的清醒认识Q比如公司的资源配置是否合理和到位,比如工程q度安排是否能最大化体现效率又不至于无法按期完成。评估项目整体和各个模块的工作量Q评估项目所需的资源,评估目可能遇到的困难,都需要大量的l验U篏Q换a之,q是一U不断ȝ的篏计才能达到的境界。在西方一些Y件系l设计的带头人都是很q长的,比如4Q?0岁,甚至更老,他们在编码方面已l远q不如年Mh那样zȝQ但是就目评估而言Q他们几十年的经验积累就是最重要和宝늚财富。中国缺q么一代程序员Q主要还不是~那U年U的E序员,而是那种q纪的程序员基本上都是研I单位作出来的,都不是从专业的品化软g研发作出来的Q他们没有能U篏那种产品化研发的l验Q这也是没有办法的事情?
W五Q团队组l管理能?/strong>
完成一个项目工E,需要团队的齐心协力Q作为项目设计者或研发的主hQ就应当有能力最大化发挥团队的整体力量,技术管理由于其专业性质Q不大同于一般的Z理Q因里面设计了一些技术性的指标和因素?
首先是工作的量化Q没有量化就很难做到合适的l效考核Q而程序量化又不是单的代码行数可以计算的,因此要求技术管理h员需要能真正评估一个模块的复杂性和工作量?
其次是对团队协作模式的调_一般而言Q程序开发的协作通常分ؓ组q行Q小l有ȝ序员方式的,也有民主方式的,ҎE序员之间的能力水^差距Q以及根据项目研发的需求,选择合适的l队方式Qƈ能将责权和成员的工作d紧密l合Q这h能最大发挥组队的效率?
一个代码水q高的hQ未必能成ؓ一个合格的目研发ȝQ这斚w的能力欠~往往是容易被忽视的?
lg可以看到Q作Z个主研发的负责人,一个项目设计者,所需要具备的素质和能力ƈ不是E序代码~写的能力,当然一般情况下Q一个程序员通过不断的ȝ提高辑ֈ了这U素质的时候,他所h的代码编写能力也已经相当不简单了Q但是请注意q里面的因果关系Q一个高水^的项目设计者通常已经是代码编写相当优U的h了,但是q不是一个代码相当优U的程序员可以胜任项目设计的工作Q这里面存在的也不是智商和课本的问题Q还是在于一个程序员在积累经验,逐步提升的时候没有意识到应当思考哪斚w的东西,没有有意识的项目的l织和复用设计进行揣摩,没有l常性的文档习惯和ȝ习惯Q不改变q些Q我们的合格的项目设计者还是非常欠~?
另外Qؓ防止有无聊的人和我较真,补充一点,本文针对目标是作商业化的软g目和工E,那些U研机构的编E高手,比如法高手Q比如图象处理高手,他们的工作是研究N而非直接完成商业软gQ当然最l间接成为商业品,比如微Y研究院在作的研究NQ,因此他们的素质可能是另外的东西,q些人(专家Q,q不能说是程序员Q不能用E序员的标准去衡量?
最后补充一点东西,一个Y仉目研发的设计程是怎样的呢Q以通常标准的设计方法ؓ例,Q不q笔者喜Ƣ快速原型法Q?
W一个步骤是市场调研Q技术和市场要结合才能体现最大h倹{?
W二个步骤是需求分?/strong>Q这个阶D需要出三样东西Q用戯图,数据词典和用h作手册。用戯图是该Y件用P包括l端用户和管理用P所能看到的面样式Q这里面包含了很多操作方面的程和条件。数据词典是指明数据逻辑关系q加以整理的东东Q完成了数据词典Q数据库的设计就完成了一半多。用h作手册是指明了操作流E的说明书。请注意Q用h作流E和用户视图是由需求决定的Q因此应该在软g设计之前完成Q完成这些,׃ؓE序研发提供了约束和准Q很遗憾太多公司都不是这样做的,因果颠倒,序不分Q开发工作和实际需求往往因此产生隔阂p的现象?
需求分析,除了以上工作Q笔者以Z为项目设计者应当完整的做出目的性能需求说明书Q因为往往性能需求只有懂技术的人才可能理解Q这需要技术专家和需求方Q客h公司市场部门Q能够有真正的沟通和了解?
W三个步骤是概要设计Q将pȝ功能模块初步划分Qƈl出合理的研发流E和资源要求。作为快速原型设计方法,完成概要设计可以进入编码阶D了Q通常采用q种Ҏ是因为涉及的研发d属于新领域,技术主h员一上来无法l出明确的详l设计说明书Q但是ƈ不是说详l设计说明书不重要,事实上快速原型法在完成原型代码后Q根据评结果和l验教训的ȝQ还要重新进行详l设计的步骤?
W四个步骤是详细设计Q这是考验技术专家设计思维的重要关卡,详细设计说明书应当把具体的模块以最‘干净’的方式(黑箱l构Q提供给~码者,使得pȝ整体模块化达到最大;一份好的详l设计说明书Q可以ɾ~码的复杂性减低到最低,实际上,严格的讲详细设计说明书应当把每个函数的每个参数的定义都精_l的提供出来Q从需求分析到概要设计到完成详l设计说明书Q一个Y仉目就应当说完成了一半了。换a之,一个大型Y件系l在完成了一半的时候,其实q没有开始一行代码工作。那些把作Y件的E序员简单理解ؓ写代码的Q就从根子上犯了错误了?
W五个步骤是~码Q在规范化的研发程中,~码工作在整个项目流E里最多不会超q?/2Q通常?/3的时_所谓磨刀不误砍柴功,设计q程完成的好Q编码效率就会极大提高,~码时不同模块之间的q度协调和协作是最需要小心的Q也怸个小模块的问题就可能影响了整体进度,让很多程序员因此被迫停下工作{待Q这U问题在很多研发q程中都出现q。编码时的相互沟通和应急的解决手段都是相当重要的,对于E序员而言Qbug永远存在Q你必须永远面对q个问题Q大名鼎鼎的微YQ可曾有q箋三个月不发补丁的时候吗Q从来没有!
W六个步骤是试Q测试有很多U:按照试执行方,可以分ؓ内部试和外部测试;按照试范围Q可以分为模块测试和整体联调Q按照测试条Ӟ可以分ؓ正常操作情况试和异常情冉|试;按照试的输入范_可以分ؓ全覆盖测试和抽样试。以上都很好理解Q不再解释?
MQ测试同h目研发中一个相当重要的步骤Q对于一个大型YӞ3个月?q的外部试都是正常的,因ؓ永远都会又不可预料的问题存在?
完成试后,完成验收q完成最后的一些帮助文档,整体目才算告一D落Q当然日后少不了升Q修补等{工作,只要不是想通过一锤子买卖骗钱Q就要不停的跟踪软g的运营状况ƈ持箋修补升Q知道这个Y件被d淘汰为止?
写这些步骤算不上卖弄什么,因ؓ实话讲我手边是一本《Y件工E》,在大学里q是计算Z业的必修评Q但是我知道很多E序员似乎从来都只是热衷于什么?0天精通VC》之cȝQ他们有些和我一h击队nQ没有正规学q这个专业,q有一些则早就在够学分后把q些真正有用的东西还l了老师?
|上现在也很躁Q一些coding fans乱嚷Ph视听Q实际上真正的技术专家很在|上乱发帖子的,如笔者这样不知天高地厚的Q其实实在是不上什么高手,只不q看不惯q种Ҏ术,对程序员的误解和胡说Q只好挺w而出Q做拨ؕ反正之言Q也希望那些q沉q于一些错误h士的coding fans们能认真xQ走到正途上Q毕竟那些聪明的头脑q远q没有发挥应有的价倹{?/font>