《java基礎》-胖菜鳥說接口

先扯兩句

  常常責怪自己,當初不應該。想寫《設計模式》就好好寫不好嗎,非要搞什麼拓展,在“工程模式”要介紹什麼是泛型;結果泛型說到泛型接口,又想要再介紹介紹什麼是接口,寫個博客,咋就也搞成面向對象了,各種封裝啊!剛剛寫完類和抽象類,終於到接口了,快點說完,就可以接着聊泛型了。

baby-2387661_1280.jpg

  好吧,這篇博客只是自己的學習筆記罷了,算是對於工作以來遇到使用接口的地方進行一個歸納,肯定沒有那些大神整理的專業,有邏輯性,如果大家有興趣的話,閒來無事的話,就姑且來看看吧。

正文

  上一篇寫抽象類的時候,給自己寫了個完全不符合自己心意的女朋友,不過好消息是,那個完全不符合自己心意的女孩並沒有在現實中出現,而壞消息則是,老頭子我依然單身。

  爲了讓自己擺脫單身的狀態,痛定思痛下,終於下定決心,要提升自己的能力,讓自己更強了才能吸引女孩(雖然不想承認,強不強的不知道,但確實是變禿了好多)。那就讓接口來幫我提升一下自己的能力吧!不過讓接口幫忙,先看看接口是什麼?

  1. 在JAVA編程語言中是一個抽象類型,是抽象方法的集合,接口通常以interface來聲明。一個類通過繼承接口的方式,從而來繼承接口的抽象方法。
  2. 接口並不是類,編寫接口的方式和類很相似,但是它們屬於不同的概念。類描述對象的屬性和方法。接口則包含類要實現的方法。
  3. 除非實現接口的類是抽象類,否則該類要定義接口中的所有方法。
  4. 接口無法被實例化,但是可以被實現。一個實現接口的類,必須實現接口內所描述的所有方法,否則就必須聲明爲抽象類。另外,在 Java 中,接口類型可用來聲明一個變量,他們可以成爲一個空指針,或是被綁定在一個以此接口實現的對象。

  好吧,以上就是菜鳥教程Java 接口,大家如果對於一些專業的定義啊、範例啊之類的有興趣,可以自己去看看。這部分內容讓我說,說得不如人家精闢,摘抄下來又太佔篇幅,尤其是我這種看定義就想睡覺的人,改排版都能改睡着了,所以這裏就不贅述了。

  還是老樣子,非要強行解釋一波的話也可以:接口就是相關聯的抽象方法的集合,用於專人幹專事。抽象方法不知道的可以看上一篇博客《胖菜鳥說抽象類》中的相關介紹(當然,也沒有專業的定義)

  不管聽沒聽懂,我還是先練練技能找女朋友,這纔是正事.想要留住一個女孩的心,要先留住女孩的胃(別問我這句話爲不是男人了,我都姜太公找女朋友這麼多年了,到現在也沒見誰來留住我的胃啊),那就從做一道清蒸多寶魚開始吧:

  無論做什麼菜,肯定有其相關的一些信息,例如:菜名、所需食材、烹飪流程,當然除了這些意外,對老頭子我來說,學習做菜還多出了一個目標,因此針對這些相關的內容,我們就可以封裝一個菜餚的接口了:

/**
 * 菜餚接口
 */
public interface IDish {

    /**
     * 目標
     */
    String aim = "想要留住一個女孩的心,要先留住女孩的胃";

    /**
     * 獲取菜餚的名字
     *
     * @return 菜餚的名字
     */
    String getDishName();

    /**
     * 購買食材
     *
     * @param ingredients 食材列表
     */
    void buyIngredients(String... ingredients);

    /**
     * 烹飪
     */
    void cook();
}

包含

  既然已經有了一個接口,那麼我們就先來看一下一個接口都能包含哪些內容吧。

接口的聲明與類聲明的區別

  可以看到,接口這貨的聲明雖然與類的聲明挺像的,但還是有些細微的區別,

  1. 是用class修飾的,而接口是用interface修飾的。
  2. 接口 添加final修飾符(接口創建的目的就是用來被繼承的,所以不能絕育)
    不能添加final
    而類可以添加final禁止這個類被禁止(抽象類也不能添加final,原因與接口一樣)
    final與abstract衝突
  3. 接口 添加abstract修飾符(接口都是抽象的,而且其中的方法也只能是抽象方法,因此添加不添加abstract都一樣)
    不用添加abstract
    同樣,正是因爲接口必須是abstract的,因爲衝突纔不能被final修飾

