計算機軟件設計原理

軟件設計一直是開發週期中最重要的階段。您花更多的時間設計彈性和靈活的體系結構,將來在發生更改時會節省更多的時間。
需求總是在變化–如果不定期添加或維護任何功能,則軟件將成爲傳統–並且這些變化的成本取決於系統的結構和體系結構。在本文中,我們將討論有助於創建易於維護和擴展的軟件的關鍵設計原則。
實際方案
假設老闆要求您創建一個將Word文檔轉換爲PDF的應用程序。該任務看起來很簡單-您要做的就是查找一個可靠的庫,該庫將Word文檔轉換爲PDF,並將其插入應用程序中。經過研究後,說您最終使用了 Aspose.words框架並創建了以下類:
/**

  • A utility class which converts a word document to PDF
  • @author Hussein

/
public class PDFConverter {
/
*
* This method accepts as input the document to be converted and
* returns the converted one.
* @param fileBytes
* @throws Exception
*/
public byte[] convertToPDF(byte[] fileBytes) throws Exception {
// We’re sure that the input is always a WORD. So we just use
//aspose.words framework and do the conversion.
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();
}
}

生活輕鬆,一切順利!
需求變更一如既往
幾個月後,一些客戶要求您也支持Excel文檔。因此,您進行了一些研究,並決定使用Aspose.cells。然後,返回您的類,添加一個名爲documentType的新字段,並按如下所示修改您的方法:
public class PDFConverter {
// we didn’t mess with the existing functionality, by default
// the class will still convert WORD to PDF, unless the client sets
// this field to EXCEL.
public String documentType = “WORD”;
/**
* This method accepts as input the document to be converted and
* returns the converted one.
* @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();
}
}
}

該代碼將對新客戶端完全有效(並且仍將按預期對現有客戶端運行),但是一些不良的設計異味開始出現在代碼中。這意味着我們沒有采用完美的方法,當請求新的文檔類型時,我們將無法輕鬆地修改類。
1.代碼重複:如您所見,在if / else塊中正在重複類似的代碼,如果有一天我們設法支持不同的擴展,那麼我們將有很多重複。同樣,例如,如果稍後我們決定返回文件而不是byte [],那麼我們必須在所有塊中進行相同的更改。
2.剛性: 所有轉換算法都在同一方法內耦合,因此,如果更改某些算法,則其他算法可能會受到影響。
3.固定性:上述方法直接取決於 documentType字段。一些客戶端會在調用convertToPDF()之前忘記設置該字段,因此他們將無法獲得預期的結果。另外,由於該方法依賴於字段,因此我們無法在其他任何項目中重用該方法。
4.高級模塊和框架之間的耦合: 如果我們以後出於某種目的決定用更可靠的框架替換Aspose框架,我們將最終修改整個PDFConverter 類-許多客戶端將受到影響。
用正確的方法做
通常,開發人員無法預測未來的變化,因此大多數開發人員將完全按照我們第一次實施的方式實施該應用程序。但是,在進行第一次更改之後,可以清楚地看到將來還會發生類似的更改。因此,優秀的開發人員不會使用if / else塊對其進行破解,而是以正確的方式進行操作,以最大程度地減少未來更改的成本。因此,我們在暴露的工具(PDFConverter)和低級轉換算法之間創建一個抽象層,並將每種算法移到一個單獨的類中,如下所示:
/**

  • This interface represents an abstract algorithm for converting
  • any type of document to a PDF.
  • @author Hussein

/
public interface Converter {
public byte[] convertToPDF(byte[] fileBytes) throws Exception;
}
/
*

  • This class holds the algorithm for converting Excel
  • documents to PDFs.
  • @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();
};
}
/
*

  • This class holds the algorithm for converting Word
  • documents to PDFs.
  • @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 {
/
*
* This method accepts the document to be converted as an input and
* returns the converted one.
* @param fileBytes
* @throws Exception
*/
public byte[] convertToPDF(Converter converter, byte[] fileBytes) throws Exception {
return converter.convertToPDF(fileBytes);
}
}

我們強制客戶端決定在調用convertToPDF()時使用哪種轉換算法。
這樣做的好處是什麼?
1.關注點分離(高內聚/低耦合):現在, PDFConverter類對應用程序中使用的轉換算法一無所知。它的主要關注點是爲客戶提供各種轉換功能,而不管轉換是如何進行的。現在我們可以替換低級轉換框架了,只要我們返回預期的結果,沒人會知道。
2.單一職責:創建抽象層並將每個動態行爲移至一個單獨的類之後,我們實際上刪除了最初在最初設計中的convertToPDF()方法所承擔的多個職責 。現在,它只需承擔一項職責,即將客戶請求委託給抽象轉換層。而且,Converter接口的每個具體類 現在都具有與將某些文檔類型轉換爲PDF有關的單一職責。結果,每個組件都有一個要修改的原因,因此沒有迴歸。
3.打開/關閉應用程序: 我們的應用程序現已打開以進行擴展,並關閉以進行修改。每當我們想要添加對某些文檔類型的支持時,我們只需從Converter接口創建一個新的具體類, 並且由於無需修改PDFConverter工具,新類型便會受到支持 ,因爲我們的工具現在依賴於抽象。
從本文中學到的設計原則
以下是構建應用程序體系結構時應遵循的一些最佳設計實踐。
1.將您的應用程序劃分爲幾個模塊,並在每個模塊的頂部添加一個抽象層。
2.在實現上偏愛抽象:始終確保依賴抽象層。這將使您的應用程序打開以供將來擴展。應該將抽象應用於應用程序的動態部分(最有可能定期更改),而不必應用於每個部分,因爲過度使用會使代碼複雜化。
3.識別應用程序中各個方面,將其與保持不變的方面分開。
4.不要重複自己:始終將重複的功能放在某個實用程序類中,並使其可在整個應用程序中訪問。這將使您的修改容易得多。
5.通過抽象層隱藏低級實現:低級模塊很可能會定期更改,因此請將其與高級模塊分開。
6.每個類/方法/模塊都應該有一個更改的原因,因此,爲了使迴歸最小化,請始終對每個類/方法/模塊負責。
7.關注點分離:每個模塊都知道另一個模塊的功能,但永遠不應該知道如何執行。
最後,開發這麼多年我也總結了一套學習Java的資料與面試題,如果你在技術上面想提升自己的話,可以關注我,私信發送領取資料或者在評論區留下自己的聯繫方式,有時間記得幫我點下轉發讓跟多的人看到哦。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章