常用設計模式之白話精簡理解及應用-下

itxiaoshen發表於2022-01-12

工廠模式

上一篇我們先學習了單例和模板方法兩個設計模式,單例模式在JDK中java.lang.Runtime使用餓漢式還有Spring從單例池獲取bean的方法getSingleton,Spring解決迴圈依賴問題的核心也是採用三級快取機制(三個Map儲存Bean),接下來我們繼續學習其他常見設計模式。

概述

  • 工廠模式將物件的建立和使用分開,即應用程式將物件的建立和初始化職責交給工廠物件;使用者不需要知道具體的建立過程,只需要使用即可。將例項化物件提取出來,放到一個類中統一維護和管理,從而達到解耦、提高專案的擴充套件性和維護性。
  • 降低程式碼重複,而且相比每次通過複雜的建構函式來建立初始化更容易使用。

程式碼示例

簡單工廠

簡單工廠不符合開閉原則,也不屬於設計模式之一,但是在一些簡單場景下還是比較實用的。

動物介面Animal.java

package cn.itxs.pattern.factory;

public interface Animal {
    void showAnimal();
}

羊Sheep.java

package cn.itxs.pattern.factory;

public class Sheep implements Animal{
    @Override
    public void showAnimal() {
        System.out.println("我是一隻可愛的小綿羊!");
    }
}

鴨子Duck.java

package cn.itxs.pattern.factory;

public class Duck implements Animal{
    @Override
    public void showAnimal() {
        System.out.println("我是一隻調皮的小鴨子!");
    }
}

動物生產工廠AnimalFactory.java,通過傳入生產動物名返回對應動物物件

package cn.itxs.pattern.factory;

public class AnimalFactory {
    public Animal createAnimal(String name){
        Animal animal = null;
        if ( "鴨子".equals(name) ){
            animal =  new Duck();
        }else if ( "羊".equals(name) ){
            animal = new Sheep();
        }
        return animal;
    }
}

測試類

package cn.itxs.pattern.factory;

public class FactoryMain {
    public static void main(String[] args) {
        AnimalFactory animalFactory = new AnimalFactory();
        Animal animal = animalFactory.createAnimal("羊");
        animal.showAnimal();
    }
}

工廠方法

通過將動物生產工廠抽象為農場介面,以後擴充則只需要新增相應的工廠和動物類,不需要修改原有,符合開閉原則。

農場介面Farm.java

package cn.itxs.pattern.factory;

public interface Farm {
    Animal createAnimal();
}

生產鴨子農場DuckFarm.java

package cn.itxs.pattern.factory;

public class DuckFarm implements Farm{
    @Override
    public Animal createAnimal() {
        return new Duck();
    }
}

生產羊農場SheepFarm.java

package cn.itxs.pattern.factory;

public class SheepFarm implements Farm{
    @Override
    public Animal createAnimal() {
        return new Sheep();
    }
}

測試類

package cn.itxs.pattern.factory;

public class FactoryMain {
    public static void main(String[] args) {
        Farm farm = new DuckFarm();
        Animal animal= farm.createAnimal();
        animal.showAnimal();
    }
}

抽象工廠

抽象工廠也屬於設計模式的一種,比如所有農場還要生產植物,且植物也有不同種類,比如農場即要生產羊還要生產番茄,抽象工廠就可以派上用場;而如果抽象工廠只有一個產品簇的維度則就退化為工廠方法。

植物介面Plant.java

package cn.itxs.pattern.factory;

public interface Plant {
    void showPlant();
}

玉米類Plant.java

package cn.itxs.pattern.factory;

public class Corn implements Plant{
    @Override
    public void showPlant() {
        System.out.println("我是一顆玉米!");
    }
}

番茄類Plant.java

package cn.itxs.pattern.factory;

public class Tomato implements Plant{
    @Override
    public void showPlant() {
        System.out.println("我是一顆番茄!");
    }
}

農場介面Farm.java

package cn.itxs.pattern.factory;

public interface Farm {
    Animal createAnimal();
    Plant createPlant();
}

深圳農場SZFarm.java生產鴨子和玉米

package cn.itxs.pattern.factory;

public class SZFarm implements Farm {
    @Override
    public Animal createAnimal() {
        return new Duck();
    }

    @Override
    public Plant createPlant() {
        return new Corn();
    }
}

廣州農場GZFarm.java生產羊和番茄

package cn.itxs.pattern.factory;

public class GZFarm implements Farm {
    @Override
    public Animal createAnimal() {
        return new Sheep();
    }

    @Override
    public Plant createPlant() {
        return new Tomato();
    }
}

測試類

package cn.itxs.pattern.factory;

public class FactoryMain {
    public static void main(String[] args) {
        Farm farm = new GZFarm();
        Animal animal= farm.createAnimal();
        animal.showAnimal();
        Plant plant = farm.createPlant();
        plant.showPlant();
    }
}

建造者模式

