---
# System prepended metadata

title: Chapter 7-11
tags: [Clean Architecture, SOLID]

---

---
tags: Clean Architecture,SOLID  
title: Chapter 7-11
---
{%hackmd Sy5Quc5JY %}
# <font color="#F59F00">**SOLID 設計原則**</font>
:::info
**SOLID只是一個想法，而Design Pattern是一個讓這個想法實踐的手段**
:::
:::info
**DS: DataStructer
DB: DataBase
I: Interface 
開放的箭頭: using 關係(import DB, 使用 Type class)
封閉的箭頭: 實作或繼承**
:::
## <font color="#F59F00">**SRP: Single Responsibility Prcinple(單一職責原則)**</font>
*    <font color="#92d1ff">**Definition**</font>
        <br>
        >**A module should have one, and only have one reason to change.**
        **一個類別(class)只做一件事，如果超過一件事就要把他拆成小類別。**
        *    **Example**
        <br>
        ```java
            public class AuthManager
            {
                public void login(String userName, String userPassward)
                {
                    //...
                }
                public void logout()
                {
                    //...
                }
            }
        ```
        你可以說 AuthManager 違反了 SRP，因為他做了兩件事
        但我也可以說，他只做了一件事，就是負責 Handle user's authentication
        因此有了下面的另一個定義
        <br>
        >**A module should be responsible to,one and only one user or stakeholder.**
        **一個類別(class)只對一個角色負責。**
        *    **Example**
        <br>
        ```java
            public class Empolyee
            {
                public int calculatePay()
                {
                    //...
                }
                public ReportHours reportHours()
                {
                    //...
                }
                public void save()
                {
                    //...
                }
            }
        ```
        <span style="display:block;text-align:center;"><img src="https://i.imgur.com/qgPTY1T.png" width="600" height="400"></img></span>
        這個類別違反了 SRP
        *    **Def-1: 它有 3 個 reasons to change**
                *    如果會計部更改時薪，更改 `calculatePay()`
                *    如果人事部更改加班，更改 `reportHours()`
                *    如果工程師更改資料，更改 `save()`
        *    **Def-2: 它負責了 3 個 users** 
                *   `calculatePay()` 由會計部負責->CFO
                *   `reportHours()`  由人事部負責->COO
                *   `save()` 工程師負責->CTO
                
        Q : 回顧 AuthManager 是否違反 SRP ?
        A : 不一定，根據實作決定。
        *    **Example**
        <br>
        ```java
            public class AuthManager
            {
                private String userName = "";
                
                public void logIn(String userName, String userPassword)
                {
                    if(userName.compareTo("hyi1014")&&userPassword.compareTo("1234"))
                    {
                        this.userName = userName;
                    }
                    if(userName.compareTo("jessica0505")&&userPassword.compareTo("4321"))
                    {
                        this.userName = userName;
                    }
                }
                public void logout()
                {
                    userName = "";
                }
            }
        ```
        Q : 這是一個很爛的code，但如果你真的這麼寫了，那要怎麼改變的理由呢?
        A : 1. 改變密碼, 2. Add/Remove users
        但仔細想一想，這兩件對你的實作來說是同一件事，只需要改變驗證的方法，也就是 `if` ，所以以上實作只有一個理由改變，符合 SRP 。讓我們再看一個範例。
        *    **Example**
                <br>
        ```java
            public class AuthManager
            {
                private String userName = "";
                
                public void logIn(String userName, String userPassword)
                {
                    String hash = hashPassword(userPassword);
                    if(isUserInDB(userName,hash))
                    {
                        this.userName = userName;
                    }
                }
                public void logout()
                {
                    userName = "";
                }
                public String hashPassword(String Userpassword)
                {
                    //hash alogrithm
                }
                public boolean isUserInDB(String userName, String hash)
                {
                    //call DB
                }
            }
        ```
        Q : 那這個code有什麼理由改變呢?
        A : 1. 改變 hash 演算法, 2. 改變底層DataBase的時候
        兩個reason to change ， 所以違反了SRP。
        Q : 那要怎麼更改 AuthManager呢?
        A :
        ```java
        public class AuthManger
        {
            private String userName = "";
            private PasswordHasher passwordHasher;
            private DBChecker databaseChecker;
            public void logIn(String userName, String userPassword)
            {
                String hash = passwordHasher.hashPassword(userPassword);
                if(databaseChecker.isUserInDB(userName, hash))
                {
                    this.userName = userName;
                }
            }
            public void logOut()
            {
                userName = "";
            }
        }
        public class PasswordHasher
        {
            public String hashPassword(String userPassword)
            {
                //hash alogrithm
            }
        }
        public class DBChecker
        {
            public isUserInDB(String userName, String hash)
            {
                //...
            }
            
        }
        ```
        <span style="display:block;text-align:center;"><img src="https://i.imgur.com/HJ90mhD.png" width="600" height="400"></img></span>
