成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

貓頭鷹的深夜翻譯:軟件設計原則--更健壯的代碼

xiaochao / 3119人閱讀

摘要:軟件設計原則這篇文章主要討論如何以健壯的方式應對變化的需求,從而保持良好的編程習慣。前言軟件設計是開發(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

相關文章

  • 頭鷹深夜翻譯:在JVM上根據(jù)合約編程

    摘要:前言這周我準備介紹一個有趣的但是很少使用的方法按照合約編程,又稱為合約編程,是一種軟件設計的方法。這些規(guī)則被稱為合約,可以比擬為商業(yè)合同中的條件和義務。通過將檢查和異常拋出指令包裝到方法中,人們可以很容易地實現(xiàn)合約式編程。 前言 這周我準備介紹一個有趣的但是很少使用的方法 按照合約編程,又稱為合約編程,是一種軟件設計的方法。它規(guī)定了軟件設計師應該為軟件組件定義正式,精確和可驗證的接口規(guī)...

    whatsns 評論0 收藏0
  • 頭鷹深夜翻譯:JAVA中異常處理最佳實踐

    摘要:無需檢查的異常也是的子類。從低層拋出的需檢查異常強制要求調用方捕獲或是拋出該異常。當前執(zhí)行的線程將會停止并報告該異常。單元測試允許我在使用中查看異常,并且作為一個可以被執(zhí)行的文檔來使用。不要捕獲最高層異常繼承的異常同樣是的子類。 前言 異常處理的問題之一是知道何時以及如何去使用它。我會討論一些異常處理的最佳實踐,也會總結最近在異常處理上的一些爭論。 作為程序員,我們想要寫高質量的能夠解...

    W_BinaryTree 評論0 收藏0
  • 頭鷹深夜翻譯:JDK Vs. JRE Vs. JVM之間區(qū)別

    摘要:什么是為執(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...

    blair 評論0 收藏0
  • 頭鷹深夜翻譯:你需要了解數(shù)據(jù)庫名詞

    摘要:讀取出數(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ù)庫也需要隨之升級。這也是為什么既要了解如...

    wangym 評論0 收藏0

發(fā)表評論

0條評論

xiaochao

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<