《java基础》-胖菜鸟说接口

先扯两句

  常常责怪自己,当初不应该。想写《设计模式》就好好写不好吗,非要搞什么拓展,在“工程模式”要介绍什么是泛型;结果泛型说到泛型接口,又想要再介绍介绍什么是接口,写个博客,咋就也搞成面向对象了,各种封装啊!刚刚写完类和抽象类,终于到接口了,快点说完,就可以接着聊泛型了。

《java基础》-胖菜鸟说接口_第1张图片

  好吧,这篇博客只是自己的学习笔记罢了,算是对于工作以来遇到使用接口的地方进行一个归纳,肯定没有那些大神整理的专业,有逻辑性,如果大家有兴趣的话,闲来无事的话,就姑且来看看吧。

正文

  上一篇写抽象类的时候,给自己写了个完全不符合自己心意的女朋友,不过好消息是,那个完全不符合自己心意的女孩并没有在现实中出现,而坏消息则是,老头子我依然单身。

  为了让自己摆脱单身的状态,痛定思痛下,终于下定决心,要提升自己的能力,让自己更强了才能吸引女孩(虽然不想承认,强不强的不知道,但确实是变秃了好多)。那就让接口来帮我提升一下自己的能力吧!不过让接口帮忙,先看看接口是什么?

  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修饰符(接口创建的目的就是用来被继承的,所以不能绝育)
    《java基础》-胖菜鸟说接口_第2张图片
    而类可以添加final禁止这个类被禁止(抽象类也不能添加final,原因与接口一样)
    final与abstract冲突
  3. 接口 添加abstract修饰符(接口都是抽象的,而且其中的方法也只能是抽象方法,因此添加不添加abstract都一样)
    《java基础》-胖菜鸟说接口_第3张图片
    同样,正是因为接口必须是abstract的,因为冲突才不能被final修饰

抽象方法

  抽象方法这里与抽象类相同,都是用来给实现(类叫继承,接口叫实现)它的类来实现具体方法的,说起来可能有些绕口,大家可以从《胖菜鸟说抽象类》一下,也可以自己写一遍打码,IDE就会给出提示的。

  当然这里之所以拿出来说,不是为了让大家去看我的抽象类,而是为了说明一下与抽象类中方法使用的不同,那就是接口中必须使用抽象方法,而不能有具体的实现,不然就会报错。

《java基础》-胖菜鸟说接口_第4张图片

  而且这还不算完,不仅必须是抽象方法,而且还必须是由public(全局可调用)来修饰,而不能使用protected(子类中可调用)或者private(只有当前方法中可以调用)
《java基础》-胖菜鸟说接口_第5张图片

《java基础》-胖菜鸟说接口_第6张图片

  而在类中的default(同一个包内可以调用)这种情况在接口中也是不存在的,因为当我们不填写的时候会默认为public,因此在填写public的时候,也会提示是冗余的。
《java基础》-胖菜鸟说接口_第7张图片

属性

  哈哈,看到属性两个字的时候聪明的你一定想到了,接口的属性与类的属性肯定也不一样,但是具体区别在哪里呢?

  1. 属性默认是共有的:因此添加public会提示冗余,添加private或者protected会报错

《java基础》-胖菜鸟说接口_第8张图片

《java基础》-胖菜鸟说接口_第9张图片

《java基础》-胖菜鸟说接口_第10张图片

  1. 属性默认是不可修改的:因此添加final会提示冗余,不赋初值会报错

《java基础》-胖菜鸟说接口_第11张图片

《java基础》-胖菜鸟说接口_第12张图片

  1. 属性默认是静态的,因此不需要构造接口的实现子类,就可以直接通过接口名调用

《java基础》-胖菜鸟说接口_第13张图片

/*
 * 清蒸多宝鱼
 *
 * @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 = ":::";
}
    

  可以看到,接口中的属性,无论是通过接口自身调用、通过子类调用、还是子类的实现调用都是可以的。但是我这里做了一个类与接口的对比,还是发现了在调用中警告的显示有些不同,代码可能看不出来,还是给大家截图看一下吧:

《java基础》-胖菜鸟说接口_第14张图片

  可以看到实体类中的静态方法,如果实现后再去调用的话,会出现警告信息“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 ingredientList = new ArrayList<>();

    @Override
    public String getDishName() {
        return "清蒸多宝鱼";
    }

    /**
     * 购买食材:
     * 

* - 多宝鱼 * - 葱 * - 姜 * - 红辣椒 * - 花椒 * - 油 * - 料酒 * - 蒸鱼豉油。 * * @param ingredients 食材列表 */ @Override public void buyIngredients(String... ingredients) { ingredientList.addAll(Arrays.asList(ingredients)); } /** * 烹饪 *

* 第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;
    }

    /**
     * 烹饪
     * 

* 第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   姜片
     */
    
    ...
};

《java基础》-胖菜鸟说接口_第15张图片

  由于例子中使用的食材是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分钟,也干不了太多事),而作为脑子不太好用的我,一旦离开了厨房,下次想起来就不一定是什么时候了。

《java基础》-胖菜鸟说接口_第16张图片

  因此就需要设置一个闹铃,到时间以后会告诉我们别玩了,时间到了,接口同样可以实现这个功能,就是我们的回调接口,通常来说,回到接口在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);
            }
        }
        
        ...
    }

  运行后的结果如下:

《java基础》-胖菜鸟说接口_第17张图片

  当然,打印的时间还是差了几毫秒的,但是毫秒级的程序运行大家理解一下吧,如果谁有兴趣试一下分钟的就不会出现这种尴尬了,不过这里可没有完成后的打印,因为是走的子线程,所有直接从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);
    }
}

《java基础》-胖菜鸟说接口_第18张图片

  可以看到我们的鱼也成功蒸好,不过好像一不小心测出来了线程池在毫秒级比直接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()));
        }
    }
}

《java基础》-胖菜鸟说接口_第19张图片

  用这种方式,可以依据我们的需求,区分出来不同的类,然后对不同类型的类的进行相应的业务逻辑处理。

  暂时能想到的接口的应用就是上面这几类,实际上有几类相互之间长得都差不多,只是使用的业务场景有所不同,而调整的不同的命名方式,里面有没有不合理的地方,或者缺少的地方,也欢迎大家在评论区指教。

鸣谢

  • 哭的像个孩子:图片取自 Pixabay 的 joffi
  • 清蒸多宝鱼菜谱:清蒸多宝鱼,鲜嫩营养,方便快捷又好吃到爆
  • 遗忘的代价:图片取自 Pixabay 的 David Mark

附录:

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;
        }
    };
}

你可能感兴趣的:(java基础)