亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

一堂如何提高代碼質(zhì)量的培訓(xùn)課

系統(tǒng) 1774 0

今天這堂培訓(xùn)課講什么呢?我既不講 Spring ,也不講 Hibernate ,更不講 Ext ,我不講任何一個(gè)具體的技術(shù)。我們拋開(kāi)任何具體的技術(shù),來(lái)談?wù)勅绾翁岣叽a質(zhì)量。如何提高代碼質(zhì)量,相信不僅是在座所有人苦惱的事情,也是所有軟件項(xiàng)目苦惱的事情。如何提高代碼質(zhì)量呢,我認(rèn)為我們首先要理解什么是高質(zhì)量的代碼。

高質(zhì)量代碼的三要素

我們?cè)u(píng)價(jià)高質(zhì)量代碼有三要素:可讀性、可維護(hù)性、可變更性。我們的代碼要一個(gè)都不能少地達(dá)到了這三要素的要求才能算高質(zhì)量的代碼。

1. 可讀性強(qiáng)

一提到可讀性似乎有一些老生常談的味道,但令人沮喪的是,雖然大家一而再,再而三地強(qiáng)調(diào)可讀性,但我們的代碼在可讀性方面依然做得非常糟糕。由于工作的需要,我常常需要去閱讀他人的代碼,維護(hù)他人設(shè)計(jì)的模塊。每當(dāng)我看到大段大段、密密麻麻的代碼,而且還沒(méi)有任何的注釋時(shí)常常感慨不已,深深體會(huì)到了這項(xiàng)工作的重要。由于分工的需要,我們寫(xiě)的代碼難免需要?jiǎng)e人去閱讀和維護(hù)的。而對(duì)于許多程序員來(lái)說(shuō),他們很少去閱讀和維護(hù)別人的代碼。正因?yàn)槿绱耍麄兒苌訇P(guān)注代碼的可讀性,也對(duì)如何提高代碼的可讀性缺乏切身體會(huì)。有時(shí)即使為代碼編寫(xiě)了注釋,也常常是注釋語(yǔ)言晦澀難懂形同天書(shū),令閱讀者反復(fù)斟酌依然不明其意。針對(duì)以上問(wèn)題,我給大家以下建議:

1)不要編寫(xiě)大段的代碼

如果你有閱讀他人代碼的經(jīng)驗(yàn),當(dāng)你看到別人寫(xiě)的大段大段的代碼,而且還不怎么帶注釋,你是怎樣的感覺(jué),是不是“嗡”地一聲頭大。各種各樣的功能糾纏在一個(gè)方法中,各種變量來(lái)回調(diào)用,相信任何人多不會(huì)認(rèn)為它是高質(zhì)量的代碼,但卻頻繁地出現(xiàn)在我們編寫(xiě)的程序了。如果現(xiàn)在你再回顧自己寫(xiě)過(guò)的代碼,你會(huì)發(fā)現(xiàn),稍微編寫(xiě)一個(gè)復(fù)雜的功能,幾百行的代碼就出去了。一些比較好的辦法就是分段。將大段的代碼經(jīng)過(guò)整理,分為功能相對(duì)獨(dú)立的一段又一段,并且在每段的前端編寫(xiě)一段注釋。這樣的編寫(xiě),比前面那些雜亂無(wú)章的大段代碼確實(shí)進(jìn)步了不少,但它們?cè)诠δ塥?dú)立性、可復(fù)用性、可維護(hù)性方面依然不盡人意。從另一個(gè)比較專業(yè)的評(píng)價(jià)標(biāo)準(zhǔn)來(lái)說(shuō),它沒(méi)有實(shí)現(xiàn)低耦合、高內(nèi)聚。我給大家的建議是,將這些相對(duì)獨(dú)立的段落另外封裝成一個(gè)又一個(gè)的函數(shù)。