概述

  • 建造者模式將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示。
  • 建造者模式和工廠模式較相似,區別在於建造者模式是一種個性化產品的建立,而工廠模式則是一種標準化產品的建立;建造者模式注重的是方法順序,工廠模式注重是建立物件;當創造一個物件需要很多步驟時 , 適合使用建造者模式 ;當創造一個物件只需要一個簡單的方法就可以完成 , 適合使用工廠模式 。
  • 使用場景
    • 需要生成的產品物件有複雜的內部結構,這些產品物件通常包含多個成員屬性且有些是可選的引數。
    • 需要生成的產品物件的屬性相互依賴,需要指定其生成順序。
    • 隔離複雜物件的建立和使用,並使得相同的建立過程可以建立不同的產品。

程式碼示例

簡單Builder

類似如Lombok的@Builder,JDK中的StringBuilder;如果建立的產品種類只有一種,只需要一個具體建造者,這時可以省略掉抽象建造者,甚至還可以省略掉指揮者角色

使用者類User.java

package cn.itxs.pattern.builder;

public class User {
    private int id;
    private String name;
    private int age;
    private String homeAddress;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getHomeAddress() {
        return homeAddress;
    }

    public void setHomeAddress(String homeAddress) {
        this.homeAddress = homeAddress;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", homeAddress='" + homeAddress + '\'' +
                '}';
    }
}

使用者建造者類UserBuilder.java

package cn.itxs.pattern.builder;

public class UserBuilder {
    private User user = new User();

    public UserBuilder id(int id){
        user.setId(id);
        return this;
    }

    public UserBuilder name(String name){
        user.setName(name);
        return this;
    }

    public UserBuilder age(int age){
        user.setAge(age);
        return this;
    }

    public UserBuilder homeAddress(String homeAddress){
        user.setHomeAddress(homeAddress);
        return this;
    }

    public User build(){
        return user;
    }
}

測試類

package cn.itxs.pattern.builder;

public class BuilderMain {
    public static void main(String[] args) {
        User user = new UserBuilder().id(1).name("張三").age(25).homeAddress("海天一路").build();
        System.out.println(user);
    }
}

建造者模式和setter方法區別在於setter是先new出來物件,而建造者模式只會在最後build之後才能使用物件。

建造者

臥室類BedRoom.java

package cn.itxs.pattern.builder;

public class BedRoom {
    private String bed;
    private String light;
    private String dresser;

    public void setBed(String bed) {
        this.bed = bed;
    }

    public void setLight(String light) {
        this.light = light;
    }

    public void setDresser(String dresser) {
        this.dresser = dresser;
    }

    public void display(){
        System.out.println("BedRoom{" +
                "bed='" + bed + '\'' +
                ", light='" + light + '\'' +
                ", dresser='" + dresser + '\'' +
                '}');
    }
}

抽象裝修工類Decorator.java

package cn.itxs.pattern.builder;

public abstract class Decorator {
    protected BedRoom bedRoom = new BedRoom();

    abstract void buildBed();
    abstract void buildLight();
    abstract void buildDresser();

    public BedRoom builder(){
        return bedRoom;
    }
}

豪華裝修工人LuxuryDecorator.java

package cn.itxs.pattern.builder;

public class LuxuryDecorator extends Decorator{
    @Override
    void buildBed() {
        bedRoom.setBed("豪華大床");
    }

    @Override
    void buildLight() {
        bedRoom.setLight("豪華吊燈");
    }

    @Override
    void buildDresser() {
        bedRoom.setDresser("豪華梳妝檯");
    }
}

簡單輕裝修工LightDecorator.java

package cn.itxs.pattern.builder;

public class LightDecorator extends Decorator{
    @Override
    void buildBed() {
        bedRoom.setBed("普通床");
    }

    @Override
    void buildLight() {
        bedRoom.setLight("普通照明燈");
    }

    @Override
    void buildDresser() {
        bedRoom.setDresser("普通梳妝檯");
    }
}

裝修指揮者DesignerManager.java

package cn.itxs.pattern.builder;

public class DesignerManager {
    private Decorator decorator;

    public DesignerManager(Decorator decorator) {
        this.decorator = decorator;
    }

    public BedRoom decorate(){
        decorator.buildBed();
        decorator.buildLight();
        decorator.buildDresser();
        return decorator.builder();
    }
}

測試類

package cn.itxs.pattern.builder;

public class BuilderMain {
    public static void main(String[] args) {
        Decorator decorator = new LuxuryDecorator();
        DesignerManager designerManager = new DesignerManager(decorator);
        BedRoom bedRoom = designerManager.decorate();
        bedRoom.display();
    }
}

策略模式

概述

  • 策略模式定義了演算法族,分別封裝起來,讓他們之間可以互相替換,此模式讓演算法的變化獨立於使用演算法的客戶;將變化的部分抽離出來,組合進類中,根據不同的子類,可以設定為不同的行為子類實現動態改變行為的目的。
  • 常見實現方式有兩種
    • 自己決定策略(具體策略角色)、交給執行人(環境角色)去執行(執行人不去決定執行策略)比如計算器加減乘除策略
    • 自己不懂策略(具體策略角色)、交給執行人(環境角色)去執行(執行人懂得決定策略) -比如本篇的出行方式選擇
  • 使用場景
    • 一個系統需要動態地在幾種演算法中選擇一種時,可將每個演算法封裝到策略類中。
    • 一個類定義了多種行為,並且這些行為在這個類的操作中以多個條件語句的形式出現,可將每個條件分支移入它們各自的策略類中以代替這些條件語句。
    • 系統中各演算法彼此完全獨立,且要求對客戶隱藏具體演算法的實現細節時。
    • 系統要求使用演算法的客戶不應該知道其操作的資料時,可使用策略模式來隱藏與演算法相關的資料結構。
    • 多個類只區別在表現行為不同,可以使用策略模式,在執行時動態選擇具體要執行的行為。

