---
# System prepended metadata

title: 第八章 邊界
tags: [Tag(Clean Code)]

---

# 第八章 邊界

###### tags: `Tag(Clean Code)`

[TOC]

## 大綱

注重邊界的目的在於將自己==不可控的部分==，比如第三方類庫，變成相對可控的部分。至少劃清邊界後，如果是因為第三方類庫版本更新造成的問題，我們可以快速定位並決定是否替換。

軟體設計在於==適應這些改變==，而不是大規模投入或重新撰寫程式，當使用無法控制的程式碼時，特別花工夫去維護，確保不花太多功夫，因應未來的修改。

邊界的程式碼必須清楚分割，定義預期的測試，最好依靠在可以控制的程式上，而不是無法控制程式碼上。

### 使用第三方軟體程式碼

開源的第三方類庫,有些時候甚至會產生一些我們意料之外的問題，比如兼容性問題，或是類庫更版導致一些方法不再可用等問題。

針對對第三方的類庫進行封裝，新建一個類對需要使用到的第三方類庫進行包裹，如果要使用第三方類庫，就調用這個包裹類，這麼做的好處在於，如果第三方類庫出現問題，我們能夠更快速地定位問題，不至於讓這些陌生的對象充斥在系統的各個角落。

### 使用尚未存在的程式

尚未被設計出來的API。

### 探索及學習邊界

==學習測試（Learning Test）==

學習第三方軟體是件困難的事，整合更是困難，書中採取另一種方法
，撰寫測試程式碼。
學習測試裡，檢驗API瞭解程度，測試想要重API獲得的結果。

* 寫簡單測試，並不花太多功夫，可以增進對API的了解，
* 定義自己的介面，掌握主控權

## 舉例：
### 學習LOG4J學習log4j

假設我們要使用apache log4j軟件包，而不是我們自己的定制記錄器。我們下載它並打開介紹性文檔頁面。無需過多閱讀，我們就可以編寫第一個測試用例，期望它會向控制台寫入“ hello”。

```
@Test
public void testLogCreate() {
   Logger logger = Logger.getLogger("MyLogger");
   logger.info("hello");
}
```

==運行時，記錄器會產生一個錯誤==，告訴我們我們需要一個叫做==Appender==的東西。閱讀更多內容後，我們發現有一個ConsoleAppender。因此，我們創建一個==ConsoleAppender==並查看是否已解鎖登錄控制台的秘密。

```
@Test
public void testLogAddAppender() {
   Logger logger = Logger.getLogger("MyLogger");
   ConsoleAppender appender = new ConsoleAppender();
   logger.addAppender(appender);
   logger.info("hello");
}
```

這次我們發現Appender沒有輸出流。奇怪-它有一個似乎是合乎邏輯的。經過Google的一點幫助之後，我們嘗試以下方法：

```
@Test
public void testLogAddAppender() {
   Logger logger = Logger.getLogger("MyLogger");
   logger.removeAllAppenders();
   logger.addAppender(new ConsoleAppender(
            new PatternLayout("%p %t %m%n"),
            ConsoleAppender.SYSTEM_OUT));
   logger.info("hello");
}
```
閱讀和測試，最終我們得到了清單8-1。我們已經發現了很多有關log4j的工作方式，並且已經將這些知識編碼為一組簡單的單元測試。



---


初始化一個簡單的控制台日誌器，也能把這些知識封裝到自己的日誌分類中，好將應用程序的其他部分與log4j的邊界接口隔離開來
```
public class LogTest {
    private Logger logger;

    @Before
    public void initialize() {
        logger = Logger.getLogger("logger");
        logger.removeAllAppenders();
        Logger.getRootLogger().removeAllAppenders();
    }

    @Test
    public void basicLogger() {
        BasicConfigurator.configure();
        logger.info("basicLogger");
    }

    @Test
    public void addAppenderWithStream() {
        logger.addAppender(new ConsoleAppender(
                new PatternLayout("%p %t %m%n"),
                ConsoleAppender.SYSTEM_OUT));
        logger.info("addAppenderWithStream");
    }

    @Test
    public void addAppenderWithoutStream() {
        logger.addAppender(new ConsoleAppender(
                new PatternLayout("%p %t %m%n")));
        logger.info("addAppenderWithoutStream");
    }
}
```