策略模式--strategy

文章目錄

我們很容易理解整型的 i>j 這樣的比較方式,但當(dāng)我們對多個對象進(jìn)行排序時,如何比較兩個對象的“大小”呢?這樣的比較 stu1 > stu2 顯然是不可能通過編譯的。
為了解決如何比較兩個對象大小的問題,JDK提供了兩個接口 java.lang.Comparable 和 java.util.Comparator 。

Comparable

Comparable接口

所有可以 “排序” 的類都實(shí)現(xiàn)了java.lang.Comparable接口,Comparable接口中只有一個方法。Comparable接口中只有一個方法:實(shí)現(xiàn)了 Comparable 接口的類通過實(shí)現(xiàn) comparaTo 方法從而確定該類對象的排序方式。

public int compareTo(T o);
返回 0 表示 this == obj
返回整數(shù)表示 this > obj
返回負(fù)數(shù)表示 this < obj

調(diào)用此方法的對象,也就是this和o進(jìn)行比較,若返回值大于0則this大于o,返回值等于0則是this等于o,返回值小于0則是this<o,而這個Comparable是直接在我們的自定義類User上實(shí)現(xiàn),因?yàn)閠his是需要一個明確的比較對象的,也就是一般情況下我們會在定義User類的時候有排序的需求,就要實(shí)現(xiàn)此接口。

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

public class UserComparable implements Comparable<UserComparable> {
    private String name;
    private int age;

    public UserComparable(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "UserComparable{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int compareTo(UserComparable o) {
    //由于字符串無法直接比較大小,所以調(diào)用String類的compareTo
        if (this.name.compareTo(o.name)==0){
            if (this.age == o.age){
                return 0;
            }else if (this.age >o.age){
                return 1;
            }else {
                return -1;
            }
        }else if (this.name.compareTo(o.name)>0){
            return 1;
        }else {
            return -1;
        }
    }

    public static void main(String[] args) {
        List<UserComparable> list = new ArrayList<UserComparable>();
        list.add(new UserComparable("gol",21));
        list.add(new UserComparable("gol",19));
        list.add(new UserComparable("xiao",21));
        list.add(new UserComparable("long",21));
        System.out.println("排序前:"+list);
        //排序規(guī)則:先按name排序,若name相等則再比較age
        Collections.sort(list);
        System.out.println("排序后:"+list);
    }
}

Comparator:

Comparator接口中方法很多,但是我們只需要實(shí)現(xiàn)一個,也是最重要的一個compare,也許有的人會好奇為什么接口中的方法可以不用實(shí)現(xiàn),因?yàn)檫@是JDK8以后的新特性,在接口中用default修飾的方法可以有方法體,在實(shí)現(xiàn)接口的時候可以不用重寫,可以類比抽象類。

int compare(T o1, T o2);

compare比較的o1和o2,返回值大于0則o1大于o2,依次類推,對于compare來說this是誰不重要,所比較的兩個對象都已經(jīng)傳入到方法中,所以Comparator就是個外部比較器,在我們設(shè)計User初時,并不需要它有比較功能,在后期擴(kuò)展業(yè)務(wù)是,Comparator的存在可以使我們在不修改源代碼的情況下來完成需求,只需要新定義一個比較器來實(shí)現(xiàn)Comparator,重寫compare方法并將User對象傳進(jìn)去。

public class emp {
    public int age;
    public String name;
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public emp(int age, String name) {
        super();
        this.age = age;
        this.name = name;
    }
    public emp() {
        super();
        // TODO Auto-generated constructor stub
    }
    @Override
    public String toString() {
        return "emp [age=" + age + ", name=" + name + "]";
    }
}

