設計模式---策略模式(Strategy Pattern with java)

策略模式是一個很簡單的模式,也是一個很常用的模式,可謂短小精悍,類庫有很多使用策略模式的例子,所以本文以模擬類庫爲例子,學習策略模式,也熟悉了java類庫設計中的精華,加深了我們的OO思想。

1 概念

策略模式(Strategy):它定義了一系列的算法,並將每一個算法封裝起來,而且使它們還可以相互替換。策略模式讓算法的變化不會影響到使用算法的客戶。(原文:The Strategy Pattern defines a family of algorithms,encapsulates each one,and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.)

image

2 模擬java類庫中Comparable和Comparator接口

本文實現一個簡單的排序算法,通過一步步設計,設計Comparable和Comparator接口,理解了策略模式和java類庫設計這2個接口的原因和不同用途。

3 最簡單的排序例子

public class Test {

    public static void main(String[] args) {

        int[] nums = { 3, 4, 1, 5, 2 };

        DataSortor.sort(nums);

        DataSortor.p(nums);

    }

}

class DataSortor {

    public static void sort(int[] nums) {

        for (int i = 0; i < nums.length; i += 1) {

            for (int j = i + 1; j < nums.length; j += 1) {

                if (nums[i] > nums[j]) {

                    nums[i] = nums[i] + nums[j];

                    nums[j] = nums[i] - nums[j];

                    nums[i] = nums[i] - nums[j];

                }

            }

        }

    }

    public static void p(int[] nums) {

        for (int i : nums) {

            System.out.print(i + " ");

        }

        System.out.println();

    }

}

一個最簡單使用冒泡的排序一個int數組,優點簡單明瞭,缺點擴展性和代碼重用性不強(不考慮算法本身,比如什麼樣的數據用不用的排序算法),而且支持的類型單一!

4 支持多重類型的排序算法

public class Test {

    public static void main(String[] args) {

        int[] nums = { 3, 4, 1, 5, 2 };

        DataSortor.sort(nums);

        DataSortor.p(nums);

        double[] doubles = { 1.2, 0.3, 2.5, 8.9, 0};

        DataSortor.sort(doubles);

        DataSortor.p(doubles);

        Dog[] dogs = {new Dog(3,3), new Dog(1,1), new Dog(5,5)};

        DataSortor.sort(dogs);

        DataSortor.p(dogs);

    }

}

class DataSortor {

    public static void sort(int[] nums) {

        for (int i = 0; i < nums.length; i += 1) {

            for (int j = i + 1; j < nums.length; j += 1) {

                if (nums[i] > nums[j]) {

                    nums[i] = nums[i] + nums[j];

                    nums[j] = nums[i] - nums[j];

                    nums[i] = nums[i] - nums[j];

                }

            }

        }

    }

    public static void p(Dog[] dogs) {

        for(int i=0; i<dogs.length; i+=1) {

            System.out.print(dogs[i] + " ");

        }

        System.out.println();

    }

    public static void sort(Dog[] dogs) {

        for (int i = 0; i < dogs.length; i += 1) {

            for (int j = i + 1; j < dogs.length; j += 1) {

                if (dogs[i].getHeight()>dogs[j].getHeight()) {

                    Dog temp = dogs[i];

                    dogs[i] = dogs[j];

                    dogs[j] = temp;

                }

            }

        }

    }

    public static void p(double[] doubles) {

        for (double i : doubles) {

            System.out.print(i + " ");

        }

        System.out.println();

    }

    public static void sort(double[] doubles) {

        for (int i = 0; i < doubles.length; i += 1) {

            for (int j = i + 1; j < doubles.length; j += 1) {

                if (doubles[i] > doubles[j]) {

                    doubles[i] = doubles[i] + doubles[j];

                    doubles[j] = doubles[i] - doubles[j];

                    doubles[i] = doubles[i] - doubles[j];

                }

            }

        }

    }

    public static void sort1(int[] nums) {

        for (int i = nums.length - 1; i >= 0; i -= 1) {

            for (int j = 0; j < i; j += 1) {

                if (nums[j] > nums[j+1]) {

                    nums[j+1] = nums[j+1] + nums[j];

                    nums[j] = nums[j+1] - nums[j];

                    nums[j+1] = nums[j+1] - nums[j];

                }

            }

        }

    }

    public static void p(int[] nums) {

        for (int i : nums) {

            System.out.print(i + " ");

        }

        System.out.println();

    }

}

class Dog {

    private int height;

    private int weight;

    public Dog(int height, int weight) {

        super();

        this.height = height;

        this.weight = weight;

    }