*    <font color="#92d1ff">**Problems**</font>
        *    **Example**
        今天人資部跟你說，原本工作超過8小時算加班，現在要工作超過10小時才算加班 請幫我改變一下 `reportHours()` 的產生方式，然後你就改了  `reportHours()` 的實作，到了月底發薪日你覺得奇怪，你的工時沒有超過8小時，但為什麼你拿的錢變多了呢，然後公司CFO打給你，說公司虧了好幾百萬，你驚覺慘了，再仔細看你的程式，你發現
                ```java
                    public class Empolyee
                    {
                        public int calculatePay()
                        {
                            //...
                            ReportHours RH = new reportHours();
                            //...
                        }
                        public ReportHours reportHours()
                        {
                            //...
                        }
                        public void save()
                        {
                            //...
                        }
                    }
                ```
                結果你發現，`calculatePay()` 在呼叫 `reportHours()` 影響了會計部的舊功能。這是因為函式與函式之間過度耦合(couple)，所以一個更改就會產生很多副作用(side effect)。
*    <font color="#92d1ff">**Resolution**</font>
        把未來有可能更改的函式分離到其他類別(class)，一個常見的解決手段是用 FACADE mode。
        ```java
            public class EmpolyeeFacade
            {
                private String id;
                public string getId(){return id;}
                PayCalculator payCalculator;
                HoursReporter hoursReporter;
                EmployeeSaver employeeSaver;
                public EmployeeFacade(
                    String id, 
                    PayCalculator payCalculator, 
                    HoursReporter hoursReporter,
                    EmployeeSaver employeeSaver)
                {
                    this.id = id;
                    this.payCalculator = payCalculator;
                    this.hoursReporter = hoursReporter;
                    this.employeeSaver = employeeSaver;
                }
                public void employee()
                {
                    System.out.println("Employee %s", id);
                    payCalculator.calculatePay();
                    hoursReporter.reportHours();
                    employeeSaver.save();
                }
            }
            public class PayCalculator
            {
                public int calculatePay()
                {
                    //...
                }
            }
            public class HoursReporter
            {
                public ReportHours reportHours()
                {
                    //...
                }
            }
            class EmployeeSaver
            {
                public void save()
                {
                    //...
                }
            }
        ```
        呼叫
        ```java
        EmployeeFacade employee = new emEmployeeFacade(id, payCalculator, hoursReporter,employeeSaver);
        employee.employee();
        ```
        <span style="display:block;text-align:center;"><img src="https://i.imgur.com/qXzRh9u.png" width="700" height="450"></img></span>
        你可以能會覺得，每個類別只包含了一個函式，但並非如此，每個類別都包含了許多  private 函式。
*    <font color="#92d1ff">**Conclusion**</font>
    符不符合 SRP ，取決於你對於你商業邏輯的了解，沒有一定的標準答案，而 SRP 的目的在於，在已知的設計目標下，將功能分割成一個類別只做一件事(一個類別只對一個腳色負責)，以達到最大的修改彈性。
            <br>
        :::warning
        **SRP 是在函式和類別層面的，而在元件層面(兩個層次以上)，它成為 CCP**
        :::