程式碼示例

旅行方式介面TravelStrategy.java

package cn.itxs.pattern.strategy;

public interface TravelStrategy {
    void tripMode();
    boolean isFeasible(int mileage);
}

公交車出行BusStrategy.java

package cn.itxs.pattern.strategy;

public class BusStrategy implements TravelStrategy{
    @Override
    public void tripMode() {
        System.out.println("使用公交車出行");
    }

    @Override
    public boolean isFeasible(int mileage) {
        if (mileage < 30){
            return true;
        }
        return false;
    }
}

汽車出行CarStrategy.java

package cn.itxs.pattern.strategy;

public class CarStrategy implements TravelStrategy{
    @Override
    public void tripMode() {
        System.out.println("使用汽車出行");
    }

    @Override
    public boolean isFeasible(int mileage) {
        if (mileage>= 30 && mileage < 500){
            return true;
        }
        return false;
    }
}

高鐵出行TrainStrategy.java

package cn.itxs.pattern.strategy;

public class TrainStrategy implements TravelStrategy{
    @Override
    public void tripMode() {
        System.out.println("使用高鐵出行");
    }

    @Override
    public boolean isFeasible(int mileage) {
        if (mileage >= 500 && mileage < 1000){
            return true;
        }
        return false;
    }
}

飛機出行AirPlaneStrategy.java

package cn.itxs.pattern.strategy;

public class AirPlaneStrategy implements TravelStrategy{
    @Override
    public void tripMode() {
        System.out.println("使用飛機出行");
    }

    @Override
    public boolean isFeasible(int mileage) {
        if (mileage > 1000){
            return true;
        }
        return false;
    }
}

出行決策環境類BusStrategy.java,由出行方式環境類來決策出行方式

package cn.itxs.pattern.strategy;

import java.util.ArrayList;
import java.util.List;

public class TravelContext {
    private List<TravelStrategy> travelStrategies;

    public TravelContext() {
        this.travelStrategies = new ArrayList<>();
        travelStrategies.add(new BusStrategy());
        travelStrategies.add(new CarStrategy());
        travelStrategies.add(new TrainStrategy());
        travelStrategies.add(new AirPlaneStrategy());
    }

    public void trip(int mileage){
        for (TravelStrategy travelStrategy : travelStrategies) {
            if (travelStrategy.isFeasible(mileage)){
                travelStrategy.tripMode();
                break;
            }
        }
    }
}

測試類

package cn.itxs.pattern.strategy;

public class StrategyMain {
    public static void main(String[] args) {
        TravelContext travelContext = new TravelContext();
        travelContext.trip(800);
    }
}

狀態模式

概述

  • 狀態模式允許物件在內部狀態改變時改變它的行為,物件看起來好像修改了它的類。
  • 狀態模式可以有效的替換充滿在程式中的if else語句:將不同條件下的行為封裝在一個類裡面,再給這些類一個統一的父類來約束他們。
  • 狀態模式策略模式很相似,也是將類的"狀態"封裝了起來,在執行動作時進行自動的轉換,從而實現,類在不同狀態下的同一動作顯示出不同結果;使用的目的是不同的——策略模式用來處理演算法變化,而狀態模式則是處理狀態變化;策略模式中,演算法是否變化完全是由客戶程式決定的,而且往往一次只能選擇一種演算法,不存在演算法中途發生變化的情況。
  • 使用場景
    • 一個物件的行為取決於它的狀態,並且它必須在執行時根據狀態改變它的行為。
    • 程式碼中包含大量與物件狀態有關的條件語句,例如,一個操作中含有龐大的分支語句 (if-else / switch-case) , 且這些分支依賴於該物件的狀態。

程式碼示例

公司績效考核結果狀態,績效考核狀態抽象類PerformanceState.java

package cn.itxs.pattern.state;

abstract class PerformanceState {
    protected PerformanceContext pc;
    protected String stateName;
    protected int score;

    public abstract void checkState();

    public void addScore(int score){
        this.score += score;
        System.out.println("當前分數為:" + this.score);
        checkState();
        System.out.println("當前狀態:"+pc.getPerformanceState().stateName);
    }
}

績效考核環境類PerformanceContext.java

package cn.itxs.pattern.state;

public class PerformanceContext {
    private PerformanceState performanceState;

    public PerformanceContext() {
        performanceState = new DisqualificationState(this);
    }

    public PerformanceState getPerformanceState() {
        return performanceState;
    }

    public void setPerformanceState(PerformanceState performanceState) {
        this.performanceState = performanceState;
    }