抽象方法

  抽象方法這裏與抽象類相同,都是用來給實現(類叫繼承,接口叫實現)它的類來實現具體方法的,說起來可能有些繞口,大家可以從《胖菜鳥說抽象類》一下,也可以自己寫一遍打碼,IDE就會給出提示的。

  當然這裏之所以拿出來說,不是爲了讓大家去看我的抽象類,而是爲了說明一下與抽象類中方法使用的不同,那就是接口中必須使用抽象方法,而不能有具體的實現,不然就會報錯。

接口中的方法不能有具體實現

  而且這還不算完,不僅必須是抽象方法,而且還必須是由public(全局可調用)來修飾,而不能使用protected(子類中可調用)或者private(只有當前方法中可以調用)
禁止使用protected

禁止使用private

  而在類中的default(同一個包內可以調用)這種情況在接口中也是不存在的,因爲當我們不填寫的時候會默認爲public,因此在填寫public的時候,也會提示是冗餘的。
public是冗餘的

屬性

  哈哈,看到屬性兩個字的時候聰明的你一定想到了,接口的屬性與類的屬性肯定也不一樣,但是具體區別在哪裏呢?

  1. 屬性默認是共有的:因此添加public會提示冗餘,添加private或者protected會報錯

public冗餘

private報錯

protected報錯

  1. 屬性默認是不可修改的:因此添加final會提示冗餘,不賦初值會報錯

final冗餘

不賦初值會報錯

  1. 屬性默認是靜態的,因此不需要構造接口的實現子類,就可以直接通過接口名調用

static冗餘

/*
 * 清蒸多寶魚
 *
 * @author 半壽翁
 * @date 2020-4-5
 */
public class SteamedTurbot implements IDish {
    ……
}

public class Blog {
    @Test
    public void studyCook() {
        String aim = IDish.aim;
        String aim2 = SteamedTurbot.aim;
        String aim3 = new SteamedTurbot().aim;

        String aim4 = Different.aim;
        String aim5 = new Different().aim;
    }
}

class Different {
    static final String aim = ":::";
}
    

  可以看到,接口中的屬性,無論是通過接口自身調用、通過子類調用、還是子類的實現調用都是可以的。但是我這裏做了一個類與接口的對比,還是發現了在調用中警告的顯示有些不同,代碼可能看不出來,還是給大家截圖看一下吧:

靜態屬性的實例化後調用警告

  可以看到實體類中的靜態方法,如果實現後再去調用的話,會出現警告信息“static修飾的方法或者屬性,建議通過實例類調用,而不是通過一個類的實現來調用”。但是接口中的方法根據前面的分析,也是靜態的,但是在new SteamedTurbot().aim;卻沒有提示相應的警告信,這裏只能分析是IED的問題,畢竟在接口中的靜態屬性的static字段會被認定爲冗餘字段自動忽略,如果IED是依據這個字段來判斷的自然無法識別(注意自動忽略四個字,也就是你寫不寫IDE都看不到)。

  不過這裏只是報的警告,也就是說即便你在編程的時候一定要這麼用,也是可以的。如果要說弊端的話,也就是後續其他同事維護的時候,可能會將這個靜態屬性誤認爲爲普通屬性。

  如果還有其他疑問嗎,歡迎大家在評論區一起討論。

應用場景

  優先說明,由於我是開發android的,所以對於一些後臺中的接口具體使用場景也不是特別熟悉,只能從我個人的工作中遇到的情況進行分析,可能有一些舉例並不一定在java後臺一樣適用,或者有一些在java後臺開發中的常用場景,在我這裏找不到對應的體現,我儘可能的將遇到的場景多列舉一些,其他的部分只能大家根據自己的情況補充了。

相關屬性集合

  這個在前面的接口例子實際上就是這麼封裝的,也就是將做菜的幾個環節封裝到一起,然後由具體的實現類去實現這個接口,並完善每一個環節的功能,比如清蒸多寶魚類(SteamedTurbot)就是菜餚(IDish)接口的具體實現:

/**
 * 菜餚接口
 */
public interface IDish {

    /**
     * 目標
     */
    String aim = "想要留住一個女孩的心,要先留住女孩的胃";

    /**
     * 獲取菜餚的名字
     *
     * @return 菜餚的名字
     */
    String getDishName();

    /**
     * 購買食材
     *
     * @param ingredients 食材列表
     */
    void buyIngredients(String... ingredients);

    /**
     * 烹飪
     */
    void cook();
}

/*
 * 清蒸多寶魚
 *
 * @author 半壽翁
 * @date 2020-4-5
 */
public class SteamedTurbot implements IDish {
    private List<String> ingredientList = new ArrayList<>();

    @Override
    public String getDishName() {
        return "清蒸多寶魚";
    }