## <font color="#F59F00">**OCP: Open-Close Prcinple(開放-封閉原則)**</font>
*    <font color="#92d1ff">**Definition**</font>
        >**A software entities(classes, modules, functions, etc) should be open for extension, but closed for modification.**
        
        **一個軟體應該對擴展是開放的，對修改則是封閉的，
        這句話我們下面再解釋，先把上述記在心裡，我們來聽聽其他定義。**
        >**Depend on stable abstractions and modify system's behavior by providing differen realization(Concrete object).**
        
        >**Protected Variation pattern: Identify points of predicted variation and create a stable interface around them**
        
        **上述兩個定義，簡而言之，建立一層抽象，並用層抽象(abstraction, interface)去建立實像。**
        *    **Example**
        <br>
        ```java
        public class  SalaryCalculator
        {
            public int calculateSalary(Employee employee)
            {   
                //...
                int taxDedection = new calculateTax(employee);
                //...   
            }
            private int calculateTax(Employee employee)
            {
                switch(employee.getType())
                {
                    case FullTime:
                        //..
                    case Contractor:
                        //..
                }
            }
        }
        ```
        以上違反了SRP，我們做一點修改。
        ```java
        public class SalaryCalculator
        {
            private TaxCalculator taxCalculator;
            
            public int calculateSalary(Employee employee)
            {
                int taxDection = taxCalculator.calculateTax(employee);
            }
        }
        public class TaxCalculator
        {
            public int calculateTax(Employee employee)
            {
                switch(employee.getType())
                {
                    case FullTime:
                        //..
                    case Contractor:
                        //..
                }
            }
        }
        ```
*    <font color="#92d1ff">**Problems & Soultions**</font>
        Q : 現在 SalaryCalculator 沒有問題了，但是 TaxCalcalator 卻有很大的問題，如果要新增類型，必須不斷的增加 `switch`，那我們要怎麼辦呢?
        A : 在 TaxCalcalator 上面加上一層抽象層，讓 TaxCalculator 變成一個介面，用這個介面去實作類別。使用抽象 interface 把實作留給 subclass ，這個手段為 Strategy mode。
        ```java
            public class SalaryCalculator
            {
                private TaxCalculator taxcalculator;
                
                public int calculateSalary(Employee employee)
                {
                    //...
                    switch(employee.getType())
                    {
                        case FullTime:
                        //..
                        case Contractor:
                        //..
                    }
                }
            }
            public interface TaxCalculator
            {
                public int calculateTax(Employee employee);
            }
            public class TaxCalculator_FullTime implements TaxCalculator
            {
                calculateTax(Employee employee)
                {
                //calculate FullTime tax
                }
            }
            public class TaxCalculator_Contractor implements TaxCalculator
            {
                calculateTax(Employee employee)
                {
                //calculate Contractor tax
                }
            }
        ```
        Q : 你說奇怪，你只是把 `switch` 從 TaxCalculator 移到 SalaryCalculator 這有什麼用嗎?
        A : 有用!因為讓 `switch` 裡面變成 simple Factory ，運用這個手段，可以讓 SalaryCalculator 變成
        ```java
            public class SalaryCalculator
            {
                private TaxCalculatorFactory taxcalculatorFactory;
                
                public int calculateSalary(Employee employee)
                {
                    //...
                    TaxCalculator taxCalculatorFactory =
                        taxCalculatorFactory.newCalculator(employee.getType());
                    {
                        int taxDection = taxCalculator.calculator(employee);
                        //...
                    }
                    
                }
            }
            public class TaxCalculatorFactory
            {
                public TaxCalculator newCalculator(Employee employee)
                {
                    switch (employeeType) {
                      case FULL_TIME:
                        return new TaxCalculatorFullTime();
                      case CONTRACTOR:
                        return new TaxCalculatorContractor();
                      default:
	                    return new TaxCalculatorDefault();
                }
            }
            public interface TaxCalculator
            {
                public int calculateTax(Employee employee);
            }
            public class TaxCalculator_FullTime implements TaxCalculator
            {
                calculateTax(Employee employee)
                {
                //calculate FullTime tax
                }
            }
            public class TaxCalculator_Contractor implements TaxCalculator
            {
                calculateTax(Employee employee)
                {
                //calculate Contractor tax
                }
            }
        ```
        <span style="display:block;text-align:center;"><img src="https://i.imgur.com/ZkPvy0H.png" width="750" height="500"></img></span>
        Q : 如果現在要新增一個 TaxCalculator 的類型， TaxCalculator_PartTime?
        A : 只要讓 TaxCalculator extend TaxCalculator 然後在 TaxCalculator_PartTime 裡面執行 partTime 的 calculateTax 就好了。

        <span style="display:block;text-align:center;"><img src="https://i.imgur.com/DX45mco.png" width="750" height="500"></img></span>
        