    public void add(int score){
        this.performanceState.addScore(score);
    }
}

績效不合格狀態DisqualificationState.java

package cn.itxs.pattern.state;

public class DisqualificationState extends PerformanceState{

    public DisqualificationState(PerformanceContext performanceContext) {
        pc = performanceContext;
        stateName = "不合格";
        score = 0;
    }

    public DisqualificationState(PerformanceState performanceState) {
        pc = performanceState.pc;
        stateName = "不合格";
        score = performanceState.score;
    }

    @Override
    public void checkState() {
        if (score >= 60 && score<70){
            pc.setPerformanceState(new ImproveState(this));
        }else if (score >= 70 && score<80){
            pc.setPerformanceState(new QualificationState(this));
        }else if (score >= 80 && score<90){
            pc.setPerformanceState(new GoodState(this));
        }else if (score >= 90){
            pc.setPerformanceState(new ExcellentState(this));
        }
    }
}

績效需改進狀態ImproveState.java

package cn.itxs.pattern.state;

public class ImproveState extends PerformanceState{

    public ImproveState(PerformanceState performanceState) {
        pc = performanceState.pc;
        stateName = "需改進";
        score = performanceState.score;
    }

    @Override
    public void checkState() {
        if (score < 60){
            pc.setPerformanceState(new DisqualificationState(this));
        }else if (score >= 70 && score<80){
            pc.setPerformanceState(new QualificationState(this));
        }else if (score >= 80 && score<90){
            pc.setPerformanceState(new GoodState(this));
        }else if (score >= 90){
            pc.setPerformanceState(new ExcellentState(this));
        }
    }
}

績效合格狀態QualificationState.java

package cn.itxs.pattern.state;

public class QualificationState extends PerformanceState{

    public QualificationState(PerformanceState performanceState) {
        pc = performanceState.pc;
        stateName = "合格";
        score = performanceState.score;
    }

    @Override
    public void checkState() {
        if (score < 60){
            pc.setPerformanceState(new DisqualificationState(this));
        }if (score >= 60 && score<70) {
            pc.setPerformanceState(new ImproveState(this));
        }else if (score >= 80 && score<90){
            pc.setPerformanceState(new GoodState(this));
        }else if (score >= 90){
            pc.setPerformanceState(new ExcellentState(this));
        }
    }
}

績效優良狀態GoodState.java

package cn.itxs.pattern.state;

public class GoodState extends PerformanceState{

    public GoodState(PerformanceState performanceState) {
        pc = performanceState.pc;
        stateName = "優良";
        score = performanceState.score;
    }

    @Override
    public void checkState() {
        if (score < 60){
            pc.setPerformanceState(new DisqualificationState(this));
        }if (score >= 60 && score<70) {
            pc.setPerformanceState(new ImproveState(this));
        }else if (score >= 70 && score<80){
            pc.setPerformanceState(new QualificationState(this));
        }else if (score >= 90){
            pc.setPerformanceState(new ExcellentState(this));
        }
    }
}

績效優秀狀態PerformanceState.java

package cn.itxs.pattern.state;

public class ExcellentState extends PerformanceState{

    public ExcellentState(PerformanceState performanceState) {
        pc = performanceState.pc;
        stateName = "優秀";
        score = performanceState.score;
    }

    @Override
    public void checkState() {
        if (score < 60){
            pc.setPerformanceState(new DisqualificationState(this));
        }if (score >= 60 && score<70) {
            pc.setPerformanceState(new ImproveState(this));
        }else if (score >= 70 && score<80){
            pc.setPerformanceState(new QualificationState(this));
        }else if (score >= 80 && score<90) {
            pc.setPerformanceState(new GoodState(this));
        }
    }
}

測試類,績效考核隨著分數變化狀態相應變化

package cn.itxs.pattern.state;

public class StateMain {
    public static void main(String[] args) {
        PerformanceContext performanceContext = new PerformanceContext();
        performanceContext.add(50);
        performanceContext.add(10);
        performanceContext.add(11);
        performanceContext.add(12);
        performanceContext.add(13);
        performanceContext.add(-20);
    }
}

介面卡模式

概述

  • 介面卡模式將某個類的介面轉換成客戶端期望的另一個介面表示,主要目的是相容性,讓原本因介面不匹配不能工作的兩個類可以協同工作。
  • 類配置器模式屬於靜態的,通過繼承實現,較少使用;物件配置器模式通過聚合物件持有他的例項,使用關聯關係來替代繼承關係,比較靈活,較常使用。
  • 使用場景
    • 系統需要使用現有的類,但現有的類卻不相容。
    • 需要建立一個可以重複使用的類,用於一些彼此關係不大的類,並易於擴充套件,以便於面對將來會出現的類。
    • 需要一個統一的輸出介面,但是輸入型別卻不可預知。

程式碼示例

發動機介面Engine.java

package cn.itxs.pattern.adaptor;

public interface Engine {
    void drive();
}

電能驅動發動機類ElectricEngine.java

package cn.itxs.pattern.adaptor;

public class ElectricEngine {
    public void electricDrive(){
        System.out.printf("使用電能驅動發動機");
    }
}

