Java Skeletal Implementation/Abstract Interfaces(骨架实现/抽象接口)

版权声明:本文为博主原创文章,未经博主允许,也可以随意转载。但麻烦,加个友情链接,增加点人气。 https://blog.csdn.net/l_o_s/article/details/80901441

前言:这个模式的主要作用是,集接口多继承的优势与抽象类可以减少重复代码的优势于一体。(skeletal implementation,简称SI)

一、接口与抽象类的优劣。

  • 接口,可以实现多继承,但抽象类不行。
  • 抽象类,可以有实现,但接口不行。

我们先一步一步来,先举一个,用接口的例子,再通过分析例子,一步一步进行讲解。

二、使用接口,规范相同行为。
假设,我们要做一个苹果自动贩卖机(自动贩卖机简称贩卖机)和葡萄贩卖机。那么,良好的代码设计,肯定不是一上来,就开始创建一个类,然后,开始写方法,做功能。我们应该,首先,定义一个贩卖机的协议 。规范两种不同水果贩卖机的行为。比如,都有启动start()、贩卖process()、停止stop()的功能。接口定义如下:


/**
 * @author Lyf
 * @describe 贩卖机协议
 */
public interface IVending {

    // 启动机器
    void start();
    // 贩卖水果
    void process();
    // 暂停机器
    void stop();

}

下一步,我们先来创建,苹果贩卖机,代码如下:

/**
 * @author Lyf
 * @describe 苹果贩卖机
 */
public class AppleVending implements IVending {

    @Override
    public void start() {
        System.out.println("启动,贩卖机。");
    }

    @Override
    public void process() {
        System.out.println("正在卖苹果....");
    }

    @Override
    public void stop() {
        System.out.println("关闭,贩卖机。");
    }

}

下一步,我们再来创建,葡萄贩卖机,代码如下:


/**
 * @author Lyf
 * @describe 葡萄贩卖机
 */
public class GrapeVending implements IVending {

    @Override
    public void start() {
        System.out.println("启动,贩卖机。");
    }

    @Override
    public void process() {
        System.out.println("正在卖葡萄....");
    }

    @Override
    public void stop() {
        System.out.println("关闭,贩卖机。");
    }

}

乍眼一看,好像没啥毛病。但只要你仔细一看,就会发现。两种贩卖机的,start()和stop()方法的代码,重复了。这就是使用接口,带来的问题。共同的协议行为有了,但是,相同的动作,却要重复的写。还好,现在只有两种。如果种类,变多,接口协议也变多,那重复的代码量,是相当可怕的。所以,为了解决这个问题,我们引出一种设计模式:模板类模式(Template)。也即是,采用抽象类的方法,来解决这一问题,因为抽象类可以包含实现,而接口不行。这个例子,只是具有模板类的一些相似思维,不是特别相似,请忽略。

三、使用抽象类,解决代码重复的问题。
首先,我们需要,创建一个抽象类。然后,把共同的动作,在抽象类里面实现。抽象类的代码如下:


/**
 * @author Lyf
 * @describe 抽象的贩卖机类
 */
public abstract class AbstractVending {

    public void start() {
        System.out.println("启动,贩卖机。");
    }

    // 卖什么,由子类自己去决定
    public abstract void process();

    public void stop() {
        System.out.println("关闭,贩卖机。");
    }

}

下一步,我们让两个子类去继承它,两个子类的代码如下:


/**
 * @author Lyf
 * @describe 葡萄贩卖机,继承自贩卖机抽象类
 */
public class GrapeVendingImpl extends AbstractVending {

    @Override
    public void start() {
        System.out.println("启动,贩卖机。");
    }

    @Override
    public void process() {
        System.out.println("正在卖葡萄....");
    }

    @Override
    public void stop() {
        System.out.println("关闭,贩卖机。");
    }

}

/**
 * @author Lyf
 * @describe 苹果贩卖机,继承自贩卖机抽象类
 */
public class AppleVendingImpl extends AbstractVending {

    @Override
    public void start() {
        System.out.println("启动,贩卖机。");
    }

    @Override
    public void process() {
        System.out.println("正在卖苹果....");
    }

    @Override
    public void stop() {
        System.out.println("关闭,贩卖机。");
    }

}

很明显,通过抽象类的方式,可以解决第二条,使用接口,所带来的代码重复问题。但这种做法,虽然解决了代码重复的问题,却引来另一个问题。即两个AbstractVending类的子类,无法再被其它类继承。这就无法达到,我们一开始所说的,要集接口和抽象类优势的说法。这时候,就该轮到Skeletal Implementation模式,粉墨登场,啪啪啪(掌声响起来)

四、使用Skeletal Implementation模式,集二、三之所长。
实现Skeletal Implementation模式,主要分三步走:

  1. 创建一个接口,即共同协议(有些时候,为了解决类无法多继承的问题,这个协议也可以不用)。
  2. 创建一个抽象类,并实现1所创建的接口,然后,把通用行为写在里面,并抽出需要子类自己定制的抽象方法。(听起来,跟模板类差不多)
  3. 在本该继承这个抽象类的子类里面,创建一个私有内部类,在这个私有内部类里面,重写,填充抽象方法。然后,由这个子类,扩展1所说的接口,然后,1所说的接口所带的方法实现,代理给这个私有内部类去做。

第3步,听起来好复杂。还是看看代码,比较好理解。下面,将会重写上面所写的GrapeVending和AppleVending的实现。你可以通过对比,来理解两者的区别


/**
 * @author Lyf
 * @describe 苹果贩卖机
 */
public class AppleVending implements IVending {

    @Override
    public void start() {
        _abstractVending.start();
    }

    @Override
    public void process() {
        _abstractVending.process();
    }

    @Override
    public void stop() {
        _abstractVending.stop();
    }

    private AbstractVending _abstractVending = new AbstractVending() {

        @Override
        public void process() {
            System.out.println("正在卖苹果....");
        }
    };

}

/**
 * @author Lyf
 * @describe 葡萄贩卖机
 */
public class GrapeVending implements IVending {

    @Override
    public void start() {
        _abstractVending.start();
    }

    @Override
    public void process() {
        _abstractVending.process();
    }

    @Override
    public void stop() {
        _abstractVending.stop();
    }


    private AbstractVending _abstractVending = new AbstractVending() {

        @Override
        public void process() {
            System.out.println("正在卖葡萄....");
        }
    };

}

这样做,是不是,既拥有抽象类的共同行为,又可以实现多继承(fake)? 有些同学可能会说,是的。但这太抽象了,在实际开发中有毛用。。。。 因为现在已经是20:23pm了,实战的举例,我明天会再补上。欢迎明天18:00后,再重新来看一下,实际运用的例子(实用,简单,好理解),谢谢收看。


参考资料:

猜你喜欢

转载自blog.csdn.net/l_o_s/article/details/80901441