*    <font color="#92d1ff">**Review Definition**</font>
        >**Depend on stable abstractions and modify system's behavior by providing differen realization(Concrete object).**
        
        **TaxCalculator 就是 stable abstractions ，
        TaxCalculatorFullTime, TaxCalculatorContrator 就是 different realizations。**
        >**Protected Variation pattern: Identify points of predicted variation and create a stable interface around them**
        
        **predicted variation就是稅金計算的方式 而 stable interface 就是 TaxCalculator。**
        >**A software entities(classes, modules, functions, etc) should be open for extension, but closed for modification.**
        
        **close 是要有一個不改變的抽象層，open 是要能提供彈性的實作。**
*    <font color="#92d1ff">**Conclusion**</font>
    一個恰當的抽象可以讓你的程式更有彈性，而 Simple Factory 和 Strategy mode 只是讓我們完成OCP的手段。
*    <font color="#92d1ff">**Assumption**</font>
        如果我們用 SRP OCP 來妥善組織一個財務摘要的系統，然後用 DIP(之後解釋) 建立之間的依賴關係。將處理程序劃分在類別中，並將類別分離到元件(較大的階層，之後解釋)中
        <br>
        在元件階層的依賴關係
        <br>
        <span style="display:block;text-align:center;"><img src="https://i.imgur.com/7Fc9fR2.png" width="750" height="500"></img></span>
        **如果要保護元件 A 免於元件 B 的改變影響，那麼元件 B 應該依賴元件 A (B->A)**
        我們希望保護 Interactor 免於 Controller 的改變、Interactor 免於 DataBase 的改變、
        Controller 免於 Presenter 的改變、Presenter 免於  Veiw 的改變。
        而 Interactor 是最符合 OCP 的位置，免於其他元件的改變影響。
        Q : 為什麼要這樣設計呢?
        A : 因為 Interactor 裡面包含了 **!業務規則!**，建立保護階層，是基於層級的觀念，有就是說， Interactor 是最高階層，最高指導原則。
        <br>
        元件與類別的依賴關係
        <br>
        <span style="display:block;text-align:center;"><img src="https://i.imgur.com/26nJLL1.png" width="750" height="500"></img></span>
        Q : 為什麼要這樣設計呢?
        A : 圖中大部分的複雜性都是為了 **改變方向**，確保元件之間有正確的依賴方向。
        例如 : Financial Data Gate 介面，這是為了反向從 Interactor 元件指向 Database 元件的依賴關係，而 Financial Report Representer 和 View 介面也是如此。
        Financial Report Requester 介面卻有所不同，是為了保護 Financial Report Controller 知道 Interactor 太多資訊。
        Q : 如果沒有 Financial Report Requester 介面會怎樣?
        A : Financial Report Controller 會對 Financial entities 有間接的傳遞，得知 Interactor 內更多資訊，傳遞性依賴，違反了 ISP 原則和 CRP 原則(之後會解釋)，**軟體不應該依賴他們使用不到的東西。** 
        因此我們的首要任務是保護 Interactor 不受到 Controller 的更改影響，我們也希望，透過隱藏 Interactor 內部，保護 Controller 受到 Interactor 的更改影響。
## <font color="#F59F00">**LSP: Liskov Sustitution Principle(Liskov 替換原則)**</font>
* <font color="#92d1ff">**Definition**</font>

    >**If S is a SubType of T, then objects of type of type T may be replaced with objects of type S without altering any of the desirable properities of the program.**
    
    **如果程式碼中的類別 T 物件，都可以被 S 類別給取代，且程式碼還運作正常，那稱 S 為 T 的 subType。**