太陽能驅動發動機類SolarEngine.java

package cn.itxs.pattern.adaptor;

public class SolarEngine {
    public void solarDrive(){
        System.out.printf("使用太陽能驅動發動機");
    }
}

電能介面卡類ElectricEngine.java

package cn.itxs.pattern.adaptor;

public class ElectricAdaptor implements Engine{
    private ElectricEngine electricEngine;

    public ElectricAdaptor() {
        electricEngine = new ElectricEngine();
    }

    @Override
    public void drive() {
        electricEngine.electricDrive();
    }
}

太陽能介面卡類ElectricEngine.java

package cn.itxs.pattern.adaptor;

public class SolarAdaptor implements Engine{
    private SolarEngine solarEngine;

    public SolarAdaptor() {
        solarEngine = new SolarEngine();
    }

    @Override
    public void drive() {
        solarEngine.solarDrive();
    }
}

測試類,實現電能適配

package cn.itxs.pattern.adaptor;

public class AdaptorMain {
    public static void main(String[] args) {
        Engine engine = new ElectricAdaptor();
        engine.drive();
    }
}

外觀模式

概述

  • 外觀模式要求一個子系統的外部與其內部的通訊必須通過一個統一的物件進行,門面模式提供一個高層的介面,使得子系統更易於使用;為複雜子系統提供一個更高層次的統一介面,只能通過該介面訪問子系統。
  • 外觀模式是較常使用的結構型設計模式,它通過引入一個外觀角色來簡化客戶端與子系統之間的互動,為複雜的子系統呼叫提供一個統一的入口,降低子系統與客戶端的耦合程度,且客戶端呼叫非常方便。
  • 使用場景
    • 為一個複雜的模組或子系統提供一個供外界訪問的介面;設計初期極端,應該有意識的將不同層分離,層與層之間建立外觀模式。
    • 開放階段,子系統越來越複雜,增加外觀模式提供一個簡單的呼叫介面。
    • 當一個系統的功能越來越複雜,依賴的子系統越來越多,客戶對系統的訪問也變得越來越複雜。這個時候如果系統內部發生改變,客戶端也會跟著變化,所以就可以建立一個統一的介面來維護這些負責系統,在新系統需要與這些子系統互動的時候,直接呼叫該介面,方便接入和維護。

程式碼示例

使用者子系統加積分User.java

package cn.itxs.pattern.facade;

public class User {
    public void credits(){
        System.out.println("加積分");
    }
}

商品子系統減庫存Good.java

package cn.itxs.pattern.facade;

public class Good {
    public void deduct(){
        System.out.println("減庫存");
    }
}

物流配送子系統下物流單Delivery.java

package cn.itxs.pattern.facade;

public class Delivery {
    public void send(){
        System.out.println("下物流單");
    }
}

訂單門面提供下單請求統一處理OrderFacade.java

package cn.itxs.pattern.facade;

public class OrderFacade {
    private Good good = new Good();
    private User user = new User();
    private Delivery delivery = new Delivery();

    public void orderRequest(){
        good.deduct();
        user.credits();
        delivery.send();
    }
}

測試類

package cn.itxs.pattern.facade;

public class FacadeMain {
    public static void main(String[] args) {
        OrderFacade orderFacade = new OrderFacade();
        orderFacade.orderRequest();
    }
}

代理模式

概述

  • 代理是在不修改原始碼的情況下使得原來不具備某種行為能力的類、物件具有該種行為能力,實現對目標物件的功能擴充套件或者增強。
  • 代理模式為其他物件提供一種代理以控制對這個物件的訪問;由於某些原因需要給某物件提供一個代理以控制對該物件的訪問時,如果訪問物件不適合或者不能直接引用目標物件,那麼代理物件就作為訪問物件和目標物件之間的中介。
  • 代理一般分為靜態代理和動態代理。
    • 靜態代理類:由程式設計師建立或由特定工具自動生成原始碼,再對其編譯。在程式執行前,代理類的.class檔案就已經存在了。事先知道要代理的是什麼,只能代理固定單一的介面,不能代理任意介面任意方法。
    • 動態代理類:在程式執行時,運用反射機制動態建立而成。
      • JDK動態代理:實現InvocationHandler介面的invoke方法,代理的是介面,也即是業務類必須要實現介面,通過Proxy裡的newProxyInstance得到代理物件;也即是動態生成代理類的位元組碼放在記憶體中。
      • CGLIB動態代理,代理的是類,不需要業務類繼承介面,通過派生的子類來實現代理。通過在執行時,動態修改位元組碼達到修改類的目的。
  • 使用場景
    • 事務處理
    • 許可權管理
    • 日誌收集

程式碼示例

靜態代理

商品運算元據庫類GoodsMapper.java

package cn.itxs.pattern.proxy;

public class GoodsMapper {
    public void handleJDBC(){
        System.out.println("商品資料寫入資料庫");
    }
}

商品代理類GoodsStaticProxy.java

package cn.itxs.pattern.proxy;

public class GoodsStaticProxy {
    private GoodsMapper goodsMapper;

    public GoodsStaticProxy(GoodsMapper goodsMapper) {
        this.goodsMapper = goodsMapper;
    }