    /**
     * 購買食材:
     * <p>
     * - 多寶魚
     * - 蔥
     * - 姜
     * - 紅辣椒
     * - 花椒
     * - 油
     * - 料酒
     * - 蒸魚豉油。
     *
     * @param ingredients 食材列表
     */
    @Override
    public void buyIngredients(String... ingredients) {
        ingredientList.addAll(Arrays.asList(ingredients));
    }

    /**
     * 烹飪
     * <p>
     * 第1步、買到一條鮮活的多寶魚摘除魚鰓和內臟清洗乾淨瀝乾水份備用
     * 第2步、蔥切絲和薑切片好備用
     * 第3步、多寶魚的兩面用刀割幾刀但不要割透,淋上料酒去腥
     * 第4步、上下兩面放上蔥姜煨制20分鐘入味
     * 第5步、盤中重新擺入蔥和姜備用
     * 第6步、多寶魚擺入盤中上面再擺一些蔥姜放入蒸鍋大火蒸15分鐘
     * 第7步、出鍋,在魚身上撒上蔥絲、紅辣椒絲
     * 第8步、均勻的淋上蒸魚豉油
     * 第9步、鍋中倒入適量油加熱放入花椒炸香切記不要炸糊,炸好後取出花椒
     * 第10步、重新切蔥花和紅辣椒放在多寶魚身上再澆上熱氣騰騰的花椒油瞬間滿廚房爆香
     */
    @Override
    public void cook() {

    }
}

  這是最最通用的一種接口的使用形式,但是說實話,作爲一個Android程序猿,實際項目中很少用到這種封裝形式,實在是工程太小了,除非是封裝通用系統架構的,但是後臺的使用相對就要多很多,前兩天玩了一下Spring Boot,數據庫讀寫都是這種形式的。

@Test
public void studyCook() {
    SteamedTurbot steamedTurbot = new SteamedTurbot();
    steamedTurbot.buyIngredients(
            "多寶魚"
            , "蔥"
            , "姜"
            , "紅辣椒"
            , "花椒"
            , "油"
            , "料酒"
            , "蒸魚豉油"
    );
    steamedTurbot.cook();
}

  而當我們使用的時候,只需要構造對應的實現類,然後調用相應的方法就可以了。但是可以看到我們的cook方法中現在還沒有編寫任何的內容,而且做菜的時候,肯定不能菜自己把自己給做了啊,我們肯定要有一些操作的部分,畢竟可以看到,整個烹飪過程一共有十步,還是挺多流程的,那該怎麼操作呢?這裏就需要接口的第二個應用場景了。

規劃執行流程

  上面我們買食材的時候,雖然是按照順序列舉的,但是實際上,我們去菜市場的時候肯定不會一定要按照這個順序去買,而是哪個近,哪樣更好(沒辦法,別忘了我們的aim,不能圖省錢啊)我纔會去哪買,哪怕我倒着買,也不會影響到最後清蒸多寶魚做出來的味道。

steamedTurbot.buyIngredients(
            "蒸魚豉油"
            , "料酒"
            , "油"
            , "花椒"
            , "紅辣椒"
            , "姜"
            , "蔥"
            , "多寶魚"
);

  但是製作的流程肯定不能這樣,如果倒着做出來,你不是研發了一個新的菜系,就是標準的黑暗料理了,所以這裏只能通過一個接口來規劃一下(這裏也是相關屬性的集合,只是換了一種應用場景)(完整代碼見附錄):

/*
 * 製作清蒸多寶魚
 *
 * @author 半壽翁
 * @date 2020-4-6
 */
public interface ICookSteamedTurbot {

    /**
     * 清洗、摘除內臟
     *
     * @param turbot 多寶魚
     * @return 清理好的多寶魚
     */
    String cleanTurbot(String turbot);

    /**
     * 切蔥絲、薑片
     *
     * @param turbot   多寶魚
     * @param scallion 蔥絲
     * @param ginger   薑片
     */
    void cutScallionAndGinger(String turbot, String scallion, String ginger);

    /**
     * 多寶魚切花刀,加料酒
     *
     * @param turbot      多寶魚
     * @param cookingWine 料酒
     */
    void cutTurbotAndCookingWine(String turbot, String cookingWine);

    /**
     * 醃製
     *
     * @param turbot   多寶魚
     * @param callBack 時間計時器回調
     */
    void pickling(String turbot, TimeUpCallBack callBack);

    ...
}

  有了流程的接口,下面就看我們怎麼進行流程規劃了:

/*
 * 清蒸多寶魚
 *
 * @author 半壽翁
 * @date 2020-4-5
 */
public class SteamedTurbot implements IDish {
    private ICookSteamedTurbot iCookSteamedTurbot;

