蝸牛君漫聊設計模式---建造者模式

在這裏插入圖片描述

Hello~ 大家好,我是蝸牛君,我們又見面了。今天依然是蝸牛君漫聊設計模式系列中的文章—建造者模式。咱們舉一個現實中的例子,可能沒那麼完全的恰當,但是基本的含義還是能夠表達清楚的。作爲一名職場人士,繳納五險一金是每個職場中人的必備屬性。用我們熟悉的互聯網公司來舉例,正規的公司會給員工繳納標準的五險一金,實力雄厚的大廠會爲員工繳納六險一金甚至七險一金,而有的初創公司或者想法剋扣員工利益的公司只給員工繳納五險不繳納公積金,或者上三險,最差的是五險一金一點都不給繳納,蝸牛君剛來北京時的第一家公司(在居民樓裏辦公,俗稱小作坊)就是這種情況,不給繳納社保的原因是試用期不給繳納,轉正後給繳納(這違反了勞動法)。那麼建造者模式和繳納五險一金有啥關係呢?正是因爲這些不正規公司的存在,他們的這一特性才和建造者模式的核心思想搭上邊。

01

建造者模式的定義

將一個複雜對象的構建與它的表示分離開,使得同樣的構建過程可以創建不同的表示。

建造者模式的核心元素
  • Product產品類:基本的業務邏輯。
  • Builder抽象建造者:規範產品的組建,一般由子類實現。
  • ConcreteBuilder具體建造者:實現抽象建造者的所有方法,並返回一個組建好的產品。
  • Director導演類:負責安排已有模塊的順序,然後告訴Builder開始建造。

02

標準版建造者模式的實現

第一步,確定我們的業務類,就是員工,繳納五險一金是每個員工的公共屬性,因此我們可以創建員工抽象類,定義這些公共屬性。
public abstract class Employee {
    private List<String> sequence;

    /**
     * 從工資中扣除 失業保險
     */
    protected abstract void deductUnemploymentInsurance();

    /**
     * 從工資中扣除 養老保險
     */
    protected abstract void deductEndowmentInsurance();

    /**
     * 從工資中扣除 醫療保險
     */
    protected abstract void deductHospitalizationInsurance();

    /**
     * 從工資中扣除 工傷保險
     */
    protected abstract void deductEmploymentInjuryInsurance();

    /**
     * 從工資中扣除 生育保險
     */
    protected abstract void deductMaternityInsurance();

    /**
     * 從工資中扣除 住房公積金
     */
    public abstract void deductHousingProvidentFund();

    /**
    * 設置需要繳納的項目及其順序
    */
    public void setSequence(List<String> sequence) {
        this.sequence = sequence;
    }

    /**
     * 執行最後的支付操作
     */
    public void payment() {
        if (sequence != null && sequence.size() != 0) {
            for (String title : sequence) {
                if ("UnemploymentInsurance".equalsIgnoreCase(title)) {
                    deductUnemploymentInsurance();
                } else if ("EndowmentInsurance".equalsIgnoreCase(title)) {
                    deductEndowmentInsurance();
                } else if ("HospitalizationInsurance".equalsIgnoreCase(title)) {
                    deductHospitalizationInsurance();
                } else if ("EmploymentInjuryInsurance".equalsIgnoreCase(title)) {
                    deductEmploymentInjuryInsurance();
                } else if ("MaternityInsurance".equalsIgnoreCase(title)) {
                    deductMaternityInsurance();
                } else if ("HousingProvidentFund".equalsIgnoreCase(title)) {
                    deductHousingProvidentFund();
                }
            }
        } else {
            System.out.println("這個公司不遵守勞動法,去告他!");
        }
    }
}
  • 第一點:我們定義了員工五險一金共六個屬性,這六個業務方法會在子類中實現,也就是說這六個業務方法的定義與實現都在業務類中完成了。
  • 第二點:我們定義了具體繳納哪些費用的方法,通過一個由外界傳遞進來的集合決定員工能繳納哪些社保項目並且還能設置繳納的順序,而具體的業務細節在業務抽象類中實現了。
  • 總結:以上兩點就是業務抽象類的具體作用,所有與業務相關的細節都在這個抽象類中定義好了並實現了部分業務邏輯。
第二步,定義我們的業務實現類,實現每個業務功能細節。
public class EmployeeA extends Employee {
    private String name;

    public EmployeeA(String name) {
        this.name = name;
    }

    @Override
    protected void deductUnemploymentInsurance() {
        // 實現業務細節
        System.out.println("姓名:" + name + ",已扣除失業保險");
    }

    @Override
    protected void deductEndowmentInsurance() {
        // 實現業務細節
        System.out.println("姓名:" + name + ",已扣除養老保險");
    }

    @Override
    protected void deductHospitalizationInsurance() {
        // 實現業務細節
        System.out.println("姓名:" + name + ",已扣除醫療保險");
    }

    @Override
    protected void deductEmploymentInjuryInsurance() {
        // 實現業務細節
        System.out.println("姓名:" + name + ",已扣除工傷保險");
    }

