GAS 軟體設計

https://hackmd.io/@zxcj04/GAS_SD

Interface

  • 「統一的」、「標準的」規格
  • 介面 USB 插座
    隨身碟 吹風機
    光碟機 吸塵器

Abstraction

只描述一個大概的流程跟邏輯,真正實現的方法交由底下各個裝置去做

interface USBInterface
{
 
    /** 裝置開機 */
    public function boot();
     
    /** 裝置是否有連線 */
    public function isConnected();
     
    /** 裝置連線不成功的錯誤訊息 */
    public function getErrorMessage();
     
    /** 裝置取得電源 */
    public function getPower();
 
    /** 裝置取得需要使用到的資料 */
    public function getData();
 
    /** 裝置把資料儲存起來 */
    public function saveData();
     
    /** 裝置關機 */
    public function shutdown();
}

實做

class Mouse implements USBInterface
{
    /** 實作 USB 滑鼠的開啟方法 */
    public function boot()
    {
        if ( $this->isBoot() ) {
            return "滑鼠開啟成功";
        } else {
            $this->bootRetry();
        }
    }
     
    /** 實作其他方法 (略) */
    ...
}
 
class Keyboard implements USBInterface
{
    /** 實作 USB 鍵盤的開機方法 */
    public function boot()
    {
        if ( $this->bluetoothConnect() ) 
        {
            return "已連線到藍芽鍵盤";
                     
        } else {
         
            if ( $this->bootKeyboard() ) 
            {
                return "鍵盤開啟成功"   
            }
 
        }
    }
 
    /** 實作其他方法 (略) */
    ...
}

耦合 內聚

  • 耦合(Coupling)
    • 兩個模組間的關連性或相依性
    • 一個很小的需求異動,但是連帶影響到跟它有相依關係的部份
  • 內聚(Cohesion)
    • 模組本身不需依賴其他模組,就能完成工作
  • 低耦合、高內聚的目的,就是為了提升各模組功能的重用性、擴展性、維護性

降低耦合性 提高內聚力

SOLID

  • 單一職責原則(Single responsibility principle, SRP)
  • 開放封閉原則(Open-close principle, OCP)
    • open for extension
    • closed for modification
  • 里氏替換原則(Liskov substitution principle, LSP)
  • 接口隔離原則(Interface segregation principle, ISP)
  • 依賴反轉原則(Dependency inversion principle, DIP)

Bad Smell

Duplicated Code

  • Modifiability(修改性)
  • Understandability(理解性)
  • Testability(可測性)
  • Resource usage(資源使用)

Long Method(過長函數)

  • Explanation(解釋)
    • method 做了太多事
    • long method 降低 understandability
  • Sharing(共享)
    • 即使其中一段程式碼有被重複使用的機會 但因為一個 method 作太多事 因此 reusability 降低
  • Modifiability(可修改性)
  • Testability(可測性)

Large Class(過大類別)

  • 參考 Long Method

Long Parameter List(過長參數列)

  • Hard to understand(難以理解)
  • Inconsistent(不一致)
  • Difficult to use(難以使用)
  • Interface change(介面改變)
    • 有著過長參數列的函數 只要任何一個參數改變 或是需要增加新的參數 都會造成函數介面改變

Divergent Change(發散式修改)

  • 一個類別會因為多種不同的原因而造成其改變 違反了 Single Responsibility Principle(SRP)
  • Modifiability(修改性)
  • Understandability(可理解性)
  • Testability(可測性)
  • Reusability(重複使用性)

Shotgun Surgery(霰彈式修改)

  • 某種修改會跨越多個類別 與 divergent change 剛好相反 某種責任分散在多個類別身上
  • 大部份發生在 duplicated code 上

Feature Envy(特性依戀)

example

一本電話簿,裡面有一些聯絡人,聯絡人存著姓名、email,與電話。每當我需要時,就要得到特定格式的『聯絡人清單』。

// Contact.java
public class Contact {

    private String name;
    private String email;
    private String phoneNumber;

    public Contact(String name, String email, String phoneNumber) {
        this.name = name;
        this.email = email;
        this.phoneNumber = phoneNumber;
    }

    // ...後略
}

// PhoneBook.java
public class PhoneBook {
    List<Contact> contacts;

    public PhoneBook() {
        this.contacts = new ArrayList<>();
    }

    public String generateFormattedPrint(){
        String result = "";
        for (Contact contact : contacts){

            result += contact.getName() + ": ";
            result += contact.getEmail() + " | ";
            result += contact.getPhoneNumber() + ". ";
            result += "\n";
        }
        return result;
    }

    // ...後略
}

類別 PhoneBook 過度依賴 Contact 這個類別
它裡面使用的method全部都是Contact提供的
這件事情嚴重地違反了物件導向的單一職責原則(SRP)

若是聯絡人的內容與輸出格式一旦有換 需要同時更動 Contact 和 PhoneBook

應該修改成

// Contact.java
public class Contact {
    // ...前略
    public String generateFormattedPrint(){
        String result = name + ": ";
        result += email + " | ";
        result += phoneNumber + ". ";
        return result;
    }
}

// PhoneBook.java
public class PhoneBook {
    List<Contact> contacts;

    public PhoneBook() {
        this.contacts = new ArrayList<>();
    }

    public String generateFormattedPrint(){
        String result = "";
        for (Contact contact : contacts){

            result += contact.generateFormattedPrint();
            result += "\n";
        }
        return result;
    }
    // ...後略
}