    public SteamedTurbot(ICookSteamedTurbot iCookSteamedTurbot) {
        this.iCookSteamedTurbot = iCookSteamedTurbot;
    }

    /**
     * 烹飪
     * <p>
     * 第1步、買到一條鮮活的多寶魚摘除魚鰓和內臟清洗乾淨瀝乾水份備用
     * 第2步、蔥切絲和薑切片好備用
     * 第3步、多寶魚的兩面用刀割幾刀但不要割透,淋上料酒去腥
     * 第4步、上下兩面放上蔥姜煨制20分鐘入味
     * 第5步、盤中重新擺入蔥和姜備用
     * 第6步、多寶魚擺入盤中上面再擺一些蔥姜放入蒸鍋大火蒸15分鐘
     * 第7步、出鍋,在魚身上撒上蔥絲、紅辣椒絲
     * 第8步、均勻的淋上蒸魚豉油
     * 第9步、鍋中倒入適量油加熱放入花椒炸香切記不要炸糊,炸好後取出花椒
     * 第10步、重新切蔥花和紅辣椒放在多寶魚身上再澆上熱氣騰騰的花椒油瞬間滿廚房爆香
     */
    @Override
    public String cook() {
        if (null != iCookSteamedTurbot) {
            final String[] currentTurbot = {"多寶魚"};
            currentTurbot[0] = iCookSteamedTurbot.cleanTurbot(currentTurbot[0]);
            currentTurbot[0] = iCookSteamedTurbot.cutScallionAndGinger(currentTurbot[0], "蔥絲", "姜");
            currentTurbot[0] = iCookSteamedTurbot.cutTurbotAndCookingWine(currentTurbot[0], "料酒");
            iCookSteamedTurbot.pickling(currentTurbot[0], new TimeUpCallBack() {
                /**
                 * 等待醃製的時間
                 * @param turbot 醃製好多寶魚
                 */
                @Override
                public void onTimeUp(String turbot) {
                    currentTurbot[0] = iCookSteamedTurbot.platePresentation(turbot, "蔥", "姜");
                    iCookSteamedTurbot.steam(currentTurbot[0], new TimeUpCallBack() {
                        /**
                         * 等待蒸的時間
                         * @param turbot 蒸熟的多寶魚
                         */
                        @Override
                        public void onTimeUp(String turbot) {
                            currentTurbot[0] = iCookSteamedTurbot.scatterScallionPepper(turbot);
                            currentTurbot[0] = iCookSteamedTurbot.pourFishAndSoySauce(currentTurbot[0]);
                            currentTurbot[0] = iCookSteamedTurbot.fryingOil(currentTurbot[0]);
                            currentTurbot[0] = iCookSteamedTurbot.coverScallionPepperAgain(currentTurbot[0]);
                        }
                    });
                }
            });
            return currentTurbot[0];
        } else {
            return null;
        }
    }
}

  這樣我們就規劃好了烹飪的流程,同時也對外提供了用來實現對應流程的接口入口,下面只需要在外面實現,就可以了(完整代碼見附錄)。

@Test
public void studyCook() {
    SteamedTurbot steamedTurbot = new SteamedTurbot(iCookSteamedTurbot);
    steamedTurbot.buyIngredients(
            "多寶魚"
            , "蔥"
            , "姜"
            , "紅辣椒"
            , "花椒"
            , "油"
            , "料酒"
            , "蒸魚豉油"
    );
    String finishTurbot = steamedTurbot.cook();
    if (null != finishTurbot) {
        System.out.println(MessageFormat.format("美味的**{0}**可以吃了", finishTurbot));
    } else {
        System.out.println("我的魚呢");
    }
}

/**
 * 流程實現
 */
private ICookSteamedTurbot iCookSteamedTurbot = new ICookSteamedTurbot() {
    private String currentTurbot;
    /**
     * 清理多寶魚
     *
     * @param turbot 多寶魚
     * @return 清理好的多寶魚
     */
    @Override
    public String cleanTurbot(String turbot) {
        if (null != turbot) {
            currentTurbot = MessageFormat.format("{0}{1}", "清潔完成的 ", turbot);
            System.out.println(currentTurbot);
        } else {
            System.out.println("沒有多寶魚");
        }
        return currentTurbot;
    }
    /**
     * 在多寶魚下方蔥絲與薑片
     *
     * @param turbot   多寶魚
     * @param scallion 蔥絲
     * @param ginger   薑片
     */
    
    ...
};

魚做好嘍!

  由於例子中使用的食材是String,如果大家想要試驗購買食材缺少的情況,可以調整一下框架,這裏就不做演示了。而且肯定還有更復雜的邏輯使用,就看大家後面應用的時候自己去驗證了,這裏就繼續第三個應用的場景了。