    @Override
    protected void deductMaternityInsurance() {
        // 實現業務細節
        System.out.println("姓名:" + name + ",已扣除生育保險");
    }

    @Override
    public void deductHousingProvidentFund() {
        // 實現業務細節
        System.out.println("姓名:" + name + ",已扣除住房公積金");
    }
}
  • 總結:關於功能業務方面的代碼我們都寫完了,業務類的功能就是實現業務邏輯,其實這裏我們應用了另一個設計模式—模板方法模式。
第三步,創建Builder抽象類,即建造者類。在上一步中,業務相關的功能我們都做完了,下面需要根據不同的情況對業務進行組裝,這就是Builder類的功能。
public abstract class Company {
    /**
     * 給員工繳納哪些社保
     *
     * @param sequence
     */
    public abstract void setSequence(List<String> sequence);

    /**
     * 給該員工繳納社保
     *
     * @return
     */
    public abstract Employee getEmployee();
}
第四步,創建Builder實現類。在Builder實現類中對員工的社保福利進行操。
public class CompanyA extends Company {
    private Employee employee;

    public CompanyA(String name) {
        // 關鍵點一
        employee = new EmployeeA(name);
    }
    
    @Override
    public void setSequence(List<String> sequence) {
        // 關鍵點二
        employee.setSequence(sequence);
    }
    
    @Override
    public Employee getEmployee() {
        // 關鍵點三
        return employee;
    }
}
  • 關鍵點一解讀:創建一個需要繳納社保的員工;
  • 關鍵點二解讀:設置這個員工享受的社保福利項目;
  • 關鍵點三解讀:獲取這名享受社保福利的員工對象;
第五步,終於到了發工資的時候,發工資前要扣除五險一金所佔去的金額並進行繳納。
public class Client {
    public static void main(String[] args) {
        // A公司的員工---蝸牛君
        Company company = new CompanyA("蝸牛君");
        
        List<String> sequence = new ArrayList<>();
        // 給蝸牛君繳納 失業保險
        sequence.add("UnemploymentInsurance");
        // 給蝸牛君繳納 養老保險
       sequence.add("EndowmentInsurance");
       // 給蝸牛君繳納 醫療保險
       sequence.add("HospitalizationInsurance");
       // 給蝸牛君繳納 工傷保險
       sequence.add("EmploymentInjuryInsurance");
       // 給蝸牛君繳納 生育保險
       sequence.add("MaternityInsurance");
       // 給蝸牛君繳納 住房公積金
       sequence.add("HousingProvidentFund");
       company.setSequence(sequence);
       
       // 獲取蝸牛君實體類
       Employee employeeA = company.getEmployee();
       // 進行繳納
       employeeA.payment();
    }
}
Log日誌
姓名:蝸牛君,已扣除失業保險
姓名:蝸牛君,已扣除養老保險
姓名:蝸牛君,已扣除醫療保險
姓名:蝸牛君,已扣除工傷保險
姓名:蝸牛君,已扣除生育保險
姓名:蝸牛君,已扣除住房公積金
  • 總結:五險一金按時繳納了,工資也到手了,一個月的辛苦有了回報,心裏美滋滋的。這家公司在員工福利方面還是很正規的。

03

坑人情況一

由於公司經營不善,資金鍊出現了斷裂,員工的福利無法按時足額的繳納,公司表示先給上五險,公積金後面有錢了再給補,讓我們看看程序如何表示:

public class Client {
    public static void main(String[] args) {
        // A公司的員工---蝸牛君
        Company company = new CompanyA("蝸牛君");
        
        List<String> sequence = new ArrayList<>();
        // 給蝸牛君繳納 失業保險
        sequence.add("UnemploymentInsurance");
        // 給蝸牛君繳納 養老保險
       sequence.add("EndowmentInsurance");
       // 給蝸牛君繳納 醫療保險
       sequence.add("HospitalizationInsurance");
       // 給蝸牛君繳納 工傷保險
       sequence.add("EmploymentInjuryInsurance");
       // 給蝸牛君繳納 生育保險
       sequence.add("MaternityInsurance");
       // 給蝸牛君繳納 住房公積金
       // sequence.add("HousingProvidentFund");
       company.setSequence(sequence);
       
       // 獲取蝸牛君實體類
       Employee employeeA = company.getEmployee();
       // 進行繳納
       employeeA.payment();
    }
}
Log日誌
姓名:蝸牛君,已扣除失業保險
姓名:蝸牛君,已扣除養老保險
姓名:蝸牛君,已扣除醫療保險
姓名:蝸牛君,已扣除工傷保險
姓名:蝸牛君,已扣除生育保險
  • 總結:我們直接將公積金這一項刪除就能拼裝出新的福利模型。

坑人情況二

公司沒錢了,員工福利五險一金改爲三險(因爲三險是勞動法規定必須繳納的)並且先給繳納失業保險,再給繳納醫療保險,最後繳納養老保險,爲什麼是這個順序?因爲養老保險錢最多,當然實際情況中是不能這樣繳納的,這裏是爲了體現建造者模式的特點:順序性。