* <font color="#92d1ff">**SubType vs SubClass**</font>
    *    <font color="#98c37a">**SubType**</font>
            如同 SLP 原則所說，如果 S 是 T 的 subType 那在所有 T 出現的地方都可以用 S 取代，
            ==**目標是讓你的架構更彈性。**==
    *    <font color="#98c37a">**SubClass**</font>
            A extend B, 那你就可以說 B 是 A 的 subClass ，
            ==**目標是 code reuse。**==
            
    當然 subType 也是靠 extend 來達成的，但正確的繼承才有資格被稱為 subType，換句話說，不正確的繼承只能被稱為繼承，而不是 subType。
    >**SubType = SubClass(realization) which can be substituted for the type it extends(implements)**
    *    **Example**
    如果 S 是 T 的 SubType
    <br>
    ```java
    class Sample
    {
        public void foo(T t)
        {
            //使用 t 的成員
        }
        public void bar(S s)
        {
            //使用 s 的成員
        }
        Sample sample = new Sample();
        //Access 因為 s 可以當作 t 使用
        sample.foo(s);
        //Wrong 因為 t 未必有 s 所有的成員
        sample.bar(t);
    }
    ```
    *    **Example**
            鳥和鴕鳥
            <br>
            ```java
                class Bird
                {
                    public void fly()
                    {
                        //fly
                    }
                }
                class Ostrich extends Bird
                {
                    @override
                    public void fly()
                    {
                        //Cannot fly
                        throw new RuntimeException();
                    }
                }
            ```
            在這個例子中 Ostrich 是 Bird 的 SubClass 但不是 Bird 的 SubType
            因為不是所有有 Bird 出現的地方都可以用 Ostrich 代替。
            <br>
            ```java
                Bird b = getBird();
                b.fly();
            ```
            如果這邊的 Bird 改成 Ostrich 就會有 Exception 跑出來。
* <font color="#92d1ff">**Problems & Resolutions**</font>
    *    **Example**
            長方形與正方形，因為正方形是長方形的長寬相等的特例，所以我們讓正方形繼承長方形
            <br>
            ```java
                class Rectangle
                {
                    int width;
                    int height;
                    public void setWidth(int width)
                    {
                        this.width = width;
                    }
                    public void setHeight(int height)
                    {
                        this.height = height;
                    }
                } 
                class Square extends Rectangle
                {
                     public void setWidth(int width)
                     {
                         this.width = width;
                         this.height = height;
                     }
                     public void setHeight(int height)
                     {
                         this.height = height;
                         this.width = width;
                     }  
                }
            ```
            Q : Square 是 Rectangle 的 SubClass ， 那 Square 是不是 Rectangle 的 SubType 呢?
            A : 不一定，要看實作，下面讓我們看一個簡單的實作。
            ```java
                public void testRectangle(Rectangle r)
                {
                    r.setWidth(10);
                    r.setHeight(20);
                    assertTrue(r.getArea() == 200);
                }
            ```
            這個函式如果丟 Square 的物件就會錯，所以 Square 不是 Rectangle 的 SubType。
            
            Q : 那這有什麼重要性呢?
            A : 因為你不確定當初設計 Rectangle 的人，有沒有保證每個繼承 Rectangle 的類別都是 Rectangle 的 SubType ，你如果知道這點，就可以預期函式的正確性。
            Q : 那要怎麼改呢?
            A : 
            ```java
                public void testRectangle(Rectangle r)
                {
                    r.setWidth(width);
                    r.setHeight(height);
                    if(r instanceof Square)
                    {
                        assertTrue(r.getArea() == 400);
                    }
                    else
                    {
                        assertTrue(r.getArea() == 200);
                    }
                }
            ```
            但是這程式相當的醜，不好改，而且強迫使用者知道太多細節，這裡的繼承式不合適宜的。
            Q : 那什麼時候該繼承呢?
            A : **!大多數你使用繼承的地方都不該用繼承!**，因為 inheritance 的依賴關係是所有裡面最強的，而太過於依賴不是我們想要的，如何判斷由以下解釋。