回調(CallBack)

  可以看到其他的流程都是直接依照順序寫的,但是其中有這麼兩個流程長得跟其他的流程不一樣:

/*
 * 製作清蒸多寶魚
 *
 * @author 半壽翁
 * @date 2020-4-6
 */
public interface ICookSteamedTurbot {

   ...
   
    /**
     * 醃製
     *
     * @param turbot   多寶魚
     * @param callBack 時間計時器回調
     */
    void pickling(String turbot, TimeUpCallBack callBack);

   ...

    /**
     * 蒸
     *
     * @param turbot   多寶魚
     * @param callBack 時間計時器回調
     */
    void steam(String turbot, TimeUpCallBack callBack);

    ...
}

  醃製多寶魚以及蒸多寶魚,這兩個流程與其他的流程不同的是,其他無論是清洗多寶魚、處理配料都需要一直操作,而醃製和蒸都需要有一個等待的時間,這個時間我們完全可以刷一集動漫或者擼一局農藥(畢竟只有15~20分鐘,也幹不了太多事),而作爲腦子不太好用的我,一旦離開了廚房,下次想起來就不一定是什麼時候了。

遺忘的代價

  因此就需要設置一個鬧鈴,到時間以後會告訴我們別玩了,時間到了,接口同樣可以實現這個功能,就是我們的回調接口,通常來說,回到接口在android中使用的環境是多線程操作的時候,等待子線程執行完時獲取回調。在我們的例子中就是等待醃製或者蒸的時間,那我們調整一下實現的方法(偷個懶,用sleep的毫秒代替分鐘了,不然真的等不起啊):

    private ICookSteamedTurbot iCookSteamedTurbot = new ICookSteamedTurbot() {

        ...

        /**
         * 醃製
         *
         * @param turbot   多寶魚
         * @param callBack 時間計時器回調
         */
        @Override
        public void pickling(final String turbot, final TimeUpCallBack callBack) {
            if (!isNull(turbot, callBack)) {
                final long beginTime = System.currentTimeMillis();
                System.out.println(MessageFormat.format("開始醃製,時間:{0}", beginTime));
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(20);
                            long endTime = System.currentTimeMillis();
                            currentTurbot = MessageFormat.format("{0}{1}", "醃製好的 ", turbot);
                            System.out.println(MessageFormat.format("結束醃製,時間:{0},共耗時{1}", endTime, endTime - beginTime));
                            System.out.println(currentTurbot);
                            callBack.onTimeUp(null);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
            } else {
                System.out.println("調料不全");
                callBack.onTimeUp(null);
            }
        }
        
        ...
        
        /**
         * 蒸
         *
         * @param turbot   多寶魚
         * @param callBack 時間計時器回調
         */
        @Override
        public void steam(final String turbot, final TimeUpCallBack callBack) {
            if (!isNull(turbot, callBack)) {
                final long beginTime = System.currentTimeMillis();
                System.out.println(MessageFormat.format("開始醃製,時間:{0}", beginTime));
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(15);
                            long endTime = System.currentTimeMillis();
                            currentTurbot = MessageFormat.format("{0}{1}", "蒸熟的 ", turbot);
                            System.out.println(MessageFormat.format("結束醃製,時間:{0},共耗時{1}", endTime, endTime - beginTime));
                            System.out.println(currentTurbot);
                            callBack.onTimeUp(null);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
            } else {
                System.out.println("調料不全");
                callBack.onTimeUp(null);
            }
        }
        
        ...
    }

  運行後的結果如下:

添加接口回調的蒸魚流程

  當然,打印的時間還是差了幾毫秒的,但是毫秒級的程序運行大家理解一下吧,如果誰有興趣試一下分鐘的就不會出現這種尷尬了,不過這裏可沒有完成後的打印,因爲是走的子線程,所有直接從cook回調只能得到進入子線程前的結果,如果想要獲得烹飪好的清蒸多寶魚,就只能調整IDisk的cook()方法,添加一個烹飪結束的回調,或者其他什麼地方添加一個回調就可以。

監聽者(Listener)

  上面說了CallBack,其實我們的Listener與回調的原理差不多,也是需要外部出發的時候執行裏面的操作,但是從我這裏理解,Listener與CallBack的區別就是:

  • CallBack是流程的延續:在流程執行中,需要等待其中一個過程的結果才能繼續向下進行
  • Listener是開啓新的流程:由外部的一個事件觸發這個監聽者,由監聽者開啓一個全新的流程

  舉例說明就是,在方法我們有一個步驟是蒸魚,蒸魚肯定就需要用鍋,而就鍋本身而言,它的開啓關閉與蒸魚的流程並不是強相關的。我可以在做蒸魚之前蒸了飯就開啓了蒸鍋,也可能蒸好魚之後忘記了關掉蒸鍋,直接進行後續的操作去了。因此,鍋的開啓和關閉就可以使用Listener來實現調用。