public class Client {
    public static void main(String[] args) {
        // A公司的員工---蝸牛君
        Company company = new CompanyA("蝸牛君");
        
        List<String> sequence = new ArrayList<>();
        // 給蝸牛君繳納 失業保險
        sequence.add("UnemploymentInsurance");
        // 給蝸牛君繳納 醫療保險
       sequence.add("HospitalizationInsurance");
       // 給蝸牛君繳納 養老保險
       sequence.add("EndowmentInsurance");
       // 給蝸牛君繳納 工傷保險
       // sequence.add("EmploymentInjuryInsurance");
       // 給蝸牛君繳納 生育保險
       // sequence.add("MaternityInsurance");
       // 給蝸牛君繳納 住房公積金
       // sequence.add("HousingProvidentFund");
       company.setSequence(sequence);
       
       // 獲取蝸牛君實體類
       Employee employeeA = company.getEmployee();
       // 進行繳納
       employeeA.payment();
    }
}
Log日誌
姓名:蝸牛君,已扣除失業保險
姓名:蝸牛君,已扣除醫療保險
姓名:蝸牛君,已扣除養老保險
總結

通過以上三種情況,充分體現出了建造者模式的優勢:靈活。因爲在業務類中功能相關的點都完成了並且每個業務點是獨立的,這就相當於一些零件。而建造者類中可以根據具體情況任意的調用業務類中的零件進行組合拼裝,最後輸出一個成品。用一句話描述就是,在業務功能點完成的情況下,通過對業務點任意的拼裝最後輸出不同的產品。

04

進一步完善建造者模式

蝸牛君所在的公司是一個集團公司下面的子公司,而這家集團公司下面有多家子公司,每個子公司的社保繳納項目是固定的,因此我們可以在封裝一層集團公司,即Director。

我們在創建一個子公司B
public class CompanyB extends Company {
    private Employee employee;

    public CompanyB(String name) {
        employee = new EmployeeB(name);
    }

    @Override
    public void setSequence(List<String> sequence) {
        employee.setSequence(sequence);
    }

    @Override
    public Employee getEmployee() {
        return employee;
    }
}
定義一個Director類,這個類用來規定已有模塊的順序
public class Director {
    private List<String> sequence = new ArrayList<>();
    // 蝸牛君所在公司
    private Company companyA = new CompanyA("蝸牛君");
    // 蝸牛君的弟弟所在公司
    private Company companyB = new CompanyB("蝸牛君的弟弟");

    // 蝸牛君所在公司的繳費項目及順序:全額繳納五險一金
    public Employee getCompanyAEmployee() {
        sequence.clear();
        sequence.add("UnemploymentInsurance");
        sequence.add("EndowmentInsurance");
        sequence.add("HospitalizationInsurance");
        sequence.add("EmploymentInjuryInsurance");
        sequence.add("MaternityInsurance");
        sequence.add("HousingProvidentFund");
        companyA.setSequence(sequence);
        return companyA.getEmployee();
    }

    // 蝸牛君的弟弟所在公司的繳費項目及順序:只繳納三險一金
    public Employee getCompanyBEmployee() {
        sequence.clear();
        sequence.add("MaternityInsurance");
        sequence.add("EndowmentInsurance");
        sequence.add("HospitalizationInsurance");
        sequence.add("HousingProvidentFund");
        companyB.setSequence(sequence);
        return companyB.getEmployee();
    }
}
終於又到了發工資的時候了,先繳納社保吧
public class Client {
    public static void main(String[] args) {
        // 創建集團公司
        Director director = new Director();
        // 給蝸牛君繳納五險一金
        Employee employeeA = director.getCompanyAEmployee();
        employeeA.payment();
        // 給蝸牛君的弟弟繳納三險一金
        Employee employeeB = director.getCompanyBEmployee();
       employeeB.payment();
    }
}
Log日誌
姓名:蝸牛君,已扣除失業保險
姓名:蝸牛君,已扣除養老保險
姓名:蝸牛君,已扣除醫療保險
姓名:蝸牛君,已扣除工傷保險
姓名:蝸牛君,已扣除生育保險
姓名:蝸牛君,已扣除住房公積金

姓名:蝸牛君的弟弟,已扣除失業保險
姓名:蝸牛君的弟弟,已扣除養老保險
姓名:蝸牛君的弟弟,已扣除醫療保險
姓名:蝸牛君的弟弟,已扣除住房公積金

05

總結

不知道大家通過上面的現實例子有沒有體會到建造者模式的核心思想,在這裏我給大家再次簡單的總結一下

第一點:所有業務功能都在業務類中完成並且獨立,其他的類中都沒有涉及業務實現相關的代碼,這些業務點可比作零件。

第二點:Builder類對已有業務點進行拼裝,雖然業務點的實現邏輯一樣,但是通過組裝時不同的順序不同的選項最後輸出不同的產品。而且各個拼裝類又相互獨立,易於擴展。

第三點:建造者模式的核心要義是:拼裝時,順序不同,零件不同,最後輸出不同的產品。


在這裏插入圖片描述

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