* <font color="#92d1ff">**Conclusion**</font>
    **判斷 A 和 B 是不是 subType 關係， 如果不是 subType 關係你就不該繼承。**
    Q : 如何有效的判斷 subType 呢 ? 因為帶進去一個一個測有沒有 Error 很智障。
    A : 有的。以下 7 個規則為判斷標準，我們以一個例子解釋。
    SuperType <- Type <- SubType
    ```java
        class SuperType{}
        class Type extends SuperType{}
        class SubType extends Type{}
    ```
###    <font color="#98c37a">**規則一 : Cotravariance of arguement**</font>
**1. 當時實作或是繼承 SuperClass 的方法時，你的 input arguement 的數目是要一致的。**
**2. SubClass 的方法 input arguements 應該是 SuperClass 相對應的 (SuperType) arguement。**
            
:::success
**換句話說，因為 SubType 擁有的東西， SuperType 也擁有，所以如果 arguements 設為 SuperType ， SuperType, SubType 都可以 input。**
:::
*    **Example**
<br>
```java
class DemoClass 
{
    public int compute(Type t);
}
class DemoClassSub
{
    //1.方法的參數的數目一致
    //2.如果你要 Override 參數只能是 SuperType ，不能是 SubType
    //因為 SubType 有的，SuperType 不一定有。
    public int compute(SuperType st);
}
```
*    **Override arguement**
        Q : 如果要 override 所有 arguement 參數應該要同一個型態吧!?不然 compile 不會過。
        A : 沒錯，但是這只有 java 不會過，因為 java 是個強型別的語言， Liskov 是可以套用在各種語言上的。
###    <font color="#98c37a">**規則二 : Covariance of result**</font>
**1. 當實作或是繼承一個 SuperClass 的方法時，你方法 return 的數目是要一樣的。**
**2. SubClass 的方法 return Type 應該是 SuperClass 相對應的 return SubClass**
:::success
**老話一句，因為 SubType 擁有的東西， SuperType 也擁有，所以如果 return 設為 SubType ， 只有 SubType 可以使用 return 值。**
:::
*    **Example**
<br>
```java
class DemoClass 
{
    public Type compute(Type t)
    {
        return new Type();
    }
}
class DemoClassSub extends DemoClass
{
    //1.回傳的數目一致
    //2.如果你要 Override 回傳只能是 SubType ，不能是 SuperType
    //因為 SubType 有的，SuperType 不一定有。
    @override
    public Type compute(Type t)
    {
        return new SubType();
    }
}
```
*    **Exception rule**
跟 covariance of result 是同一件事，因為 exception 某種程度上也是一種 result。
<br>
```java
class DemoClass
{
    public compute() throws Type
{
    //...
}
    public void demo(DemoClass d)
    {
        try
        {
            //有機會拋出 Type Exception
            d.compute();
        }
        catch(Type t)//(Exception exp)
        {
            //可以抓到 Type 的所有子類
        }
    }
}    
class DemoSubClass
{
    @override
    public compute() throws SubType
    {
        //...
    }
    public void demo(DemoClass d)
    {
        try
        {
            //有機會拋出 SubType Exception
            d.compute();
        }
        catch(Type t)//(Exception exp)
        {
            //可以抓到 Type 的所有子類
        }
    }
}
```

###    **小小總結規則一、二**
SubType 的好處就是可以放心使用 polymorphism
```java
    Bird b = getBird();
    b.fly();
```
我們的目標是 ==**讓 Client 對於 Bird 這個類別的了解越少越好，不用去看所有鳥的 SubType 來決定 Bird 的程式該怎麼寫。**==
只要她回傳給我一定是 Bird 的 SubType 或是 exception 的 SubType，降低了依賴關係。
### <font color="#98c37a">**規則三 : Pre-condintion rule**</font>
>**Pre-condition : An assertion about the state of system before the method is called**
>**執行某方法前，必須先將使用到的類別初始化。**

>**Pre-condition rule : SubType 的方法所需要的 pre-condition 不能比 Type 的 pre-condition 嚴謹 (strict)。**
### <font color="#98c37a">**規則四 : Post-condintion rule**</font>
>**Post-condition : An assertion about the state of system after the method is called**
>**執行某方法後，某些指定類別物件不能為 null。**