    public void handleJDBC(){
        System.out.println("獲取資料庫連線");
        goodsMapper.handleJDBC();
        System.out.println("關閉資料庫連線");
    }
}

測試類,這樣獲取資料庫和關閉資料庫就由代理類來完成,

package cn.itxs.pattern.proxy;

public class StaticProxyMain {
    public static void main(String[] args) {
        GoodsStaticProxy goodsStaticProxy = new GoodsStaticProxy(new GoodsMapper());
        goodsStaticProxy.handleJDBC();
    }
}

如果有多個業務資料庫操作類比如增加物流配送Mapper,先定義介面CommonMapper.java

package cn.itxs.pattern.proxy;

public interface CommonMapper {
    void handleJDBC();
}

商品運算元據庫類GoodsMapper.java實現CommonMapper介面

package cn.itxs.pattern.proxy;

public class GoodsMapper implements CommonMapper{
    @Override
    public void handleJDBC(){
        System.out.println("商品資料寫入資料庫");
    }
}

物流配送運算元據庫類GoodsMapper.java實現CommonMapper介面

package cn.itxs.pattern.proxy;

public class DeliveryMapper implements CommonMapper{
    @Override
    public void handleJDBC(){
        System.out.println("物流單資料寫入資料庫");
    }
}

通用代理類CommonStaticProxy.java

package cn.itxs.pattern.proxy;

public class CommonStaticProxy {
    private CommonMapper commonMapper;

    public CommonStaticProxy(CommonMapper commonMapper) {
        this.commonMapper = commonMapper;
    }

    public void handleJDBC(){
        System.out.println("獲取資料庫連線");
        commonMapper.handleJDBC();
        System.out.println("關閉資料庫連線");
    }
}

測試類

package cn.itxs.pattern.proxy;

public class StaticProxyMain {
    public static void main(String[] args) {
        CommonStaticProxy commonStaticProxy = new CommonStaticProxy(new GoodsMapper());
        commonStaticProxy.handleJDBC();
        commonStaticProxy = new CommonStaticProxy(new DeliveryMapper());
        commonStaticProxy.handleJDBC();
    }
}

JDK動態代理

很明顯如果還有其他介面需要統一代理則上面的靜態代理則需要新增加代理類來實現,無法統一代理統一方法實現。增加行為介面BehaviorMapper.java

package cn.itxs.pattern.proxy;

public interface BehaviorMapper {
    void doLog();
}

登入日誌類LoginMapper.java

package cn.itxs.pattern.proxy;

public class LoginMapper implements BehaviorMapper{
    @Override
    public void doLog() {
        System.out.println("登入日誌業務資料庫入庫");
    }
}

ItxsInvocationHandle.java

package cn.itxs.pattern.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class ItxsInvocationHandle implements InvocationHandler {

    private Object target;

    public ItxsInvocationHandle(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("獲取資料庫連線");
        method.invoke(target,args);
        System.out.println("關閉資料庫連線");
        return null;
    }
}

測試類,可以實現CommonMapper介面和BehaviorMapper介面代理。

package cn.itxs.pattern.proxy;

import java.lang.reflect.Proxy;

public class DynamicProxyMain {
    public static void main(String[] args) {
        GoodsMapper goodsMapper = new GoodsMapper();
        ItxsInvocationHandle invocationHandle = new ItxsInvocationHandle(goodsMapper);
        CommonMapper goodsMapperI = (CommonMapper)Proxy.newProxyInstance(goodsMapper.getClass().getClassLoader(),goodsMapper.getClass().getInterfaces(),invocationHandle);
        goodsMapperI.handleJDBC();

        LoginMapper loginMapper = new LoginMapper();
        invocationHandle = new ItxsInvocationHandle(loginMapper);
        BehaviorMapper loginMapperI = (BehaviorMapper)Proxy.newProxyInstance(loginMapper.getClass().getClassLoader(),loginMapper.getClass().getInterfaces(),invocationHandle);
        loginMapperI.doLog();
    }
}

JDK動態程式碼原始碼如下,可以看出代理類名稱\(Proxy後面加自增序列號,將生成類的位元組碼直接放在jvm的記憶體中,如果有興趣也可以直接將將\)Proxy0的byte[] proxyClassFile的位元組碼寫到本地檔案看下類的資訊。

image-20220112162146049

組合模式

概述

  • 組合模式是部分整體模式,屬於結構型模式,它建立了物件組的樹形結構,將物件組合成樹狀結構以表示“整體-部分”的層次關係。
  • 組合模式使得使用者對單個物件和組合物件的訪問具有一致性,即:組合能讓客戶以一致的方式處理個別物件以及組合物件。
  • 當發現需求中是用樹形結構體現部分與整體層次關係的結構時,且使用者可以忽略整體和部分、組合物件和單個物件的不同,統一地使用組合結構中的所有物件時,就應該使用組合模式。
  • 使用場景
    • 需要遍歷組織機構,或者處理的物件具有樹形結構時, 非常適合使用組合模式
    • 樹形選單
    • 檔案、資料夾的管理

程式碼示例