/*
 * 蒸鍋使用狀態變更監聽者
 *
 * @author 半壽翁
 * @date 2020-4-6
 */
public interface ISteamerStateChangeListener {
    /**
     * 設置定時時間的開啓
     *
     * @param time       定時時間
     * @param ingredient 食材
     */
    void open(Object ingredient, long time);

    /**
     * 無定時的開始,需手動關閉
     *
     * @param ingredient 食材
     */
    void open(Object ingredient);

    /**
     * 手動關閉
     */
    void close();
}


/*
 * 蒸鍋
 *
 * @author 半壽翁
 * @date 2020-4-6
 */
public class Steamer implements ISteamerStateChangeListener {
    private Object ingredient;
    /**
     * 定時器線程池
     */
    private ScheduledExecutorService service;

    /**
     * 記過回調
     */
    private SteamDoneCallBack callBack;

    /**
     * 線程回調
     */
    private Runnable runnable = new Runnable() {
        @Override
        public void run() {
            callBack.done(ingredient);
        }
    };

    /**
     * 構造方法
     *
     * @param callBack 結果回調
     */
    public Steamer(SteamDoneCallBack callBack) {
        this.callBack = callBack;
    }

    @Override
    public void open(Object ingredient, long time) {
        if (null == service) {
            service = Executors.newSingleThreadScheduledExecutor();
        }
        if (service.isShutdown()) {
            service.schedule(runnable, time, TimeUnit.MILLISECONDS);
        } else {
            throw new IllegalStateException("鍋正用着呢,後面等着!!!");
        }
    }

    @Override
    public void close() {
        if (null != service) {
            service.shutdown();
        } else {
            throw new IllegalStateException("鍋都沒打開,關什麼關!!!");
        }
    }

    /**
     * 蒸完的回調
     */
    public interface SteamDoneCallBack {
        /**
         * 結果回傳
         *
         * @param ingredient 蒸好的食材
         */
        void done(Object ingredient);
    }
}

  蒸鍋調整好了,讓我們調整一下蒸鍋的流程,然後蒸一下魚試試效果:

/**
 * 蒸
 *
 * @param turbot   多寶魚
 * @param callBack 時間計時器回調
 */
@Override
public void steam(final String turbot, final TimeUpCallBack callBack) {
    if (!isNull(turbot, callBack)) {
        final long beginTime = System.currentTimeMillis();
        System.out.println(MessageFormat.format("開始蒸,時間:{0}", beginTime));
        new Steamer(new Steamer.SteamDoneCallBack() {
            @Override
            public void done(Object ingredient) {
                long endTime = System.currentTimeMillis();
                currentTurbot = MessageFormat.format("{0}{1}", "蒸熟的 ", turbot);
                System.out.println(MessageFormat.format("結束蒸,時間:{0},共耗時{1}", endTime, endTime - beginTime));
                System.out.println(currentTurbot);
                callBack.onTimeUp(currentTurbot);
            }
        })
                .open(turbot, 15);
    } else {
        System.out.println("調料不全");
        callBack.onTimeUp(null);
    }
}

蒸鍋蒸好

  可以看到我們的魚也成功蒸好,不過好像一不小心測出來了線程池在毫秒級比直接new Thread要慢一些,可能是我使用錯了吧,如果有大神發現了能夠指點一下。

類型判斷

  好了經過前面一系列的操作,我們終於把清蒸多寶魚做好了,下面就看能不能靠着這道菜找到個女朋友了。如果出現喜歡吃清蒸多寶魚的還有機會;如果遇到個討厭清蒸多寶魚的,那就肯定沒戲了。

/*
 * 女孩接口
 *
 * @author 半壽翁
 * @date 2020-4-6
 */
public interface IGirl {
}

/*
 * 喜歡吃清蒸多寶魚的女孩
 *
 * @author 半壽翁
 * @date 2020-4-6
 */
public interface ILikeSteamedTurbot extends IGirl {
}

/*
 * 不喜歡吃清蒸多寶魚的女孩
 *
 * @author 半壽翁
 * @date 2020-4-6
 */
public interface IDislikeSteamedTurbot extends IGirl {
}

/*
 * Lina:不喜歡吃清蒸多寶魚
 *
 * @author 半壽翁
 * @date 2020-4-6
 */
public class Lina implements IDislikeSteamedTurbot {
}

/*
 * Tina:不喜歡吃清蒸多寶魚
 *
 * @author 半壽翁
 * @date 2020-4-6
 */