import org.junit.Test;
import junit.framework.TestCase;
public class TestCompare extends TestCase {
     @Test
    public void test2(){
        List<emp> list=new ArrayList<emp>();
        emp test1=new emp(69,"李四");
        emp test2=new emp(29,"王五");
        emp test3=new emp(28,"趙六");
        emp test4=new emp(20,"錢三");
        list.add(test4);
        list.add(test3);
        list.add(test2);
        list.add(test1);
        Collections.sort(list,new Comparator<emp>(){
            @Override
            public int compare(emp o1, emp o2) {
                if(o1.age==o2.age&&o1.name==o2.name){
                    return 0;
                }else if(o1.age>o2.age){
                    return 1;           
                }else{
                    return 0;
                }
            }
        });     
        for(Object s:list){
            System.out.println(s);
        }       
    }   
}

結(jié)論:

1、Comparator 使用比較靈活,不需要修改實(shí)體類源碼,但是需要實(shí)現(xiàn)一個比較器。
2、Comparable 使用簡單,但是對代碼有侵入性,需要修改實(shí)體類源碼。
3、java中大部分我們常用的數(shù)據(jù)類型的類都實(shí)現(xiàn)了Comparable接口,而僅僅只有一個抽象類RuleBasedCollator實(shí)現(xiàn)了Comparator接口 ,還是我們不常用的類,
這并不是說要用Comparab而不要使用Comparator,在設(shè)計初時有需求就選擇Comparable,若后期需要擴(kuò)展或增加排序需求是,再增加一個比較器Comparator,畢竟能寫Collections.sort(arg1),沒人樂意寫Collections.sort(arg1,arg2)。

策略模式

在策略模式(Strategy Pattern)中,一個類的行為或其算法可以在運(yùn)行時更改。這種類型的設(shè)計模式屬于行為型模式。

在策略模式中,我們創(chuàng)建表示各種策略的對象和一個行為隨著策略對象改變而改變的 context 對象。策略對象改變 context 對象的執(zhí)行算法。

介紹

意圖:定義一系列的算法,把它們一個個封裝起來, 并且使它們可相互替換。

主要解決:在有多種算法相似的情況下,使用 if...else 所帶來的復(fù)雜和難以維護(hù)。

何時使用:一個系統(tǒng)有許多許多類,而區(qū)分它們的只是他們直接的行為。

如何解決:將這些算法封裝成一個一個的類,任意地替換。

關(guān)鍵代碼:實(shí)現(xiàn)同一個接口。

應(yīng)用實(shí)例: 1、諸葛亮的錦囊妙計,每一個錦囊就是一個策略。 2、旅行的出游方式,選擇騎自行車、坐汽車,每一種旅行方式都是一個策略。 3、JAVA AWT 中的 LayoutManager。

優(yōu)點(diǎn): 1、算法可以自由切換。 2、避免使用多重條件判斷。 3、擴(kuò)展性良好。

缺點(diǎn): 1、策略類會增多。 2、所有策略類都需要對外暴露。

使用場景: 1、如果在一個系統(tǒng)里面有許多類,它們之間的區(qū)別僅在于它們的行為,那么使用策略模式可以動態(tài)地讓一個對象在許多行為中選擇一種行為。 2、一個系統(tǒng)需要動態(tài)地在幾種算法中選擇一種。 3、如果一個對象有很多的行為,如果不用恰當(dāng)?shù)哪J?,這些行為就只好使用多重的條件選擇語句來實(shí)現(xiàn)。

注意事項(xiàng):如果一個系統(tǒng)的策略多于四個,就需要考慮使用混合模式,解決策略類膨脹的問題。

實(shí)現(xiàn)

我們將創(chuàng)建一個定義活動的 Strategy 接口和實(shí)現(xiàn)了 Strategy 接口的實(shí)體策略類。Context 是一個使用了某種策略的類。

StrategyPatternDemo,我們的演示類使用 Context 和策略對象來演示 Context 在它所配置或使用的策略改變時的行為變化。

策略模式的 UML 圖

步驟 1

創(chuàng)建一個接口。

image.png

步驟 2

創(chuàng)建實(shí)現(xiàn)接口的實(shí)體類。

image.png
image.png
image.png

步驟 3

創(chuàng)建 Context 類。

image.png

步驟 4

使用 Context 來查看當(dāng)它改變策略 Strategy 時的行為變化。

image.png

步驟 5

執(zhí)行程序,輸出結(jié)果:

image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容