設計模式—策略模式

簡述

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

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

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

優化案例

最初版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語句時。
Exit mobile version