public class Tina implements IDislikeSteamedTurbot {
}

/*
 * Shawn:喜歡吃清蒸多寶魚
 *
 * @author 半壽翁
 * @date 2020-4-6
 */
public class Shawn implements ILikeSteamedTurbot {
}

/*__________________________執行驗證__________________________*/

@Test
public void blindDate() {
    judge(new Lina(), new Tina(), new Shawn());
}
private void judge(IGirl... girls) {
    for (IGirl girl : girls) {
        if (girl instanceof ILikeSteamedTurbot) {
            System.out.println(MessageFormat.format("{0}喜歡吃清蒸多寶魚,我要追她"
                    , girl.getClass().getSimpleName()));
        } else if (girl instanceof IDislikeSteamedTurbot) {
            System.out.println(MessageFormat.format("{0}討厭吃清蒸多寶魚,沒機會了"
                    , girl.getClass().getSimpleName()));
        }
    }
}

區分了可以追的女孩

  用這種方式,可以依據我們的需求,區分出來不同的類,然後對不同類型的類的進行相應的業務邏輯處理。

  暫時能想到的接口的應用就是上面這幾類,實際上有幾類相互之間長得都差不多,只是使用的業務場景有所不同,而調整的不同的命名方式,裏面有沒有不合理的地方,或者缺少的地方,也歡迎大家在評論區指教。

鳴謝

附錄:

ICookSteamedTurbot

/*
 * 製作清蒸多寶魚
 *
 * @author 半壽翁
 * @date 2020-4-6
 */
public interface ICookSteamedTurbot {

    /**
     * 清洗、摘除內臟
     *
     * @param turbot 多寶魚
     * @return 清理好的多寶魚
     */
    String cleanTurbot(String turbot);

    /**
     * 切蔥絲、薑片
     *
     * @param turbot   多寶魚
     * @param scallion 蔥絲
     * @param ginger   薑片
     */
    String cutScallionAndGinger(String turbot, String scallion, String ginger);

    /**
     * 多寶魚切花刀,加料酒
     *
     * @param turbot      多寶魚
     * @param cookingWine 料酒
     */
    String cutTurbotAndCookingWine(String turbot, String cookingWine);

    /**
     * 醃製
     *
     * @param turbot   多寶魚
     * @param callBack 時間計時器回調
     */
    void pickling(String turbot, TimeUpCallBack callBack);

    /**
     * 擺盤
     *
     * @param turbot   多寶魚
     * @param scallion 蔥絲
     * @param ginger   薑片
     */
    String platePresentation(String turbot, String scallion, String ginger);

    /**
     * 蒸
     *
     * @param turbot   多寶魚
     * @param callBack 時間計時器回調
     */
    void steam(String turbot, TimeUpCallBack callBack);

    /**
     * 撒蔥絲、辣椒
     *
     * @param turbot 多寶魚
     */
    String scatterScallionPepper(String turbot);

    /**
     * 淋蒸魚豉油
     *
     * @param turbot 多寶魚
     */
    String pourFishAndSoySauce(String turbot);

    /**
     * 炸花椒爆香油
     *
     * @param turbot 多寶魚
     */
    String fryingOil(String turbot);

    /**
     * 重新撒上TimeUpCallBack
     *
     * @param turbot 多寶魚
     */
    String coverScallionPepperAgain(String turbot);
}

ICookSteamedTurbot實現

public class Blog {
    @Test
    public void studyCook() {
        SteamedTurbot steamedTurbot = new SteamedTurbot(iCookSteamedTurbot);
        steamedTurbot.buyIngredients(
                "多寶魚"
                , "蔥"
                , "姜"
                , "紅辣椒"
                , "花椒"
                , "油"
                , "料酒"
                , "蒸魚豉油"
        );
        String finishTurbot = steamedTurbot.cook();
        if (null != finishTurbot) {
            System.out.println(MessageFormat.format("美味的**{0}**可以吃了", finishTurbot));
        } else {
            System.out.println("我的魚呢");
        }
    }