    public int getHeight() {

        return height;

    }

    public void setHeight(int height) {

        this.height = height;

    }

    public int getWeight() {

        return weight;

    }

    public void setWeight(int weight) {

        this.weight = weight;

    }

    @Override

    public String toString() {

        return "Dog(" + height + "," + weight +")";

    }

     

}

輸出結果:

 

1 2 3 4 5

0.0 0.30000000000000004 1.2000000000000002 2.5 8.9

Dog(1,1) Dog(3,3) Dog(5,5)

添加了更多基本類型的排序和基本類的比較,但也擴展性和重用性差還是很差。

如果我們繼續添加新的類,我們必須重新寫比較方法,重新改我們的排序代碼,排序算法不能得到重用!所以我們可以添加比較接口!

5 引入Comparable接口

public class Test {

    public static void main(String[] args) {

        Dog[] dogs = { new Dog(3, 3), new Dog(1, 1), new Dog(5, 5) };

        DataSortor.sort(dogs);

        DataSortor.p(dogs);

    }

}

class DataSortor {

    public static void p(Object[] objs) {

        for (int i = 0; i < objs.length; i += 1) {

            System.out.print(objs[i] + " ");

        }

        System.out.println();

    }

    public static void sort(Comparable[] objs) {

        for (int i = 0; i < objs.length; i += 1) {

            for (int j = i + 1; j < objs.length; j += 1) {

                if (objs[i].compareTo(objs[j])>0) {

                    Comparable temp = objs[i];

                    objs[i] = objs[j];

                    objs[j] = temp;

                }

            }

        }

    }

}

interface Comparable {

    int compareTo(Object obj);

}

class Dog implements Comparable {

    private int height;

    private int weight;

    public Dog(int height, int weight) {

        super();

        this.height = height;

        this.weight = weight;

    }

    public int getHeight() {

        return height;

    }

    public void setHeight(int height) {

        this.height = height;

    }

    public int getWeight() {

        return weight;

    }

    public void setWeight(int weight) {

        this.weight = weight;

    }

    @Override

    public String toString() {

        return "Dog(" + height + "," + weight + ")";

    }

    @Override

    public int compareTo(Object obj) {

        if (obj instanceof Dog) {

            Dog d = (Dog) obj;

            if (height > d.getHeight())

                return 1;

            else

                return -1;

        }

        return 0;

    }

}

實現了Comparable接口的類,都可以使用排序算法,而不需要更改算法代碼!提高的可用性,據有很大的擴展性。

但實現Comparable接口實現比較方法不能解決使用其他比較策略比較大小,比如上面使用cat的height作爲比較依據,可能以後使用cat的weight作爲比較依據。所以我們提出了策略模式,引入了Comparator接口,可以讓cat選擇比較策略,來實現不同的比較方法。

6 引入Comparator接口

public class Test {

    public static void main(String[] args) {

        Dog[] dogs = { new Dog(3, 3), new Dog(1, 1), new Dog(5, 5) };

        DataSortor.sort(dogs);

        DataSortor.p(dogs);

    }

}

class DataSortor {

    public static void p(Object[] objs) {

        for (int i = 0; i < objs.length; i += 1) {

            System.out.print(objs[i] + " ");

        }

        System.out.println();

    }

 

    public static void sort(Comparable[] objs) {

        for (int i = 0; i < objs.length; i += 1) {

            for (int j = i + 1; j < objs.length; j += 1) {

                if (objs[i].compareTo(objs[j]) > 0) {

                    Comparable temp = objs[i];

                    objs[i] = objs[j];

                    objs[j] = temp;

                }

            }

        }

    }

}

interface Comparable {

    int compareTo(Object obj);

}

interface Comparator {

    int compare(Object o1, Object o2);

}

class DogHeightComparator implements Comparator {

    @Override

    public int compare(Object o1, Object o2) {

        if (!(o1 instanceof Dog))

            return 0;

        if (!(o2 instanceof Dog))

            return 0;

        Dog dog1 = (Dog) o1;

        Dog dog2 = (Dog) o2;

        if (dog1.getHeight() > dog2.getHeight())

            return 1;

        else if (dog1.getHeight() < dog2.getHeight())

            return -1;

        return 0;

    }

 

}

class Dog implements Comparable {

    private int height;

    private int weight;

    private Comparator comparator = new DogHeightComparator();

    @Override

    public String toString() {

        return "Dog(" + height + "," + weight + ")";

    }

    @Override

    public int compareTo(Object obj) {

        return comparator.compare(this, obj);

    }

    public Dog(int height, int weight) {

        super();

        this.height = height;

        this.weight = weight;

    }

     