>**Post-condition rule : SubType 的方法所需要的 post-condition 不能比 BaseType 的 post-condition 鬆散 (weak)。**
### **小小總結規則三、四**
這很直覺，因為 SubType 可以代替任何 Type 的地方
所以嚴謹程度
1. pre-condition : SubType > Type
2. post-condition : Type > SubType
###    <font color="#98c37a">規則五 : Invariant rule</font>
> **Invariant : Some assertion about a specific class property which is always true**
> **對於一個類別的永恆不變的法則**
*    **Example : a kind of Invariant of queue**
        number of elements in the queue <= capacity
> **Invariant rule : 一個 SubType 類別的 invariant 必續包含所有的 Type 的所有 invariant**        
###    <font color="#98c37a">規則六 : Constraint rule</font>
> **Constraint : Some assertion about how class property evloves overtime**
> **隨著時間的變化，對於一個類別的改變或是不改變**
*    **Example : a kind of Constraint of queue**
        capacity of queue never change after initialized
> **Constraint rule : 一個 SubType 類別的 Constraint 必續包含所有的 Type 的所有 Constraint**
### **小小總結規則五、六**
**Invariant vs Constraint**
* **Example**
    <br>
    ```java
    class MessageErrorDetector 
    {
        boolean isError = false;
        public void  processMessage(Message m)
        {
            if(m.findError())
            {
                isError = true;
            }
        }
        public boolean isErrorDetector()
        {
            return isError;
        }
    }
    ```
    `MessageErrorDetector` 類別，只要曾經找到 error 那每次 `isErrorDetector` 都會回傳 true。
    那 `MessageErrorDetector` 類別的 constraint : 只要偵測到 error，這個物件就會停在 error 的狀態。
    假設你今天繼承 `MessageErrorDetector` 寫了 `ResestableMessageErrorDetector`
    <br>
    ```java
        class ResestableMessageErrorDetector extends MessageErrorDetector
        {
            public void reset()
            {
                isError = false;
            }
        }
    ```
    你覺得這樣很合理，可是這樣就違反 LSP 規則六，因為只要偵測到 error，這個物件就會停在 error 的狀態這個 constraint 就不成立了，所以 `ResestableMessageErrorDetector` 不是 `MessageErrorDetector` 的 SubType。
* <font color="#92d1ff">**Review Definition**</font>
```java
    class Rectangle
    {
        int width;
        int height;
        public void setWidth(int width)
        {
            this.width = width;
        }
        public void setHeight(int height)
        {
            this.height = height;
        }
    } 
    class Square extends Rectangle
    {
         public void setWidth(int width)
         {
             this.width = width;
             this.height = height;
         }
         public void setHeight(int height)
         {
             this.height = height;
             this.width = width;
         }  
    }
```
- [x] 1. Contravariance of argument: 函式 argument
- [x] 2. Covariance of result: 函式都回傳 void 沒有拋出 Exception
- [x] 3. Pre-condition rule: 都沒有 pre-condition
- [x] 4. Post-condition rule: 都沒有 post-condition
- [x] 5. Invariant rule: 長方形沒有 Invariant 正方形的 Invariant 就是長寬一樣。
- [ ] 6. Constraint rule: 在 `Rectangle` 的 Contraint 包含了
    1. `setWidth` 時不可以動到`height`
    2. `setHeight`時不可以動到 `width` 
        正方形違反了這個 Constraint，所以正方形不是長方形的 SubType
* <font color="#92d1ff">**Conclusion**</font>
    大多數你繼承時，是不該繼承的，而如果你一定要繼承，用這幾條規則去確認它是不是 SubType ，用這幾條規則去確認。
## <font color="#F59F00">**ISP : Interface Segregation Principle (介面隔離原則)**</font>
* <font color="#92d1ff">**Definition**</font>
>**No client should be forced to depend on methods it does not use**
>**你不該依賴你根本用不到的東西**
*    **Example**
        一台全功能印表機 Xerox WorkCentre 可以，影印、傳真、掃描...
        <br>
        ```java
            interface IMultiFunction
            {
                public void print();
                public void scan();
                public void fax();
                public void copy();
            }
            class XeroxWorkCentre implements IMultFunction
            {
                public void print()
                {
                    //implement print
                }
                public void scan()
                {
                    //implement scan
                }
                public void fax()
                {
                    //implement fax
                }
                public void copy()
                {
                    //implement copy
                }
            }
        ```
