設計模式---策略模式

2022-11-03 06:02:01

簡述

預先定義有著不同執行過程但結果相同演演算法族,執行時指定所需演演算法。

演演算法族
此處為一組有共同主題的有相同結果的不同演演算法的集合。

話不多說,看個優化案例。

優化案例

最初版v0

不使用策略模式的案例。四種不同的計算策略。使用者端的程式碼如下。

// 使用者端
public class Client {
    public static void main(String[] args) {
        String target = "公園";
        Scanner sc = new Scanner(System.in);
        String input = sc.next();
        if ("foot".equals(input)) {
            System.out.println("徒步到目的地:" + target);
        } else if ("bike".equals(input)) {
            System.out.println("騎自行車到目的地:" + target);
        } else if ("car".equals(input)) {
            System.out.println("開車到目的地:" + target);
        }
        sc.close();
    }
}

具體的條件分支都寫在使用者端,日後增加新的條件分支時也需要修改使用者端。修改使用者端這件事往往是不太願意接受的。原因是我們希望使用者端儘可能少的改變,以便減少客戶使用系統的學習成本。

傳統的方法就只能增加if條件判斷了,如下。

修改版v1

只需修改使用者端,其他程式碼不變。

// 使用者端
public class Client {
    public static void main(String[] args) {
        String target = "公園";
        Scanner sc = new Scanner(System.in);
        String input = sc.next();
        if ("foot".equals(input)) {
            Foot foot = new Foot();
            foot.toTarget(target);
        } else if ("bike".equals(input)) {
            Bike bike = new Bike();
            bike.toTarget(target);
        } else if ("car".equals(input)) {
            Car car = new Car();
            car.toTarget(target);
        }
        sc.close();
    }
}

可以看出使用者端依舊與各個具體的類耦合(從類的建立到方法的呼叫都是如此)。

可以使用策略模式優化,使得方法呼叫不需要if條件判斷,傳入什麼樣的物件就使用什麼物件的行為。

修改版v2(策略模式)

public interface Trans {
    void toTarget(String target);
}

// 徒步去目的地
public class Foot implements Trans {
    @Override
    public void toTarget(String target) {
        System.out.println("徒步到目的地:" + target);
    }
}

// 騎自行車去目的地
public class Bike implements Trans {
    @Override
    public void toTarget(String target) {
        System.out.println("騎自行車到目的地:" + target);
    }
}

// 開車去目的地
public class Car implements Trans {
    @Override
    public void toTarget(String target) {
        System.out.println("開車到目的地:" + target);
    }
}

// 上下文類,根據使用者端業務的需求持有不同的計算物件
public class Context {
    private Trans trans;
    
    public Context(Trans trans) {
       	this.trans = trans;
    }
    
    // 更改持有的計算物件
    public change(Trans trans) {
        this.trans = trans;
    }
    
    // 實際呼叫持有的trans實現計算
    public int toTarget(String target) {
        return trans.toTarget(target);
    }
}

修改後,使用者端程式碼呼叫。

// 使用者端
public class Client {
    public static void main(String[] args) {
        String target = "公園";
        Scanner sc = new Scanner(System.in);
        String input = sc.next();
        Context context = null;
        if ("foot".equals(input)) {
            context = new Context(new Foot());
        } else if ("bike".equals(input)) {
            context = new Context(new Bike());
        } else if ("car".equals(input)) {
            context = new Context(new Car());
        }
        System.out.println(context.toTarget(target));
        sc.close();
    }
}

程式碼量確實有一定的減少,但是使用者端程式碼從只與各個具體Trans類的實現類耦合到多耦合一個上下文類,這樣想與我們的需求背道而馳啊。實際上,單純的策略模式就是如此,只負責減少方法呼叫的if語句,而不設計物件建立的封裝與優化。

說到物件建立的優化,就得說到工廠模式了,事實上在使用策略模式時,為了建立物件也變得方便,通常也會使用到工廠模式進行優化。詳情看以下優化案例。

修改版v3(策略+簡單工廠)

現有程式碼都不需要改變,只需要使用簡單工廠封裝上下文物件的建立即可。

// 工廠類,建立持有不同Trans物件的上下文物件
public class Factory {
    public static Context create(String input) {
        if ("foot".equals(input)) {
            return new Context(new Foot());
        } else if ("bike".equals(input)) {
            return new Context(new Bike());
        } else if ("car".equals(input)) {
            return new Context(new Car());
        }
        return null;
    }
}

修改後,使用者端程式碼呼叫。

// 使用者端
public class Client {
    public static void main(String[] args) {
        String target = "公園";
        Scanner sc = new Scanner(System.in);
        Context context = Factory.create(sc.next());
        System.out.println(context.toTarget(target));
        sc.close();
    }
}

使用者端程式碼大幅減少,並且使用者端中僅僅與Context類存在耦合。建立與使用的核心邏輯都從使用者端剝離,且具體呼叫的方法也只有在執行時才知曉(核心目的)。這樣就能少些很多if語句了。

總結

優點

  • 可以大幅減少if語句的書寫。
  • 增加新的實現方法也不需要修改使用者端程式碼,只需要增加實現類。

缺點

  • 單純的策略模式需要使用者端對於各個實現類有足夠的瞭解,提升了開發時對系統的理解難度。

  • 策略過多時,存在策略膨脹的問題。鑑於策略膨脹問題,應該慎用策略模式。這是使用混合模式或許可以解決這個問題。

    混合模式
    即在策略模式的實現類的方法中使用if語句分割各個情況。

適用場景

  • 想要優化系統中過多的if語句時。