商品介面Item.java

package cn.itxs.pattern.composite;

public interface Item {
    float calculate();
    void dispaly();
}

物品類實現商品介面Articles.java

package cn.itxs.pattern.composite;

public class Articles implements Item{

    private String name;
    private int quantify;
    private float price;

    public Articles(String name, int quantify, float price) {
        this.name = name;
        this.quantify = quantify;
        this.price = price;
    }

    @Override
    public float calculate() {
        return quantify*price;
    }

    @Override
    public void dispaly() {
        System.out.println("商品{" +
                "名稱='" + name + '\'' +
                ", 數量=" + quantify +
                ", 單價=" + price +
                '}');
    }
}

袋子類實現商品介面Bag.java

package cn.itxs.pattern.composite;

import java.util.ArrayList;
import java.util.List;

public class Bag implements Item{
    private String name;
    private List<Item> bags = new ArrayList<Item>();

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

    public void add(Item item){
        bags.add(item);
    }

    public void remove(Item item){
        bags.remove(item);
    }

    public Item getChildNode(int index){
        return bags.get(index);
    }

    @Override
    public float calculate() {
        float total = 0;
        for (Item bag : bags) {
            total += bag.calculate();
        }
        return total;
    }

    @Override
    public void dispaly() {
        for (Item bag : bags) {
            bag.dispaly();
        }
    }
}

測試類,袋子可以存放商品或袋子,最終顯示商品列表詳情和總價

package cn.itxs.pattern.composite;

public class CompositeMain {
    public static void main(String[] args) {
        Bag bigBag = new Bag("大號袋子");
        Bag mediumBag = new Bag("中號袋子");
        Bag smallLucencyBag = new Bag("小號透明袋子");
        Bag smallGrayBag = new Bag("小號灰色袋子");

        Articles articles;
        articles = new Articles("金鯧魚",1,25.6f);
        smallLucencyBag.add(articles);
        articles = new Articles("火龍果",5,4.9f);
        smallGrayBag.add(articles);
        articles = new Articles("檸檬",3,1.2f);
        smallGrayBag.add(articles);

        articles = new Articles("雪花啤酒",4,4.6f);
        mediumBag.add(articles);
        articles = new Articles("椰奶",2,4.5f);
        mediumBag.add(articles);
        mediumBag.add(smallLucencyBag);

        articles = new Articles("耐克運動鞋",1,299.9f);
        bigBag.add(articles);
        bigBag.add(smallGrayBag);
        bigBag.add(mediumBag);

        bigBag.dispaly();
        float total = bigBag.calculate();
        System.out.println("總計:" + total +"元");
    }
}

裝飾者模式

概述

  • 裝飾者模式在保持相同介面的同時,動態地給一個物件新增額外的功能。相對於繼承的功能擴充套件而言,裝飾器也是一個靈活的替代方式。
  • 裝飾器模式最本質的特徵是將原有類的附加功能抽離出來,簡化原有類的邏輯。建立了一個裝飾類,用來包裝原有的類,並在保持類方法簽名完整性的前提下,提供了額外的功能。
  • 使用場景
    • 擴充套件一個類的功能。
    • 動態增加功能,動態撤銷。

程式碼示例

人物介面Figure.java

package cn.itxs.pattern.decorate;

public interface Figure {
    void show();
}

綠箭俠原身類實現人物介面Arrow.java

package cn.itxs.pattern.decorate;

public class Arrow implements Figure{

    @Override
    public void show() {
        System.out.println("綠箭俠原身");
    }
}

變身父類或者裝飾類實現人物介面Transfiguration.java

package cn.itxs.pattern.decorate;

public class Transfiguration implements Figure{
    Figure figure;

    public Transfiguration(Figure figure) {
        this.figure = figure;
    }

    @Override
    public void show() {
        figure.show();
    }
}

天使變身具體類Angel.java

package cn.itxs.pattern.decorate;

public class Angel extends Transfiguration{
    public Angel(Figure figure) {
        super(figure);
    }

    @Override
    public void show() {
        System.out.println("天使化身形象附在");
        super.show();
    }
}

惡魔變身具體類Figure.java

package cn.itxs.pattern.decorate;

public class Demon extends Transfiguration{
    public Demon(Figure figure) {
        super(figure);
    }

    @Override
    public void show() {
        System.out.println("惡魔化身形象附在");
        super.show();
    }
}

測試類

package cn.itxs.pattern.decorate;

public class DecorateMain {
    public static void main(String[] args) {
        Figure arrow = new Arrow();
        arrow.show();

        Figure arrowAngel = new Angel(arrow);
        arrowAngel.show();

        Figure arrowDemon = new Demon(arrow);
        arrowDemon.show();
    }
}

原型模式

