摘要:軟件設計原則這篇文章主要討論如何以健壯的方式應對變化的需求,從而保持良好的編程習慣。前言軟件設計是開發(fā)周期中最重要的一個環(huán)節(jié)。識別出系統(tǒng)會發(fā)生變化的部分,并將其和不變的部分分開。
軟件設計原則
這篇文章主要討論如何以健壯的方式應對變化的需求,從而保持良好的編程習慣。
前言軟件設計是開發(fā)周期中最重要的一個環(huán)節(jié)。在實現(xiàn)彈性和靈活的設計上花的時間越多,未來在面對需求變更時節(jié)約的時間就越多。
需求總是在變化--如果沒有定期加入新功能,或是維護現(xiàn)有功能,軟件很快就會成為遺棄產(chǎn)物--而這些變化帶來的開銷是由系統(tǒng)的架構和體系結構決定的。在這篇文章中,我們將會討論一個關鍵的設計原則,該設計原則能幫助我們創(chuàng)建易于維護和擴展的軟件。
一個實際場景假設你的老板讓你創(chuàng)建一個將Word文件轉化為PDF文件的應用。這個任務看上去很簡單--你要做的就是找到一個可靠的將Word轉化為PDF的庫,并將這個庫插入到你的應用中。在一番查找之后,假設你決定使用Aspose.words插件,并且新建了這樣一個類:
/** * A utility class which converts a word document to PDF * @author Hussein * */ public class PDFConverter { /** * 這個方法傳入一個待轉化的文檔作為參數(shù)并返回轉化后的文檔 * @param fileBytes * @throws Exception */ public byte[] convertToPDF(byte[] fileBytes) throws Exception { // 我們確定輸入總是一個WORD格式的文件,所以我們直接用aspose.words框架進行轉化 InputStream input = new ByteArrayInputStream(fileBytes); com.aspose.words.Document wordDocument = new com.aspose.words.Document(input); ByteArrayOutputStream pdfDocument = new ByteArrayOutputStream(); wordDocument.save(pdfDocument, SaveFormat.PDF); return pdfDocument.toByteArray(); } }
現(xiàn)在一切運轉正常!生活多么美好!
需求當然變更啦在幾個月以后,一些客戶要求還能夠支持轉換Excel文件。于是你經(jīng)過一番研究后,決定使用Aspose.cells插件。然后你回到了之前創(chuàng)建的那個類,添加了一個新的變量`documentType·,修改后的代碼如下:
public class PDFConverter { // 我們不想影響現(xiàn)有的功能 // 默認情況下,這個類將WORD轉化為PDF // 當用戶將該變量設為EXCEL時,會將EXCEL轉化為PDF /** public String documentType = "WORD"; * 這個方法傳入一個待轉化的文檔作為參數(shù)并返回轉化后的文檔 * @param fileBytes * @throws Exception */ public byte[] convertToPDF(byte[] fileBytes) throws Exception { if (documentType.equalsIgnoreCase("WORD")) { InputStream input = new ByteArrayInputStream(fileBytes); com.aspose.words.Document wordDocument = new com.aspose.words.Document(input); ByteArrayOutputStream pdfDocument = new ByteArrayOutputStream(); wordDocument.save(pdfDocument, SaveFormat.PDF); return pdfDocument.toByteArray(); } else { InputStream input = new ByteArrayInputStream(fileBytes); Workbook workbook = new Workbook(input); PdfSaveOptions saveOptions = new PdfSaveOptions(); saveOptions.setCompliance(PdfCompliance.PDF_A_1_B); ByteArrayOutputStream pdfDocument = new ByteArrayOutputStream(); workbook.save(pdfDocument, saveOptions); return pdfDocument.toByteArray(); } } }
這段代碼對新客戶來說是完美的(現(xiàn)有的客戶也可以如期使用它),但是代碼中開始出現(xiàn)了壞味道。這意味著,我們的修改并不完美。當出現(xiàn)新的文檔類型時,我們不能簡單的修改這個類。
代碼的重復:如你所見,在if/else塊中出現(xiàn)了相似的代碼。如果某天我們設法擴展這段代碼,我們將會產(chǎn)生大量的重復代碼。除此以外,如果我們以后決定,比如,返回一個file而不是byte[],那么我們需要在所有的代碼快中進行重復的修改。
僵硬:所有的轉化算法在同一個方法中高度耦合,所以當你改變其中某個算法時,很有可能會影響別的算法。
固定性:上面的方法直接依賴于documentType變量。一些用戶在使用方法converToPDF之前可能會忘記設置該變量,所以他們無法得到預期的結果。而且,因為這個方法依賴于該變量,我們無法在別的項目中重用該方法。
高層模塊額底層框架的耦合:如果我們后面出于某種原因,決定將Aspose框架換成另一個更可靠的框架,我們將會需要修改整個PDFConverter類,很多用戶將會受到影響。
正確的方式通常情況下,開發(fā)者無法預見未來的變化,因此初次開發(fā)時我們會將其實現(xiàn)成第一個class那樣。但是,在第一次變更后,就明確知道了未來可能會出現(xiàn)類似的變更。所以,優(yōu)秀的開發(fā)者會采取正確的實踐減少未來變更的開銷,而不是用if/else強行解決。所以,我們在工具層(PDFConverter)和底層的轉化算法之間,添加了一個抽象層,并將所有的算法移動到多帶帶的類中,如下:
/** * 這個接口代表一個抽象算法,用于將任何類型的文檔轉化為PDF * @author Hussein */ public interface Converter { public byte[] convertToPDF(byte[] fileBytes) throws Exception; }
/** * 這個類包含將Excel文檔轉化為PDF的算法 * @author Hussein * */ public class ExcelPDFConverter implements Converter { public byte[] convertToPDF(byte[] fileBytes) throws Exception { InputStream input = new ByteArrayInputStream(fileBytes); Workbook workbook = new Workbook(input); PdfSaveOptions saveOptions = new PdfSaveOptions(); saveOptions.setCompliance(PdfCompliance.PDF_A_1_B); ByteArrayOutputStream pdfDocument = new ByteArrayOutputStream(); workbook.save(pdfDocument, saveOptions); return pdfDocument.toByteArray(); }; }
/** * 這個類持有將Word文檔轉化為PDF的算法 * @author Hussein * */ public class WordPDFConverter implements Converter { @Override public byte[] convertToPDF(byte[] fileBytes) throws Exception { InputStream input = new ByteArrayInputStream(fileBytes); com.aspose.words.Document wordDocument = new com.aspose.words.Document(input); ByteArrayOutputStream pdfDocument = new ByteArrayOutputStream(); wordDocument.save(pdfDocument, SaveFormat.PDF); return pdfDocument.toByteArray(); } }
public class PDFConverter { /** * 這個方法接收待轉化文檔作為參數(shù)并且返回轉化后的文檔 * @param fileBytes * @throws Exception */ public byte[] convertToPDF(Converter converter, byte[] fileBytes) throws Exception { return converter.convertToPDF(fileBytes); } }
我們強迫用戶在調用convertToPDF()方法時決定轉化算法。
這樣做的好處?關注點分離(高內聚/低耦合):PDFConverter類現(xiàn)在對應用中使用的轉化算法一無所知。它只關注于想用戶提供各種轉化功能,而不去關心轉化是如何實現(xiàn)的。現(xiàn)在,只要能夠返回預期的結果,我們就能夠在沒有人注意到的情況話,替換底層的轉換框架。
單一職責:在創(chuàng)建了抽象層,并將每個動態(tài)的行為移動到各個類之后,我們能夠刪除原始設計中convertToPDF()方法持有的多個職責。現(xiàn)在它只有一個職責,就是將客戶的請求委托給抽象轉化層。除此以外,Converter接口的每個具體實現(xiàn)都只有將某種類型的文檔轉化為PDF這一個職責。因此,每個組件只可能因為單個原因被修改,不會相互影響。
開閉原則:我們的應用現(xiàn)在對擴展開放,對更改關閉。無論何時我們想要添加對某種文檔的支持,我們只需要創(chuàng)建Converter接口的一個新的具體類,然后這個新的類型就會立刻被支持,而無需修改PDFConverter工具類,因為該工具類現(xiàn)在依賴于抽象接口。
本文中學習到的設計原則當你創(chuàng)建你自己系統(tǒng)的體系結構時,以下是一些最佳實踐:
將應用拆分成幾個模塊,并且在每個模塊之上添加抽象層。
抽象優(yōu)先于實現(xiàn):確??偸且蕾囉诔橄髮印_@會使你的應用對未來的擴展開放。抽象技術應使用于系統(tǒng)的動態(tài)部分(即最可能頻繁變化的部分)而不必使用于所有部分。濫用它會增加代碼的復雜度。
識別出系統(tǒng)會發(fā)生變化的部分,并將其和不變的部分分開。
不要重復:將重復的功能放在工具類中,使其在整個應用中都可以訪問。這將會使變更更簡單一些。
通過抽象機制隱藏低層實現(xiàn):低層的模塊有很大的可能會頻繁變更。所以將它們和高層模塊分開。
每個類/方法/模塊應當只有一個變更的理由,所以只給它們一個職責。
分離關注點:每個模塊知道另一個模塊做什么,但無需知道它們怎么做。
想要了解更多開發(fā)技術,面試教程以及互聯(lián)網(wǎng)公司內推,歡迎關注我的微信公眾號!將會不定期的發(fā)放福利哦~
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://systransis.cn/yun/70983.html
摘要:前言這周我準備介紹一個有趣的但是很少使用的方法按照合約編程,又稱為合約編程,是一種軟件設計的方法。這些規(guī)則被稱為合約,可以比擬為商業(yè)合同中的條件和義務。通過將檢查和異常拋出指令包裝到方法中,人們可以很容易地實現(xiàn)合約式編程。 前言 這周我準備介紹一個有趣的但是很少使用的方法 按照合約編程,又稱為合約編程,是一種軟件設計的方法。它規(guī)定了軟件設計師應該為軟件組件定義正式,精確和可驗證的接口規(guī)...
摘要:無需檢查的異常也是的子類。從低層拋出的需檢查異常強制要求調用方捕獲或是拋出該異常。當前執(zhí)行的線程將會停止并報告該異常。單元測試允許我在使用中查看異常,并且作為一個可以被執(zhí)行的文檔來使用。不要捕獲最高層異常繼承的異常同樣是的子類。 前言 異常處理的問題之一是知道何時以及如何去使用它。我會討論一些異常處理的最佳實踐,也會總結最近在異常處理上的一些爭論。 作為程序員,我們想要寫高質量的能夠解...
摘要:什么是為執(zhí)行字節(jié)碼提供一個運行環(huán)境。它的實現(xiàn)主要包含三個部分,描述實現(xiàn)規(guī)格的文檔,具體實現(xiàn)和滿足要求的計算機程序以及實例具體執(zhí)行字節(jié)碼。該類先被轉化為一組字節(jié)碼并放入文件中。字節(jié)碼校驗器通過字節(jié)碼校驗器檢查格式并找出非法代碼。 什么是Java Development Kit (JDK)? JDK通常用來開發(fā)Java應用和插件。基本上可以認為是一個軟件開發(fā)環(huán)境。JDK包含Java Run...
摘要:讀取出數(shù)據(jù)時,將此版本號一同讀出,之后更新時,對此版本號加一。此時,將提交數(shù)據(jù)的版本數(shù)據(jù)與數(shù)據(jù)庫表對應記錄的當前版本信息進行比對,如果提交的數(shù)據(jù)版本號大于數(shù)據(jù)庫表當前版本號,則予以更新,否則認為是過期數(shù)據(jù)。 前言 很多人都在討論數(shù)據(jù)的指數(shù)型增長,以及我們將會有比想象的還要大的數(shù)據(jù)量。但是,很少有人從數(shù)據(jù)庫的角度談論這個問題。隨著數(shù)據(jù)量的暴漲,數(shù)據(jù)庫也需要隨之升級。這也是為什么既要了解如...
閱讀 759·2023-04-26 01:30
閱讀 3309·2021-11-24 10:32
閱讀 2197·2021-11-22 14:56
閱讀 1994·2021-11-18 10:07
閱讀 563·2019-08-29 17:14
閱讀 636·2019-08-26 12:21
閱讀 3115·2019-08-26 10:55
閱讀 2951·2019-08-23 18:09