    public Comparator getComparator() {

        return comparator;

    }

 

    public void setComparator(Comparator comparator) {

        this.comparator = comparator;

    }

 

    public int getHeight() {

        return height;

    }

 

    public void setHeight(int height) {

        this.height = height;

    }

 

    public int getWeight() {

        return weight;

    }

 

    public void setWeight(int weight) {

        this.weight = weight;

    }

}


這樣可以設計不同的Comparator來實現不同的比較方式,大大的提高了程序的擴展性,但值得說的類也要實現Comparable接口,只是在Comparable接口的實現方法中使用我們自己設置的排序策略,當然我們可以指定默認的排序算法,如上,這樣如果有特殊需求時,在設置相應的排序算法,極大的提高了系統的靈活性。

現在,應該對策略模式有點較深的認識了吧,知道了爲什麼java類庫中爲什麼有了Comparable還有Comparator接口了吧。

7 策略模式優缺點

優點:

1、 簡化了單元測試,因爲每個算法都有自己的類,可以通過自己的接口單獨測試。
2、 避免程序中使用多重條件轉移語句,使系統更靈活,並易於擴展。
3、 遵守大部分GRASP原則和常用設計原則,高內聚、低偶合。

缺點:
1、 因爲每個具體策略類都會產生一個新類,所以會增加系統需要維護的類的數量。

2、 在基本的策略模式中,選擇所用具體實現的職責由客戶端對象承擔,並轉給策略模式的Context對象。(這本身沒有解除客戶端需要選擇判斷的壓力,而策略模式與簡單工廠模式結合後,選擇具體實現的職責也可以由Context來承擔,這就最大化的減輕了客戶端的壓力。)

8 簡單工廠模式和策略模式區別(摘錄,出處

這兩種模式的作用就是擁抱變化,減少耦合。在變化來臨時爭取做最小的改動來適應變化。這就要求我們把些“善變”的功能從客戶端分離出來,形成一個個的功能類,然後根據多態特性,使得功能類變化的同時,客戶端代碼不發生變化。

簡單工廠模式

簡單工廠模式:有一個父類需要做一個運算(其中包含了不同種類的幾種運算),將父類涉及此運算的方法都設成虛方法,然後父類派生一些子類,使得每一種不同的運算都對應一個子類。另外有一個工廠類,這個類一般只有一個方法(工廠的生成方法),這個方法的返回值是一個超類,在方法的內部,根據傳入參數的不同,分別構造各個不同的子類的對象,並返回。客戶端並不認識子類,客戶端只認識超類和工廠類。每次客戶端需要一中運算時,就把相應的參數傳給工廠類,讓工廠類構造出相應的子類,然後在客戶端用父類接收(這裏有一個多態的運用)。客戶端很順理成章地用父類的計算方法(其實這是一個虛方法,並且已經被子類特化過了,其實是調用子類的方法)計算出來結果。如果要增加功能時,你只要再從父類中派生相應功能的子類,然後修改下工廠類就OK了,對於客戶端是透明的。

策略模式

策略模式:策略模式更直接了一點,沒有用工廠類,而是直接把工廠類的生成方法的代碼寫到了客戶端。客戶端自己構造出了具有不同功能的子類(而且是用父類接收的,多態),省掉了工廠類。策略模式定義了算法家族,分別封裝起來,讓他們之間可以互相替換,此模式讓算法的變化,不會影響到使用算法的客戶。這裏的算法家族和簡單工廠模式裏的父類是同一個概念。當不同的行爲堆砌在一個類中時,就很難避免使用條件語句來選擇合適的行爲,將這些行爲封裝在一個個獨立的策略子類中,可以在客戶端中消除條件語句。

簡單工廠模式+策略模式:爲了將工廠方法的代碼從客戶端移出來,我們把這些代碼搬到了父類的構造函數中,讓父類在構造的時候,根據參數,自己實現工廠類的作用。這樣做的好處就是,在客戶端不用再認識工廠類了,客戶端只要知道父類一個就OK,進一步隔離了變化,降低了耦合。

在基本的策略模式中,選擇所用具體實現的職責由客戶端對象成端,並轉給客戶端。這本身並沒有減除客戶端需要選擇判斷的壓力,而策略模式與簡單工廠模式結合後,選擇具體實現的職責也可以由父類承擔,這就最大化地減輕了客戶端的職責。

9 總結

策略模式作爲一種軟件設計模式,指對象有某個行爲,但是在不同的場景中,該行爲有不同的實現算法。

發佈了18 篇原創文章 · 獲贊 2 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章