概述

  • 原型模式的核心思想是,通過拷貝指定的原型例項(物件),建立跟該物件一樣的新物件。簡單理解就是克隆指定物件,屬於建立型設計模式中的一種。
  • 需要拷貝的原型類必須實現"java.lang.Cloneable"介面,然後重寫Object類中的clone方法,從而才可以實現類的拷貝。當一個類實現了Cloneable介面後,該類才會被賦予呼叫重寫自Object類的clone方法得權利。否則會丟擲“CloneNotSupportedException”異常。
  • 原型模式中的拷貝可分為淺拷貝和深拷貝。
    • 淺拷貝:當類的成員變數是基本資料型別時,淺拷貝會複製該屬性的值賦值給新物件;當成員變數是引用資料型別時,淺拷貝複製的是引用資料型別的地址值。這種情況下,當拷貝出的某一個類修改了引用資料型別的成員變數後,會導致所有拷貝出的類都發生改變。
    • 深拷貝:深拷貝不僅會複製成員變數為基本資料型別的值,給新物件。還會給是引用資料型別的成員變數申請儲存空間,並複製引用資料型別成員變數的物件。這樣拷貝出的新物件就不怕修改了是引用資料型別的成員變數後,對其它拷貝出的物件造成影響了。有以下兩種方式實現:
      • 將所有引用型別都執行clone複製。
      • 通過序列化方式。
  • 使用場景
    • 當系統中需要大量建立相同或者相似的物件時,就可以通過“原型設計模式”來實現。
    • 如果一個物件的初始化需要很多其他物件的資料準備或其他資源的繁瑣計算,那麼可以使用原型模式。
    • 當需要一個物件的大量公共資訊,少量欄位進行個性化設定的時候,也可以使用原型模式拷貝出現有物件的副本進行加工處理。

程式碼示例

淺克隆

證照類Certificate.java實現Cloneable介面

package cn.itxs.pattern.prototype;

public class Certificate implements Cloneable{
    private String name;
    private String company;
    private String detail;

    public Certificate(String name, String company, String detail) {
        this.name = name;
        this.company = company;
        this.detail = detail;
        System.out.println("證照建立成功");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCompany() {
        return company;
    }

    public void setCompany(String company) {
        this.company = company;
    }

    public String getDetail() {
        return detail;
    }

    public void setDetail(String detail) {
        this.detail = detail;
    }

    public void show() {
        System.out.println("Certificate{" +
                "name='" + name + '\'' +
                ", company='" + company + '\'' +
                ", detail='" + detail + '\'' +
                '}');
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        System.out.println("證照拷貝成功");
        return (Certificate)super.clone();
    }
}

測試類

package cn.itxs.pattern.prototype;

public class ProtoTypeMain {
    public static void main(String[] args) throws CloneNotSupportedException {
        Certificate certificate = new Certificate("林有力","大疆無邊科技股份有限公司","2021年獲得資訊系統架構師");
        certificate.show();
        Certificate clone = (Certificate)certificate.clone();
        clone.setName("李天生");
        clone.show();
    }
}

深克隆

如果上面證照Certificate類中有非基本型別的話,那麼淺克隆無法對引用型別實現克隆。

等級類Level.java

package cn.itxs.pattern.prototype;

public class Level implements Cloneable{
    private String name;
    private String detail;

    public Level(String name, String detail) {
        this.name = name;
        this.detail = detail;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDetail() {
        return detail;
    }

    public void setDetail(String detail) {
        this.detail = detail;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

證照類Certificate.java,還是使用上面克隆實現方式

package cn.itxs.pattern.prototype;

public class Certificate implements Cloneable{
    private String name;
    private String company;
    private String detail;
    private Level level;

    public Certificate(String name, String company, String detail, Level level) {
        this.name = name;
        this.company = company;
        this.detail = detail;
        this.level = level;
        System.out.println("證照建立成功");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCompany() {
        return company;
    }

    public void setCompany(String company) {
        this.company = company;
    }

    public String getDetail() {
        return detail;
    }

    public void setDetail(String detail) {
        this.detail = detail;
    }

    public Level getLevel() {
        return level;
    }

    public void setLevel(Level level) {
        this.level = level;
    }

    public void show() {
        System.out.println("Certificate{" +
                "name='" + name + '\'' +
                ", company='" + company + '\'' +
                ", detail='" + detail + '\'' +
                ", level name='" + level.getName() + '\'' +
                ", level detail='" + level.getDetail() + '\'' +
                '}');
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        System.out.println("證照拷貝成功");
        return (Certificate)super.clone();
    }
}

測試類

package cn.itxs.pattern.prototype;

public class ProtoTypeMain {
    public static void main(String[] args) throws CloneNotSupportedException {
        Level level = new Level("高階", "中國軟體最有影響力");
        Certificate certificate = new Certificate("林有力","大疆無邊科技股份有限公司","2021年獲得資訊系統架構師",level);
        certificate.show();
        Certificate clone = (Certificate)certificate.clone();
        clone.setName("李天生");
        clone.show();
        level.setName("中級");
        clone.show();
    }
}

image-20220112191019841

再將Certificate.java中的clone()方法修改如下

    @Override
    public Object clone() throws CloneNotSupportedException {
        System.out.println("證照拷貝成功");
        Certificate certificate = (Certificate)super.clone();
        certificate.level = (Level) level.clone();
        return certificate;
    }

重新執行測試類,結果如下

image-20220112191805176

針對常用設計模式學習分享就暫時告一段落,後續有時間再補充其他的設計模式.........

相關文章