代碼復用6
① 高內聚、低耦合的含義是什麼如何提高代碼的可重用性
網路粘過來的,你看看:
基本解釋
高內聚低耦合,是軟體工程中的概念,是判斷設計好壞的標准,主要是面向對象的設計,主要是看類的內聚性是否高,耦合度是否低。
高內聚
內聚就是一個模塊內各個元素彼此結合的緊密程度,高內聚就是一個模塊內各個元素彼此結合的緊密程度高。 所謂高內聚是指一個軟體模塊是由相關性很強的代碼組成,只負責一項任務,也就是常說的單一責任原則。
低耦合
耦合:一個軟體結構內不同模塊之間互連程度的度量(耦合性也叫塊間聯系。指軟體系統結構中各模塊間相互聯系緊密程度的一種度量。模塊之間聯系越緊密,其耦合性就越強,模塊的獨立性則越差,模塊間耦合的高低取決於模塊間介面的復雜性,調用的方式以及傳遞的信息。) 對於低耦合,粗淺的理解是: 一個完整的系統,模塊與模塊之間,盡可能的使其獨立存在。 也就是說,讓每個模塊,盡可能的獨立完成某個特定的子功能。 模塊與模塊之間的介面,盡量的少而簡單。 如果某兩個模塊間的關系比較復雜的話,最好首先考慮進一步的模塊劃分。 這樣有利於修改和組合。[1]
編輯本段為什麼要追求高內聚和低耦合
軟體架構設計的目的簡單說就是在保持軟體內在聯系的前提下,分解軟體系統,降低軟體系統開發的復雜性,而分解軟體系統的基本方法無外乎分層和分割。但是在保持軟體內在聯系的前提下,如何分層分割系統,分層分割到什麼樣的粒度,並不是一件容易的事,這方面有各種各樣的分解方法,比如:關注點分離,面向方面,面向對象,面向介面,面向服務,依賴注入,以及各種各樣的設計原則等,而所有這些方法都基於高內聚,低耦合的原則。 高內聚和低耦合是相互矛盾的,分解粒度越粗的系統耦合性越低,分解粒度越細的系統內聚性越高,過度低耦合的軟體系統,軟體模塊內部不可能高內聚,而過度高內聚的軟體模塊之間必然是高度依賴的,因此如何兼顧高內聚和低耦合是軟體架構師功力的體現。 高內聚,低耦合的系統有什麼好處呢?事實上,短期來看,並沒有很明顯的好處,甚至短期內會影響系統的開發進度,因為高內聚,低耦合的系統對開發設計人員提出了更高的要求。高內聚,低耦合的好處體現在系統持續發展的過程中,高內聚,低耦合的系統具有更好的重用性,維護性,擴展性,可以更高效的完成系統的維護開發,持續的支持業務的發展,而不會成為業務發展的障礙。[2]
② 面試中被問到了java中的「六原則一法則」是什麼
原則包括:
單一職責原則:一個類只做它該做的事情。(單一職責原則想表達的就是」高內聚」,寫代碼最終極的原則只有六個字」高內聚、低耦合」,就如同葵花寶典或辟邪劍譜的中心思想就八個字」欲練此功必先自宮」,所謂的高內聚就是一個代碼模塊只完成一項功能,在面向對象中,如果只讓一個類完成它該做的事,而不涉及與它無關的領域就是踐行了高內聚的原則,這個類就只有單一職責。提醒大家有一句話叫」因為專注,所以專業」,一個對象如果承擔太多的職責,那麼註定它什麼都做不好。這個世界上任何好的東西都有兩個特徵,一個是功能單一,好的相機絕對不是電視購物裡面賣的那種一個機器有一百多種功能的,它基本上只能照相;另一個是模塊化,好的自行車是組裝車,從減震叉、剎車到變速器,所有的部件都是可以拆卸和重新組裝的,好的乒乓球拍也不是成品拍,一定是底板和膠皮可以拆分和自行組裝的,一個好的軟體系統,它裡面的每個功能模塊也應該是可以輕易的拿到其他系統中使用的,這樣才能實現軟體復用的目標。)
開閉原則:軟體實體應當對擴展開放,對修改關閉。(在理想的狀態下,當我們需要為一個軟體系統增加新功能時,只需要從原來的系統派生出一些新類就可以,不需要修改原來的任何一行代碼。要做到開閉有兩個要點:①抽象是關鍵,一個系統中如果沒有抽象類或介面系統就沒有擴展點;②封裝可變性,將系統中的各種可變因素封裝到一個繼承結構中,如果多個可變因素混雜在一起,系統將變得復雜而混亂,如果不清楚如何封裝可變性,可以參考《設計模式精解》一書中對橋梁模式的講解的章節。)
依賴倒轉原則:面向介面編程。(該原則說得直白和具體一些就是聲明方法的參數類型、方法的返回類型、變數的引用類型時,盡可能使用抽象類型而不用具體類型,因為抽象類型可以被它的任何一個子類型所替代,請參考下面的里氏替換原則。)
里氏替換原則:任何時候都可以用子類型替換掉父類型。(關於里氏替換原則的描述,Barbara Liskov女士的描述比這個要復雜得多,但簡單的說就是能用父類型的地方就一定能使用子類型。里氏替換原則可以檢查繼承關系是否合理,如果一個繼承關系違背了里氏替換原則,那麼這個繼承關系一定是錯誤的,需要對代碼進行重構。例如讓貓繼承狗,或者狗繼承貓,又或者讓正方形繼承長方形都是錯誤的繼承關系,因為你很容易找到違反里氏替換原則的場景。需要注意的是:子類一定是增加父類的能力而不是減少父類的能力,因為子類比父類的能力更多,把能力多的對象當成能力少的對象來用當然沒有任何問題。
介面隔離原則:介面要小而專,絕不能大而全。(臃腫的介面是對介面的污染,既然介面表示能力,那麼一個介面只應該描述一種能力,介面也應該是高度內聚的。例如,琴棋書畫就應該分別設計為四個介面,而不應設計成一個介面中的四個方法,因為如果設計成一個介面中的四個方法,那麼這個介面很難用,畢竟琴棋書畫四樣都精通的人還是少數,而如果設計成四個介面,會幾項就實現幾個介面,這樣的話每個介面被復用的可能性是很高的。Java中的介面代表能力、代表約定、代表角色,能否正確的使用介面一定是編程水平高低的重要標識。)
合成聚合復用原則:
優先使用聚合或合成關系復用代碼。(通過繼承來復用代碼是面向對象程序設計中被濫用得最多的東西,因為所有的教科書都無一例外的對繼承進行了鼓吹從而誤導了初學者,類與類之間簡單的說有三種關系,Is-A關系、Has-A關系、Use-A關系,分別代表繼承、關聯和依賴。其中,關聯關系根據其關聯的強度又可以進一步劃分為關聯、聚合和合成,但說白了都是Has-A關系,合成聚合復用原則想表達的是優先考慮Has-A關系而不是Is-A關系復用代碼,原因嘛可以自己從網路上找到一萬個理由,浙江優就業需要說明的是,即使在Java的API中也有不少濫用繼承的例子,例如Properties類繼承了Hashtable類,Stack類繼承了Vector類,這些繼承明顯就是錯誤的,更好的做法是在Properties類中放置一個Hashtable類型的成員並且將其鍵和值都設置為字元串來存儲數據,而Stack類的設計也應該是在Stack類中放一個Vector對象來存儲數據。記住:任何時候都不要繼承工具類,工具是可以擁有並可以使用的,而不是拿來繼承的。)
法則指迪米特法則:迪米特法則又叫最少知識原則,一個對象應當對其他對象有盡可能少的了解。(迪米特法則簡單的說就是如何做到」低耦合」,門面模式和調停者模式就是對迪米特法則的踐行。對於門面模式可以舉一個簡單的例子,你去一家公司洽談業務,你不需要了解這個公司內部是如何運作的,你甚至可以對這個公司一無所知,去的時候只需要找到公司入口處的前台美女,告訴她們你要做什麼,她們會找到合適的人跟你接洽,前台的美女就是公司這個系統的門面。再復雜的系統都可以為用戶提供一個簡單的門面,Java Web開發中作為前端控制器的Servlet或Filter不就是一個門面嗎,瀏覽器對伺服器的運作方式一無所知,但是通過前端控制器就能夠根據你的請求得到相應的服務。調停者模式也可以舉一個簡單的例子來說明,例如一台計算機,CPU、內存、硬碟、顯卡、音效卡各種設備需要相互配合才能很好的工作,但是如果這些東西都直接連接到一起,計算機的布線將異常復雜,在這種情況下,主板作為一個調停者的身份出現,它將各個設備連接在一起而不需要每個設備之間直接交換數據,這樣就減小了系統的耦合度和復雜度)
這個問題在Java中屬於比較基礎的問題,但是也請題主不要灰心。面試是一個考量綜合素質的過程,即使一兩道題發揮不好也仍然擁有機會。
③ java怎麼實現代碼的可重用性
可重用性有很多方面
對象的重用
方法的重用
變數的重用
對象和變數的可重用性很好理解吧
對象就是類的可重用性的體現 把同一類型的對象抽象化 創建類
變數的重用在於 同一作用域 一次定義 到處使用
方法的可重用性 在於 把同一邏輯抽象出來作為方法 在作用域內反復使用
比如 做加法 3 +4 5+ 6 7+8 就是同一邏輯
抽象出方法 public static int add(int a ,int b){
return a+b;
}
現在只需要 int c = add(3,4) add(5,6)
次數少 邏輯簡單看不出來什麼 當邏輯復雜時 代碼的可重用性 對於 代碼的易讀性是很好的提升
④ 1234567890可以組成哪些6位數,可重復使用
若可重復使用,可組成的6位數太多了,有
9×10×10×10×10×10=900000(個)
⑤ 6.mybatis裡面的動態sql是怎麼設定的,常用標簽有那些以及其
1、動態SQL片段
通過SQL片段達到代碼復用
<!-- 動態條件分頁查詢 -->
<sql id="sql_count">
select count(*)
</sql>
<sql id="sql_select">
select *
</sql>
<sql id="sql_where">
from icp
<dynamic prepend="where">
<isNotEmpty prepend="and" property="name">
name like '%$name$%'
</isNotEmpty>
<isNotEmpty prepend="and" property="path">
path like '%path$%'
</isNotEmpty>
<isNotEmpty prepend="and" property="area_id">
area_id = #area_id#
</isNotEmpty>
<isNotEmpty prepend="and" property="hided">
hided = #hided#
</isNotEmpty>
</dynamic>
<dynamic prepend="">
<isNotNull property="_start">
<isNotNull property="_size">
limit #_start#, #_size#
</isNotNull>
</isNotNull>
</dynamic>
</sql>
<select id="findByParamsForCount" parameterClass="map" resultClass="int">
<include refid="sql_count"/>
<include refid="sql_where"/>
</select>
<select id="findByParams" parameterClass="map" resultMap="icp.result_base">
<include refid="sql_select"/>
<include refid="sql_where"/>
</select>
2、數字范圍查詢
所傳參數名稱是捏造所得,非資料庫欄位,比如_img_size_ge、_img_size_lt欄位
<isNotEmpty prepend="and" property="_img_size_ge">
<![CDATA[
img_size >= #_img_size_ge#
]]>
</isNotEmpty>
<isNotEmpty prepend="and" property="_img_size_lt">
<![CDATA[
img_size < #_img_size_lt#
]]>
</isNotEmpty>
多次使用一個參數也是允許的
<isNotEmpty prepend="and" property="_now">
<![CDATA[
execplantime >= #_now#
]]>
</isNotEmpty>
<isNotEmpty prepend="and" property="_now">
<![CDATA[
closeplantime <= #_now#
]]>
</isNotEmpty>
3、時間范圍查詢
<isNotEmpty prepend="" property="_starttime">
<isNotEmpty prepend="and" property="_endtime">
<![CDATA[
createtime >= #_starttime#
and createtime < #_endtime#
]]>
</isNotEmpty>
</isNotEmpty>
⑥ java 數組復用時 int A[]={2,6,7,9}; A = new int[2]; 後面的數組A是用的之前的空間還是另外的
另外的,只要是帶 new 的,都會重新 分配 空間。
⑦ 如何提高代碼質量6
高質量代碼的三要素 我們評價高質量代碼有三要素:可讀性、可維護性、可變更性。我們的代碼要一個都不能少地達到了這三要素的要求才能算高質量的代碼。1.可讀性強 一提到可讀性似乎有一些老生常談的味道,但令人沮喪的是,雖然大家一而再,再而三地強調可讀性,但我們的代碼在可讀性方面依然做得非常糟糕。由於工作的需要,我常常需要去閱讀他人的代碼,維護他人設計的模塊。每當我看到大段大段、密密麻麻的代碼,而且還沒有任何的注釋時常常感慨不已,深深體會到了這項工作的重要。由於分工的需要,我們寫的代碼難免需要別人去閱讀和維護的。而對於許多程序員來說,他們很少去閱讀和維護別人的代碼。正因為如此,他們很少關注代碼的可讀性,也對如何提高代碼的可讀性缺乏切身體會。有時即使為代碼編寫了注釋,也常常是注釋語言晦澀難懂形同天書,令閱讀者反復斟酌依然不明其意。針對以上問題,我給大家以下建議:1)不要編寫大段的代碼 如果你有閱讀他人代碼的經驗,當你看到別人寫的大段大段的代碼,而且還不怎麼帶注釋,你是怎樣的感覺,是不是「嗡」地一聲頭大。各種各樣的功能糾纏在一個方法中,各種變數來回調用,相信任何人多不會認為它是高質量的代碼,但卻頻繁地出現在我們編寫的程序了。如果現在你再回顧自己寫過的代碼,你會發現,稍微編寫一個復雜的功能,幾百行的代碼就出去了。一些比較好的辦法就是分段。將大段的代碼經過整理,分為功能相對獨立的一段又一段,並且在每段的前端編寫一段注釋。這樣的編寫,比前面那些雜亂無章的大段代碼確實進步了不少,但它們在功能獨立性、可復用性、可維護性方面依然不盡人意。從另一個比較專業的評價標准來說,它沒有實現低耦合、高內聚。我給大家的建議是,將這些相對獨立的段落另外封裝成一個又一個的函數。許多大師在自己的經典書籍中,都鼓勵我們在編寫代碼的過程中應當養成不斷重構的習慣。我們在編寫代碼的過程中常常要編寫一些復雜的功能,起初是寫在一個類的一個函數中。隨著功能的逐漸展開,我們開始對復雜功能進行歸納整理,整理出了一個又一個的獨立功能。這些獨立功能有它與其它功能相互交流的輸入輸出數據。當我們分析到此處時,我們會非常自然地要將這些功能從原函數中分離出來,形成一個又一個獨立的函數,供原函數調用。在編寫這些函數時,我們應當仔細思考一下,為它們取一個釋義名稱,並為它們編寫注釋(後面還將詳細討論這個問題)。另一個需要思考的問題是,這些函數應當放到什麼地方。這些函數可能放在原類中,也可能放到其它相應職責的類中,其遵循的原則應當是「職責驅動設計」(後面也將詳細描述)。下面是我編寫的一個從XML文件中讀取數據,將其生成工廠的一個類。這個類最主要的一段程序就是初始化工廠,該功能歸納起來就是三部分功能:用各種方式嘗試讀取文件、以DOM的方式解析XML數據流、生成工廠。而這些功能被我歸納整理後封裝在一個不同的函數中,並且為其取了釋義名稱和編寫了注釋:Java代碼 /** * 初始化工廠。根據路徑讀取XML文件,將XML文件中的數據裝載到工廠中 * @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}; } /** * 初始化工廠。根據路徑列表依次讀取XML文件,將XML文件中的數據裝載到工廠中 * @param paths 路徑列表 */ public void initFactory(String[] paths){ for(int i=0; i<paths.length; i++){ initFactory(paths[i]); } this.paths = paths; } /** * 重新初始化工廠,初始化所需的參數,為上一次初始化工廠所用的參數。 */ public void reloadFactory(){ initFactory(this.paths); } /** * 採用ClassLoader的方式試圖查找一個文件,並調用<code>readXmlStream()</code>進行解析 * @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的方式試圖查找一個目錄中的所有XML文件,並調用<code>readXmlStream()</code>進行解析 * @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的方式試圖查找文件,並調用<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; } /** * 讀取並解析一個XML的文件輸入流,以Element的形式獲取XML的根, * 然後調用<code>buildFactory(Element)</code>構建工廠 * @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); } /** * 用從一個XML的文件中讀取的數據構建工廠 * @param root 從一個XML的文件中讀取的數據的根 */ protected abstract void buildFactory(Element root); /** * 初始化工廠。根據路徑讀取XML文件,將XML文件中的數據裝載到工廠中 * @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}; } /** * 初始化工廠。根據路徑列表依次讀取XML文件,將XML文件中的數據裝載到工廠中 * @param paths 路徑列表 */ public void initFactory(String[] paths){ for(int i=0; i<paths.length; i++){ initFactory(paths[i]); } this.paths = paths; } /** * 重新初始化工廠,初始化所需的參數,為上一次初始化工廠所用的參數。 */ public void reloadFactory(){ initFactory(this.paths); } /** * 採用ClassLoader的方式試圖查找一個文件,並調用<code>readXmlStream()</code>進行解析 * @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的方式試圖查找一個目錄中的所有XML文件,並調用<code>readXmlStream()</code>進行解析 * @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的方式試圖查找文件,並調用<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; } /** * 讀取並解析一個XML的文件輸入流,以Element的形式獲取XML的根, * 然後調用<code>buildFactory(Element)</code>構建工廠 * @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); } /** * 用從一個XML的文件中讀取的數據構建工廠 * @param root 從一個XML的文件中讀取的數據的根 */ protected abstract void buildFactory(Element root); 完整代碼在附件中。在編寫代碼的過程中,通常有兩種不同的方式。一種是從下往上編寫,也就是按照順序,每分出去一個函數,都要將這個函數編寫完,才回到主程序,繼續往下編寫。而一些更有經驗的程序員會採用另外一種從上往下的編寫方式。當他們在編寫程序的時候,每個被分出去的程序,可以暫時只寫一個空程序而不去具體實現功能。當主程序完成以後,再一個個實現它的所有子程序。採用這樣的編寫方式,可以使復雜程序有更好的規劃,避免只見樹木不見森林的弊病。有多少代碼就算大段代碼,每個人有自己的理解。我編寫代碼,每當達到15~20行的時候,我就開始考慮是否需要重構代碼。同理,一個類也不應當有太多的函數,當函數達到一定程度的時候就應該考慮分為多個類了;一個包也不應當有太多的類。。。。。。2)釋義名稱與注釋 我們在命名變數、函數、屬性、類以及包的時候,應當仔細想想,使名稱更加符合相應的功能。我們常常在說,設計一個系統時應當有一個或多個系統分析師對整個系統的包、類以及相關的函數和屬性進行規劃,但在通常的項目中這都非常難於做到。對它們的命名更多的還是程序員來完成。但是,在一個項目開始的時候,應當對項目的命名出台一個規范。譬如,在我的項目中規定,新增記錄用new或add開頭,更新記錄用edit或mod開頭,刪除用del開頭,查詢用find或query開頭。使用最亂的就是get,因此我規定,get開頭的函數僅僅用於獲取類屬性。注釋是每個項目組都在不斷強調的,可是依然有許多的代碼沒有任何的注釋。為什麼呢?因為每個項目在開發過程中往往時間都是非常緊的。在緊張的代碼開發過程中,注釋往往就漸漸地被忽略了。利用開發工具的代碼編寫模板也許可以解決這個問題。用我們常用的MyEclipse為例,在菜單「window>>Preferences>>Java>>Code Style>>Code Templates>>Comments」中,可以簡單的修改一下。「Files」代表的是我們每新建一個文件(可能是類也可能是介面)時編寫的注釋,我通常設定為:Java代碼 /* * created on ${date} */ /* * created on ${date} */「Types」代表的是我們新建的介面或類前的注釋,我通常設定為:Java代碼 /** * * @author ${user} */ /** * * @author ${user} */第一行為一個空行,是用於你寫該類的注釋。如果你採用「職責驅動設計」,這里首先應當描述的是該類的職責。如果需要,你可以寫該類一些重要的方法及其用法、該類的屬性及其中文含義等。${user}代表的是你在windows中登陸的用戶名。如果這個用戶名不是你的名稱,你可以直接寫死為你自己的名稱。其它我通常都保持為默認值。通過以上設定,你在創建類或介面的時候,系統將自動為你編寫好注釋,然後你可以在這個基礎上進行修改,大大提高注釋編寫的效率。同時,如果你在代碼中新增了一個函數時,通過Alt+Shift+J快捷鍵,可以按照模板快速添加註釋。在編寫代碼時如果你編寫的是一個介面或抽象類,我還建議你在@author後面增加@see注釋,將該介面或抽象類的所有實現類列出來,因為閱讀者在閱讀的時候,尋找介面或抽象類的實現類比較困難。Java代碼 /** * 抽象的單表數組查詢實現類,僅用於單表查詢 * @author 范鋼 * @see com.htxx.support.query.DefaultArrayQuery * @see com.htxx.support.query.DwrQuery */ public abstract class ArrayQuery implements ISingleQuery { ... /** * 抽象的單表數組查詢實現類,僅用於單表查詢 * @author 范鋼 * @see com.htxx.support.query.DefaultArrayQuery * @see com.htxx.support.query.DwrQuery */public abstract class ArrayQuery implements ISingleQuery {...2.可維護性 軟體的可維護性有幾層意思,首先的意思就是能夠適應軟體在部署和使用中的各種情況。從這個角度上來說,它對我們的軟體提出的要求就是不能將代碼寫死。1)代碼不能寫死 我曾經見我的同事將系統要讀取的一個日誌文件指定在C盤的一個固定目錄下,如果系統部署時沒有這個目錄以及這個文件就會出錯。如果他將這個決定路徑下的目錄改為相對路徑,或者通過一個屬性文件可以修改,代碼豈不就寫活了。一般來說,我在設計中需要使用日誌文件、屬性文件、配置文件,通常都是以下幾個方式:將文件放到與類相同的目錄,使用ClassLoader.getResource()來讀取;將文件放到classpath目錄下,用File的相對路徑來讀取;使用web.xml或另一個屬性文件來制定讀取路徑。我也曾見另一家公司的軟體要求,在部署的時候必須在C:/bea目錄下,如果換成其它目錄則不能正常運行。這樣的設定常常為軟體部署時帶來許多的麻煩。如果伺服器在該目錄下已經沒有多餘空間,或者已經有其它軟體,將是很撓頭的事情。2)預測可能發生的變化 除此之外,在設計的時候,如果將一些關鍵參數放到配置文件中,可以為軟體部署和使用帶來更多的靈活性。要做到這一點,要求我們在軟體設計時,應當更多地有更多的意識,考慮到軟體應用中可能發生的變化。比如,有一次我在設計財務軟體的時候,考慮到一些單據在製作時的前置條件,在不同企業使用的時候,可能要求不一樣,有些企業可能要求嚴格些而有些要求鬆散些。考慮到這種可能的變化,我將前置條件設計為可配置的,就可能方便部署人員在實際部署中進行靈活變化。然而這樣的配置,必要的注釋說明是非常必要的。軟體的可維護性的另一層意思就是軟體的設計便於日後的變更。這一層意思與軟體的可變更性是重合的。所有的軟體設計理論的發展,都是從軟體的可變更性這一要求逐漸展開的,它成為了軟體設計理論的核心。3.可變更性 前面我提到了,軟體的變更性是所有軟體理論的核心,那麼什麼是軟體的可變更性呢?按照現在的軟體理論,客戶對軟體的需求時時刻刻在發生著變化。當軟體設計好以後,為應對客戶需求的變更而進行的代碼修改,其所需要付出的代價,就是軟體設計的可變更性。由於軟體合理地設計,修改所付出的代價越小,則軟體的可變更性越好,即代碼設計的質量越高。一種非常理想的狀態是,無論客戶需求怎樣變化,軟體只需進行適當地修改就能夠適應。但這之所以稱之為理想狀態,因為客戶需求變化是有大有小的。如果客戶需求變化非常大,即使再好的設計也無法應付,甚至重新開發。然而,客戶需求的適當變化,一個合理地設計可以使得變更代價最小化,延續我們設計的軟體的生命力。1)通過提高代碼復用提高可維護性 我曾經遇到過這樣一件事,我要維護的一個系統因為應用范圍的擴大,它對機關級次的計算方式需要改變一種策略。如果這個項目統一採用一段公用方法來計算機關級次,這樣一個修改實在太簡單了,就是修改這個公用方法即可。但是,事實卻不一樣,對機關級次計算的代碼遍布整個項目,甚至有些還寫入到了那些復雜的SQL語句中。在這樣一種情況下,這樣一個需求的修改無異於需要遍歷這個項目代碼。這樣一個實例顯示了一個項目代碼復用的重要,然而不幸的是,代碼無法很好復用的情況遍布我們所有的項目。代碼復用的道理十分簡單,但要具體運作起來非常復雜,它除了需要很好的代碼規劃,還需要持續地代碼重構。對整個系統的整體分析與合理規劃可以根本地保證代碼復用。系統分析師通過用例模型、領域模型、分析模型的一步一步分析,最後通過正向工程,生成系統需要設計的各種類及其各自的屬性和方法。採用這種方法,功能被合理地劃分到這個類中,可以很好地保證代碼復用。採用以上方法雖然好,但技術難度較高,需要有高深的系統分析師,並不是所有項目都能普遍採用的,特別是時間比較緊張的項目。通過開發人員在設計過程中的重構,也許更加實用。當某個開發人員在開發一段代碼時,發現該功能與前面已經開發功能相同,或者部分相同。這時,這個開發人員可以對前面已經開發的功能進行重構,將可以通用的代碼提取出來,進行相應地改造,使其具有一定的通用性,便於各個地方可以使用。一些比較成功的項目組會指定一個專門管理通用代碼的人,負責收集和整理項目組中各個成員編寫的,可以通用的代碼。這個負責人同時也應當具有一定的代碼編寫功力,因為將專用代碼提升為通用代碼,或者以前使用該通用代碼的某個功能,由於業務變更,而對這個通用代碼的變更要求,都對這個負責人提出了很高的能力要求。雖然後一種方式非常實用,但是它有些亡羊補牢的味道,不能從整體上對項目代碼進行有效規劃。正因為兩種方法各有利弊,因此在項目中應當配合使用。2)利用設計模式提高可變更性 對於初學者,軟體設計理論常常感覺晦澀難懂。一個快速提高軟體質量的捷徑就是利用設計模式。這里說的設計模式,不僅僅指經典的32個模式,是一切前人總結的,我們可以利用的、更加廣泛的設計模式。a. if...else...這個我也不知道叫什麼名字,最早是哪位大師總結的,它出現在Larman的《UML與模式應用》,也出現在出現在Mardin的《敏捷軟體開發》。它是這樣描述的:當你發現你必須要設計這樣的代碼:「if...elseif...elseif...else...」時,你應當想到你的代碼應當重構一下了。我們先看看這樣的代碼有怎樣的特點。Java代碼 if(var.equals("A")){ doA(); }else if(var.equals("B")){ doB(); }else if(var.equals("C")){ doC(); }else{ doD(); } if(var.equals("A")){ doA(); }else if(var.equals("B")){ doB(); }else if(var.equals("C")){ doC(); }else{ doD(); }這樣的代碼很常見,也非常平常,我們大家都寫過。但正是這樣平常才隱藏著我們永遠沒有注意的問題。問題就在於,如果某一天這個選項不再僅僅是A、B、C,而是增加了新的選項,會怎樣呢?你也許會說,那沒有關系,我把代碼改改就行。然而事實上並非如此,在大型軟體研發與維護中有一個原則,每次的變更盡量不要去修改原有的代碼。如果我們重構一下,能保證不修改原有代碼,僅僅增加新的代碼就能應付選項的增加,這就增加了這段代碼的可維護性和可變更性,提高了代碼質量。那麼,我們應當如何去做呢?經過深入分析你會發現,這里存在一個對應關系,即A對應doA(),B對應doB()...如果將doA()、doB()、doC()...與原有代碼解耦,問題就解決了。如何解耦呢?設計一個介面X以及它的實現A、B、C...每個類都包含一個方法doX(),並且將doA()的代碼放到A.doX()中,將doB()的代碼放到B.doX()中...經過以上的重構,代碼還是這些代碼,效果卻完全不一樣了。我們只需要這樣寫:Java代碼 X x = factory.getBean(var); x.doX(); X x = factory.getBean(var);x.doX();這樣就可以實現以上的功能了。我們看到這里有一個工廠,放著所有的A、B、C...並且與它們的key對應起來,並且寫在配置文件中。如果出現新的選項時,通過修改配置文件就可以無限制的增加下去。這個模式雖然有效提高了代碼質量,但是不能濫用,並非只要出現if...else...就需要使用。由於它使用了工廠,一定程度上增加了代碼復雜度,因此僅僅在選項較多,並且增加選項的可能性很大的情況下才可以使用。另外,要使用這個模式,繼承我在附件中提供的抽象類XmlBuildFactoryFacade就可以快速建立一個工廠。如果你的項目放在spring或其它可配置框架中,也可以快速建立工廠。設計一個Map靜態屬性並使其V為這些A、B、C...這個工廠就建立起來了。b.策略模式 也許你看過策略模式(strategy model)的相關資料但沒有留下太多的印象。一個簡單的例子可以讓你快速理解它。如果一個員工系統中,員工被分為臨時工和正式工並且在不同的地方相應的行為不一樣。在設計它們的時候,你肯定設計一個抽象的員工類,並且設計兩個繼承類:臨時工和正式工。這樣,通過下塑類型,可以在不同的地方表現出臨時工和正式工的各自行為。在另一個系統中,員工被分為了銷售人員、技術人員、管理人員並且也在不同的地方相應的行為不一樣。同樣,我們在設計時也是設計一個抽象的員工類,並且設計數個繼承類:銷售人員、技術人員、管理人員。現在,我們要把這兩個系統合並起來,也就是說,在新的系統中,員工既被分為臨時工和正式工,又被分為了銷售人員、技術人員、管理人員,這時候如何設計。如果我們還是使用以往的設計,我們將不得不設計很多繼承類:銷售臨時工、銷售正式工、技術臨時工、技術正式工...如此的設計,在隨著劃分的類型,以及每種類型的選項的增多,呈笛卡爾增長。通過以上一個系
⑧ 頻分復用的6. 優點
頻率復用系統的最大優點是信道復用率高,允許復用的路數多,同時它的分路也很方便。因此,它是目前模擬通信中最主要的一種復用方式,特別是在有線、微波通信系統及衛星通信系統內廣泛應用。例如,在衛星通信系統中的頻分多址(FDMA)方式,就是按照頻率的不同,把各地球站發射的信號安排在衛星頻帶內的指定位置進行頻分復用,然後,按照頻率不同來區分地球站站址,進行多址復用。
· 有效減少多徑及頻率選擇性信道造成接收端誤碼率上升的影響;
· 接收端可利用簡單一階均衡器補償信道傳輸的失真;
· 頻譜效率上升。
⑨ 代碼重用性很重要嗎怎樣在自己的代碼中重用他人的優秀代碼如何去得到這些代碼
看你要做什麼、為什麼人做了。如果你的公司把代碼行數當KPI的話,聽我的,千萬別想著什麼代碼重用。甚至循環、遞歸神馬的,能不用就盡量別用。如果一個功能,比如排序,如果計算機執行它需要O(n log n)條指令,咱最好就寫出這么多行代碼——簡單省心,而且KPI源源而來。偶曾經在某外包公司,為這種公司寫了個線程調度器,可以把任何耗時的調用變成非同步,不需要改原本的處理邏輯和介面(這就意味著要自動維護參數傳遞所用的內存,一個小操作系統的雛形)。這個東東我就用了150來行C代碼(加上注釋300多行),高效、安全、可靠,0bug。很得意。然後就被批評代碼行數太少——這玩意兒要當個操作系統來寫,幾百萬行都打不住;再用上我之前說的「不循環不遞歸,全部一條條手工咔咔代碼」這個小手段,光這個功能飆上數千萬行代碼簡直太輕松不過了。哪怕一條代碼1毛錢,這就直接創造了幾百萬的價值。結果被我這個笨蛋300行就搞定了,導致公司承受了數百萬的慘重損失^_^。前車之鑒啊。____________________________但,如果你們是寫真正到市場上公平競爭的軟體——這種公司絕對不會用代碼行數當KPI指標——那麼,你就必須時刻琢磨著,哪段代碼可以重用。代碼的可重用性之所以重要,並不僅僅是說今後類似項目可以挖來用;而是在當前項目中,你的一些基礎演算法實現本身就可以當作庫來用,可以作為高層演算法所操作的基本原子。比如說,你要統計教授、助教、領導以及食堂工人的工資,那麼就必須考慮到,所有的工資都是工資,都可以用同樣的方式來統計。在這個案例里,能否重用工資統計演算法,代碼量就是5、6倍的差異;這5、6倍的代碼理所當然會帶來5、6倍的bug——甚至,因為你必須區分不同的人群來選擇不同的統計演算法,這個選擇演算法本身也會帶來額外的bug。最終,你可能要多寫10倍的代碼,多出10倍的bug,並最終導致你的軟體比競爭對手晚一年才能上市。反之,如果你會說,工資統計說白了也是統計;所有的統計都是一樣的——那好,現在你的系統里,不僅無需區分教授、助教,甚至可以無需區分采購、維護、撥款等等不同項目:因為說到底,它們都是把一些數字加起來,然後算平均算方差算總額……如此而已。一旦你能想到這步,那麼你就可以用原來幾十分之一的代碼,完成所有功能。代碼量少、邏輯簡單,自然bug就少;同時,應當需求變更的能力也就更強。於是,當競爭對手的軟體需要半年才能上市時,你可能一個星期就已經搞定了。而且,由於簡單,你的東西可以一寫出來就是0bug;而對手,卻要被用戶的投訴搞的焦頭爛額。代碼重用之所以重要,這就是原因。——————————————————————另外,對商業公司來說,請 不要抄襲他人的優秀代碼 。甚至看的半懂不懂,然後把其中的看懂的一部分改頭換面,用自己的方式敲出來,都是不行的——而且,這也並 不是重用, 還會惹上極為麻煩的法律問題——當然,國內的話,無視這點吧。前面已經說過,重用最重要的,是重用自己的代碼,這樣才可能利用重用來減少代碼量、簡化程序結構、提高生產效率。這和抄襲優秀代碼,本質上是不同的。而且,優秀代碼有自己的適用范圍。離開了這個特定的適用范圍,它很可能就充滿了bug。我做過一個項目,最初的實驗論證階段要做一個粗糙實現。其中的網路通信部分,我先寫了一部分;寫完有人提議,說可以用openvpn的。想想也對,反正這個版本只是論證,肯定不拿出去賣,直接利用成熟的代碼應該能節省點時間。於是剩下的一部分就直接調用openvpn里的通信函數了。寫完測試,卻發現無法工作。花了幾個小時調試,最後跟進去才發現,我寫的代碼完全正常,但到了openvpn代碼那裡就停住不動了。這才發現,雖然做的是幾乎完全相同的工作,但由於openvpn自己的特殊需要額外做了一個動作,這個動作就導致程序不能正常工作。把來自openvpn的代碼全部刪掉,替換成我的東西,也就十幾分鍾的功夫,程序很順利就跑起來了。