    private ICookSteamedTurbot iCookSteamedTurbot = new ICookSteamedTurbot() {
        private String currentTurbot;

        /**
         * 清理多寶魚
         *
         * @param turbot 多寶魚
         * @return 清理好的多寶魚
         */
        @Override
        public String cleanTurbot(String turbot) {
            if (null != turbot) {
                currentTurbot = MessageFormat.format("{0}{1}", "清潔完成的 ", turbot);
                System.out.println(currentTurbot);
            } else {
                System.out.println("沒有多寶魚");
            }
            return currentTurbot;
        }

        /**
         * 在多寶魚下方蔥絲與薑片
         *
         * @param turbot   多寶魚
         * @param scallion 蔥絲
         * @param ginger   薑片
         */
        @Override
        public String cutScallionAndGinger(String turbot, String scallion, String ginger) {
            if (!isNull(turbot, scallion, ginger)) {
                currentTurbot = MessageFormat.format("{0}{1}", "添加好蔥絲與薑片的 ", turbot);
                System.out.println(currentTurbot);
            } else {
                System.out.println("調料不全");
            }
            return currentTurbot;
        }

        /**
         * 料酒去腥
         *
         * @param turbot      多寶魚
         * @param cookingWine 料酒
         */
        @Override
        public String cutTurbotAndCookingWine(String turbot, String cookingWine) {
            if (!isNull(turbot, cookingWine)) {
                currentTurbot = MessageFormat.format("{0}{1}", "料酒去腥的 ", turbot);
                System.out.println(currentTurbot);
            } else {
                System.out.println("調料不全");
            }
            return currentTurbot;
        }

        /**
         * 醃製
         *
         * @param turbot   多寶魚
         * @param callBack 時間計時器回調
         */
        @Override
        public void pickling(String turbot, TimeUpCallBack callBack) {
            if (!isNull(turbot, callBack)) {
                currentTurbot = MessageFormat.format("{0}{1}", "醃製好的 ", turbot);
                System.out.println(currentTurbot);
            } else {
                System.out.println("調料不全");
            }
            callBack.onTimeUp(currentTurbot);
        }

        /**
         * 蔥姜擺盤
         *
         * @param turbot 多寶魚
         * @param scallion 蔥絲
         * @param ginger   薑片
         * @return 擺好盤
         */
        @Override
        public String platePresentation(String turbot, String scallion, String ginger) {
            if (!isNull(turbot, scallion, ginger)) {
                currentTurbot = MessageFormat.format("{0}{1}", "蔥姜擺好盤的 ", turbot);
                System.out.println(currentTurbot);
            } else {
                System.out.println("調料不全");
            }
            return currentTurbot;
        }

        /**
         * 蒸
         *
         * @param turbot   多寶魚
         * @param callBack 時間計時器回調
         */
        @Override
        public void steam(String turbot, TimeUpCallBack callBack) {
            if (!isNull(turbot, callBack)) {
                currentTurbot = MessageFormat.format("{0}{1}", "蒸熟的 ", turbot);
                System.out.println(currentTurbot);
            } else {
                System.out.println("調料不全");
            }
            callBack.onTimeUp(currentTurbot);
        }

        /**
         * 撒蔥絲辣醬
         * @param turbot 多寶魚
         * @return 撒好的魚
         */
        @Override
        public String scatterScallionPepper(String turbot) {
            if (!isNull(turbot)) {
                currentTurbot = MessageFormat.format("{0}{1}", "撒好蔥絲辣椒 ", turbot);
                System.out.println(currentTurbot);
            } else {
                System.out.println("調料不全");
            }
            return currentTurbot;
        }

        /**
         * 淋蒸魚豉油
         * @param turbot 多寶魚
         * @return 淋好的多寶魚
         */
        @Override
        public String pourFishAndSoySauce(String turbot) {
            if (!isNull(turbot)) {
                currentTurbot = MessageFormat.format("{0}{1}", "淋好蒸魚豉油的 ", turbot);
                System.out.println(currentTurbot);
            } else {
                System.out.println("調料不全");
            }
            return currentTurbot;
        }

        /**
         * 炸花椒油,並淋到魚身上
         * @param turbot 多寶魚
         * @return 淋了新炸花椒油的多寶魚
         */
        @Override
        public String fryingOil(String turbot) {
            if (!isNull(turbot)) {
                currentTurbot = MessageFormat.format("{0}{1}", "淋好新炸花椒油的 ", turbot);
                System.out.println(currentTurbot);
            } else {
                System.out.println("調料不全");
            }
            return currentTurbot;
        }

        /**
         * 重新撒蔥絲辣椒
         * @param turbot 多寶魚
         * @return
         */
        @Override
        public String coverScallionPepperAgain(String turbot) {
            if (!isNull(turbot)) {
                currentTurbot = MessageFormat.format("{0}{1}", "重新撒上蔥絲辣椒的 ", turbot);
                System.out.println(currentTurbot);
            } else {
                System.out.println("調料不全");
            }
            return currentTurbot;
        }

        /**
         * 判斷是否爲空
         *
         * @param objects 被判斷對象
         * @return 是否爲空
         */
        private boolean isNull(Object... objects) {
            for (Object o : objects) {
                if (null == o) {
                    return true;
                }
            }
            return false;
        }
    };
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章