許多大師在自己的經(jīng)典書(shū)籍中,都鼓勵(lì)我們?cè)诰帉?xiě)代碼的過(guò)程中應(yīng)當(dāng)養(yǎng)成不斷重構(gòu)的習(xí)慣。我們?cè)诰帉?xiě)代碼的過(guò)程中常常要編寫(xiě)一些復(fù)雜的功能,起初是寫(xiě)在一個(gè)類的一個(gè)函數(shù)中。隨著功能的逐漸展開(kāi),我們開(kāi)始對(duì)復(fù)雜功能進(jìn)行歸納整理,整理出了一個(gè)又一個(gè)的獨(dú)立功能。這些獨(dú)立功能有它與其它功能相互交流的輸入輸出數(shù)據(jù)。當(dāng)我們分析到此處時(shí),我們會(huì)非常自然地要將這些功能從原函數(shù)中分離出來(lái),形成一個(gè)又一個(gè)獨(dú)立的函數(shù),供原函數(shù)調(diào)用。在編寫(xiě)這些函數(shù)時(shí),我們應(yīng)當(dāng)仔細(xì)思考一下,為它們?nèi)∫粋€(gè)釋義名稱,并為它們編寫(xiě)注釋(后面還將詳細(xì)討論這個(gè)問(wèn)題)。另一個(gè)需要思考的問(wèn)題是,這些函數(shù)應(yīng)當(dāng)放到什么地方。這些函數(shù)可能放在原類中,也可能放到其它相應(yīng)職責(zé)的類中,其遵循的原則應(yīng)當(dāng)是“職責(zé)驅(qū)動(dòng)設(shè)計(jì)”(后面也將詳細(xì)描述)。

下面是我編寫(xiě)的一個(gè)從 XML 文件中讀取數(shù)據(jù),將其生成工廠的一個(gè)類。這個(gè)類最主要的一段程序就是初始化工廠,該功能歸納起來(lái)就是三部分功能:用各種方式嘗試讀取文件、以 DOM 的方式解析 XML 數(shù)據(jù)流、生成工廠。而這些功能被我歸納整理后封裝在一個(gè)不同的函數(shù)中,并且為其取了釋義名稱和編寫(xiě)了注釋:

?

    	/**
	 * 初始化工廠。根據(jù)路徑讀取XML文件,將XML文件中的數(shù)據(jù)裝載到工廠中
	 * @param path XML的路徑
	 */
	public void initFactory(String path){
		if(findOnlyOneFileByClassPath(path)){return;}
		if(findResourcesByUrl(path)){return;}
		if(findResourcesByFile(path)){return;}
		this.paths = new String[]{path};
	}
	
	/**
	 * 初始化工廠。根據(jù)路徑列表依次讀取XML文件,將XML文件中的數(shù)據(jù)裝載到工廠中
	 * @param paths 路徑列表
	 */
	public void initFactory(String[] paths){
		for(int i=0; i<paths.length; i++){
			initFactory(paths[i]);
		}
		this.paths = paths;
	}
	
	/**
	 * 重新初始化工廠,初始化所需的參數(shù),為上一次初始化工廠所用的參數(shù)。
	 */
	public void reloadFactory(){
		initFactory(this.paths);
	}
	
	/**
	 * 采用ClassLoader的方式試圖查找一個(gè)文件,并調(diào)用<code>readXmlStream()</code>進(jìn)行解析
	 * @param path XML文件的路徑
	 * @return 是否成功
	 */
	protected boolean findOnlyOneFileByClassPath(String path){
		boolean success = false;
		try {
			Resource resource = new ClassPathResource(path, this.getClass());
			resource.setFilter(this.getFilter());
			InputStream is = resource.getInputStream();
			if(is==null){return false;}
			readXmlStream(is);
			success = true;
		} catch (SAXException e) {
			log.debug("Error when findOnlyOneFileByClassPath:"+path,e);
		} catch (IOException e) {
			log.debug("Error when findOnlyOneFileByClassPath:"+path,e);
		} catch (ParserConfigurationException e) {
			log.debug("Error when findOnlyOneFileByClassPath:"+path,e);
		}
		return success;
	}
	
	/**
	 * 采用URL的方式試圖查找一個(gè)目錄中的所有XML文件,并調(diào)用<code>readXmlStream()</code>進(jìn)行解析
	 * @param path XML文件的路徑
	 * @return 是否成功
	 */
	protected boolean findResourcesByUrl(String path){
		boolean success = false;
		try {
			ResourcePath resourcePath = new PathMatchResource(path, this.getClass());
			resourcePath.setFilter(this.getFilter());
			Resource[] loaders = resourcePath.getResources();
			for(int i=0; i<loaders.length; i++){
				InputStream is = loaders[i].getInputStream();
				if(is!=null){
					readXmlStream(is);
					success = true;
				}
			}
		} catch (SAXException e) {
			log.debug("Error when findResourcesByUrl:"+path,e);
		} catch (IOException e) {
			log.debug("Error when findResourcesByUrl:"+path,e);
		} catch (ParserConfigurationException e) {
			log.debug("Error when findResourcesByUrl:"+path,e);
		}
		return success;
	}
	
	/**
	 * 用File的方式試圖查找文件,并調(diào)用<code>readXmlStream()</code>解析
	 * @param path XML文件的路徑
	 * @return 是否成功
	 */
	protected boolean findResourcesByFile(String path){
		boolean success = false;
		FileResource loader = new FileResource(new File(path));
		loader.setFilter(this.getFilter());
		try {
			Resource[] loaders = loader.getResources();
			if(loaders==null){return false;}
			for(int i=0; i<loaders.length; i++){
				InputStream is = loaders[i].getInputStream();
				if(is!=null){
					readXmlStream(is);
					success = true;
				}
			}
		} catch (IOException e) {
			log.debug("Error when findResourcesByFile:"+path,e);
		} catch (SAXException e) {
			log.debug("Error when findResourcesByFile:"+path,e);
		} catch (ParserConfigurationException e) {
			log.debug("Error when findResourcesByFile:"+path,e);
		}
		return success;
	}

	/**
	 * 讀取并解析一個(gè)XML的文件輸入流,以Element的形式獲取XML的根,
	 * 然后調(diào)用<code>buildFactory(Element)</code>構(gòu)建工廠
	 * @param inputStream 文件輸入流
	 * @throws SAXException
	 * @throws IOException
	 * @throws ParserConfigurationException
	 */
	protected void readXmlStream(InputStream inputStream) throws SAXException, IOException, ParserConfigurationException{
		if(inputStream==null){
			throw new ParserConfigurationException("Cann't parse source because of InputStream is null!");
		}
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setValidating(this.isValidating());
        factory.setNamespaceAware(this.isNamespaceAware());
        DocumentBuilder build = factory.newDocumentBuilder();
        Document doc = build.parse(new InputSource(inputStream));
        Element root = doc.getDocumentElement();
        buildFactory(root);
	}
	
	/**
	 * 用從一個(gè)XML的文件中讀取的數(shù)據(jù)構(gòu)建工廠
	 * @param root 從一個(gè)XML的文件中讀取的數(shù)據(jù)的根
	 */
	protected abstract void buildFactory(Element root);
	

  

?

完整代碼在附件中。在編寫(xiě)代碼的過(guò)程中,通常有兩種不同的方式。一種是從下往上編寫(xiě),也就是按照順序,每分出去一個(gè)函數(shù),都要將這個(gè)函數(shù)編寫(xiě)完,才回到主程序,繼續(xù)往下編寫(xiě)。而一些更有經(jīng)驗(yàn)的程序員會(huì)采用另外一種從上往下的編寫(xiě)方式。當(dāng)他們?cè)诰帉?xiě)程序的時(shí)候,每個(gè)被分出去的程序,可以暫時(shí)只寫(xiě)一個(gè)空程序而不去具體實(shí)現(xiàn)功能。當(dāng)主程序完成以后,再一個(gè)個(gè)實(shí)現(xiàn)它的所有子程序。采用這樣的編寫(xiě)方式,可以使復(fù)雜程序有更好的規(guī)劃,避免只見(jiàn)樹(shù)木不見(jiàn)森林的弊病。

有多少代碼就算大段代碼,每個(gè)人有自己的理解。我編寫(xiě)代碼,每當(dāng)達(dá)到 15~20 行的時(shí)候,我就開(kāi)始考慮是否需要重構(gòu)代碼。同理,一個(gè)類也不應(yīng)當(dāng)有太多的函數(shù),當(dāng)函數(shù)達(dá)到一定程度的時(shí)候就應(yīng)該考慮分為多個(gè)類了;一個(gè)包也不應(yīng)當(dāng)有太多的類······

2)釋義名稱與注釋