* <font color="#92d1ff">**Problems & Resoultions**</font>
    Q : 如果今天來了一台傳真機 FaxMachine 呢?
    A : 
        <br>
    ```java
        interface IMultiFunction
        {
            public void print();
            public void scan();
            public void fax();
            public void copy();
        }
        class FaxMachine implements IMultFunction
        {
            public void print(){}
            public void scan(){}
            public void fax()
            {
                //implement fax
            }
            public void copy(){}
        }
    ```
    其他用不到的功能只能留白，這很不好。
    Q : 哪裡不好?
    A : 今天想使用 `FaxMachine` 的 Clinet 看到了你實作了 `IMuiltiFunction` 就以為這是個全功能的機器，使用者必續仔細去看 FaxMachine ， 才會知道 `print` 是沒用的，這樣對 code 的依賴感很重。
    我們先來看這兩個名詞 
    *    <font color="#98c37a">**Cohesion vs Coupling**</font>
            >**Cohesion : degree to which the various part of a software component are related**
            >**一個元件裡面，所有資料、方法之間的關聯性**
            
            >**Coupling : level of inter dependency between various software component**
            >**不同元件之間的依賴關係強度**
            
    我們使用 S.O.L.I.D 的目標是 ==**高內聚、低耦合**==
    所以回頭看 `IMultiFunction` 的 cohesion 很低，而且還違反了 SRP。
    Q : 那要怎麼改?
    A : 
    ```java
        interface IPrint
        {
            public void print();
        }
        interface IScan
        {
            public void scan();
        }
        interface IFax
        {
            public void fax();
        }
        interface ICopy
        {
            public void copy();
        }
    ```
    如果你要做一個影印+掃描的機器，你就讓該機器實作 IPrint + IScan 兩個不同的介面。
    ==**這也就是 Design Pattern(1) Strategy**==
* <font color="#92d1ff">**Conclusion**</font>
    ISP 就是認識極少化以及模組之間的訊息隱藏，我們只要看模組的依賴關係，就可以知道它可以做什麼和不可以做什麼。
## <font color="#F59F00">**DIP : Dependency Inversion Principle (反向依賴原則)**</font>
* <font color="#92d1ff">**Definition**</font>
    >**High-level modules should not depend on low-level modules.
    Both should depends on abstractions.**

    >**Abstractions should not depends on details.
    Details should depend on abstractions**

    這個規則也就是 OCP 的 Conclusion
    <br><span style="display:block;text-align:center;"><img src="https://i.imgur.com/RGuW71a.png" width="450" height="280"></img></span><br>
    依賴關係因為 `CheesePizza` 實作介面 `Pizza` 而反向了，所以稱為 **反向依賴**
    
    應用 DIP 時，我們傾向忽略作業系統和平台機制的穩定背景，我們可以忽略那些具體之間的依賴關係，因為我們信任他們不會改變， ==**我們要避免依賴的是系統中容易變化的具體元素**==，他們正是我在發展的模組。
* **Example**
    ```java
        interface ServiceFactory
        {
            abstract ConcreteImpl makeService();
        }
        class ServiceFactoryImpl extends ServiceFactory
        {
            makeService()
            {
                //...
            }
        }
        interface Service
        {
            //abstractMethods
        }
        class ConcreteImpl extends Service
        {
            //concreteImpl
        }
        class Application
        {
            public Application()
            {
                ServiceFactory serviceFactory = new ServiceFactory();
                Service service = serviceFactory.makeService();
            }
        }
    ```
    
    <br><span style="display:block;text-align:center;"><img src="https://i.imgur.com/Ab99HQk.png" width="600" height="400"></img></span><br>
    圖中的虛線是一個 ==**架構邊界** 把 abstract 和 concrete 分開==
    再具體元件中出現了一個依賴關係，違反了 DIP，也因此違反了 OCP ，但這是很常發生的，
    **違反原則的這些行為無法完全消除，但可以將其收集到少量的具體元件中，並將系統的其他部分區別開來。**