我們?cè)诿兞俊⒑瘮?shù)、屬性、類以及包的時(shí)候,應(yīng)當(dāng)仔細(xì)想想,使名稱更加符合相應(yīng)的功能。我們常常在說(shuō),設(shè)計(jì)一個(gè)系統(tǒng)時(shí)應(yīng)當(dāng)有一個(gè)或多個(gè)系統(tǒng)分析師對(duì)整個(gè)系統(tǒng)的包、類以及相關(guān)的函數(shù)和屬性進(jìn)行規(guī)劃,但在通常的項(xiàng)目中這都非常難于做到。對(duì)它們的命名更多的還是程序員來(lái)完成。但是,在一個(gè)項(xiàng)目開(kāi)始的時(shí)候,應(yīng)當(dāng)對(duì)項(xiàng)目的命名出臺(tái)一個(gè)規(guī)范。譬如,在我的項(xiàng)目中規(guī)定,新增記錄用 new add 開(kāi)頭,更新記錄用 edit mod 開(kāi)頭,刪除用 del 開(kāi)頭,查詢用 find query 開(kāi)頭。使用最亂的就是 get ,因此我規(guī)定, get 開(kāi)頭的函數(shù)僅僅用于獲取類屬性。

注釋是每個(gè)項(xiàng)目組都在不斷強(qiáng)調(diào)的,可是依然有許多的代碼沒(méi)有任何的注釋。為什么呢?因?yàn)槊總€(gè)項(xiàng)目在開(kāi)發(fā)過(guò)程中往往時(shí)間都是非常緊的。在緊張的代碼開(kāi)發(fā)過(guò)程中,注釋往往就漸漸地被忽略了。利用開(kāi)發(fā)工具的代碼編寫(xiě)模板也許可以解決這個(gè)問(wèn)題。

用我們常用的 MyEclipse 為例,在菜單“ window>>Preferences>>Java>>Code Style>>Code Templates>>Comments ”中,可以簡(jiǎn)單的修改一下。

?


一堂如何提高代碼質(zhì)量的培訓(xùn)課
?

?

?

Files ”代表的是我們每新建一個(gè)文件(可能是類也可能是接口)時(shí)編寫(xiě)的注釋,我通常設(shè)定為:

    /*
 * created on ${date}
 */

  

??

Types ”代表的是我們新建的接口或類前的注釋,我通常設(shè)定為:

?

    /**
 * 
 * @author ${user}
 */

  

??

?

第一行為一個(gè)空行,是用于你寫(xiě)該類的注釋。如果你采用“職責(zé)驅(qū)動(dòng)設(shè)計(jì)”,這里首先應(yīng)當(dāng)描述的是該類的職責(zé)。如果需要,你可以寫(xiě)該類一些重要的方法及其用法、該類的屬性及其中文含義等。

${user} 代表的是你在 windows 中登陸的用戶名。如果這個(gè)用戶名不是你的名稱,你可以直接寫(xiě)死為你自己的名稱。

其它我通常都保持為默認(rèn)值。通過(guò)以上設(shè)定,你在創(chuàng)建類或接口的時(shí)候,系統(tǒng)將自動(dòng)為你編寫(xiě)好注釋,然后你可以在這個(gè)基礎(chǔ)上進(jìn)行修改,大大提高注釋編寫(xiě)的效率。

同時(shí),如果你在代碼中新增了一個(gè)函數(shù)時(shí),通過(guò) Alt+Shift+J 快捷鍵,可以按照模板快速添加注釋。

在編寫(xiě)代碼時(shí)如果你編寫(xiě)的是一個(gè)接口或抽象類,我還建議你在 @author 后面增加 @see 注釋,將該接口或抽象類的所有實(shí)現(xiàn)類列出來(lái),因?yàn)殚喿x者在閱讀的時(shí)候,尋找接口或抽象類的實(shí)現(xiàn)類比較困難。

?

    /**
 * 抽象的單表數(shù)組查詢實(shí)現(xiàn)類,僅用于單表查詢
 * @author 范鋼
 * @see com.htxx.support.query.DefaultArrayQuery
 * @see com.htxx.support.query.DwrQuery
 */
public abstract class ArrayQuery implements ISingleQuery {
...

  

?

2. 可維護(hù)性

軟件的可維護(hù)性有幾層意思,首先的意思就是能夠適應(yīng)軟件在部署和使用中的各種情況。從這個(gè)角度上來(lái)說(shuō),它對(duì)我們的軟件提出的要求就是不能將代碼寫(xiě)死。

1)代碼不能寫(xiě)死

我曾經(jīng)見(jiàn)我的同事將系統(tǒng)要讀取的一個(gè)日志文件指定在 C 盤的一個(gè)固定目錄下,如果系統(tǒng)部署時(shí)沒(méi)有這個(gè)目錄以及這個(gè)文件就會(huì)出錯(cuò)。如果他將這個(gè)決定路徑下的目錄改為相對(duì)路徑,或者通過(guò)一個(gè)屬性文件可以修改,代碼豈不就寫(xiě)活了。一般來(lái)說(shuō),我在設(shè)計(jì)中需要使用日志文件、屬性文件、配置文件,通常都是以下幾個(gè)方式:將文件放到與類相同的目錄,使用 ClassLoader.getResource() 來(lái)讀取;將文件放到 classpath 目錄下,用 File 的相對(duì)路徑來(lái)讀取;使用 web.xml 或另一個(gè)屬性文件來(lái)制定讀取路徑。

我也曾見(jiàn)另一家公司的軟件要求,在部署的時(shí)候必須在 C:/bea 目錄下,如果換成其它目錄則不能正常運(yùn)行。這樣的設(shè)定常常為軟件部署時(shí)帶來(lái)許多的麻煩。如果服務(wù)器在該目錄下已經(jīng)沒(méi)有多余空間,或者已經(jīng)有其它軟件,將是很撓頭的事情。

2)預(yù)測(cè)可能發(fā)生的變化

除此之外,在設(shè)計(jì)的時(shí)候,如果將一些關(guān)鍵參數(shù)放到配置文件中,可以為軟件部署和使用帶來(lái)更多的靈活性。要做到這一點(diǎn),要求我們?cè)谲浖O(shè)計(jì)時(shí),應(yīng)當(dāng)更多地有更多的意識(shí),考慮到軟件應(yīng)用中可能發(fā)生的變化。比如,有一次我在設(shè)計(jì)財(cái)務(wù)軟件的時(shí)候,考慮到一些單據(jù)在制作時(shí)的前置條件,在不同企業(yè)使用的時(shí)候,可能要求不一樣,有些企業(yè)可能要求嚴(yán)格些而有些要求松散些。考慮到這種可能的變化,我將前置條件設(shè)計(jì)為可配置的,就可能方便部署人員在實(shí)際部署中進(jìn)行靈活變化。然而這樣的配置,必要的注釋說(shuō)明是非常必要的。

軟件的可維護(hù)性的另一層意思就是軟件的設(shè)計(jì)便于日后的變更。這一層意思與軟件的可變更性是重合的。所有的軟件設(shè)計(jì)理論的發(fā)展,都是從軟件的可變更性這一要求逐漸展開(kāi)的,它成為了軟件設(shè)計(jì)理論的核心。

?

? (繼。。。)

一堂如何提高代碼質(zhì)量的培訓(xùn)課


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 国产亚洲新品一区二区 | 爱爱免费 | 黄色录像欧美 | 日本人成18在线播放 | 免费爱爱的视频太爽了 | 特级全黄一级毛片视频 | 四虎精品影院在线观看视频 | 精品乱人伦一区二区三区 | 老司机毛片 | 精品69久久久久久99 | 看欧美的一级毛片 | 国产精品第一 | 欧美精品亚洲精品 | 成年人黄色小视频 | 深夜影院在线观看 | 亚洲精品色综合久久 | 深夜福利视频网址 | 亚洲国产天堂 | 在线视频 中文字幕 | 99久久精品在免费线18 | 91在线视频免费看 | 日本午夜www高清视频 | 站长推荐国产午夜免费视频 | aaaaaa国产毛片孕妇版 | 天天干天天操天天摸 | 久久免费小视频 | 草草影院第一页 | 99视频在线观看视频一区 | 福利观看 | 在线成人毛片 | 国产精品一区高清在线观看 | 香蕉国产人午夜视频在线观看 | 久久久视频在线 | 毛片久久 | 久久综合桃花网 | 亚洲香蕉中文网 | 夜福利视频 | 午夜在线不卡 | 国产成人h片视频在线观看 国产成人h综合亚洲欧美在线 | 亚洲成人欧美 | 97av在线 |