美文网首页程序员Android技术知识Android知识
详解java抽象类和模板方法设计模式

详解java抽象类和模板方法设计模式

作者: kikoroc | 来源:发表于2016-05-10 13:14 被阅读957次

    原文http://kikoroc.com/2016/05/10/java-abstract-classes-and-template-method-design-pattern.html发表在我的个人博客。

    java中的抽象类是不能被实例化的,意味着你不能创建一个抽象类的实例。抽象类的目的是为了作为子类的基类,本文将介绍如何在java中创建抽象类以及使用抽象类的一些规则。

    定义抽象类

    java中定义抽象类很简单,在类的定义中加入abstract关键字即可。下面是java中定义抽象类的实例:

    public abstract class MyAbstractClass {
    
    }
    

    上面就是java中定义抽象类的所有内容,现在你不能创建MyAbstractClass的实例,所以下面的代码不再合法:

    MyAbstractClass myClassInstance =
        new MyAbstractClass(); // not valid
    

    如果你尝试编译上面的代码,编译器将报错,告诉你不能实例化MyAbstractClass,因为它是一个抽象类。

    抽象方法

    抽象类中可以定义抽象方法,定义抽象方法也很简单,在方法的定义前面添加abstract关键字即可,下面是定义抽象方法的实例:

    public abstract class MyAbstractClass {
        public abstract void abstractMethod();
    }
    

    抽象方法是没有具体实现细节的,仅仅是方法的定义,跟java interface中的方法类似。

    如果一个类中包含抽象方法,那么这个类必须定义为abstract抽象类。但并不要求抽象类中所有方法都是抽象方法。抽象类中既可包含抽象方法也可以包含非抽象方法。

    抽象类的子类必须实现(override)父抽象类中所有的抽象方法,非抽象方法直接继承到子类,如有需要也可以重写非抽象方法。
    下面是MyAbstractClass抽象类子类的实例:

    public class MySubClass extends MyAbstractClass {
        public void abstractMethod() {
            System.out.println("My method implementation");
        }
    }
    

    注意MySubClass必须实现抽象父类MyAbstractClass中的抽象方法abstractMethod

    抽象类的子类不用实现父类中的抽象方法唯一的情况是:子类也是抽象类。

    抽象类的作用

    抽象类的作用是为了作为子类的基类,这样子类很容易继承父类来做具体的细节实现。比如,假如一个流程需要3个步骤:

    1. 具体动作之前的操作
    2. 执行具体的动作
    3. 具体动作结束后的操作

    如果第一步和第三步的实现总是相同的,那么这个三步走的流程可以用java抽象类这么实现:

    public abstract class MyAbstractProcess {
        public void process() {
            stepBefore();
            action();
            stepAfter();
        }
    
        public void stepBefore() {
            //直接在抽象父类中实现
        }
    
        public abstract void action(); // 由子类来实现
    
        public void stepAfter() {
            //直接在抽象父类中实现
        }
    }
    

    注意到action()方法是抽象方法,MyAbstractProcess的子类现在可以继承MyAbstractProcess然后重写action()方法。

    当子类的process()方法被调用,那么整个三步走的流程都会调用,包括父类中的stepBefore()stepAfter()方法以及子类中实现的action()方法。

    当然,MyAbstractProcess作为基类不一定非要是抽象类,action()也不是必须要是抽象方法的,你可以直接使用普通类来实现同样的逻辑,但是,让具体的实现方法和类抽象化,你可以清晰的告诉使用者这个类不能直接使用,它应该作为基类,然后让子类来实现抽象方法。

    上面实例中action()方法没有默认的实现,是某些情况下父类也可以有默认的实现,子类也可以重写该方法,当然这种情况下这个方法就不能再定义为抽象方法,但是你依旧可以将类定义为抽象类,即使类里面没有抽象方法。

    下面是一个更具体的实例用来打开一个URL,处理数据然后关闭链接。

    public abstract class URLProcessorBase {
    
        public void process(URL url) throws IOException {
            URLConnection urlConnection = url.openConnection();
            InputStream input = urlConnection.getInputStream();
    
            try{
                processURLData(input);
            } finally {
                input.close();
            }
        }
    
        protected abstract void processURLData(InputStream input)
            throws IOException;
    
    }
    

    processURLData()是个抽象方法,URLProcessorBase是个抽象类,那么URLProcessorBase的子类必须要实现父类的抽象方法processURLData(),因为它是一个抽象方法。

    URLProcessorBase的子类可以直接处理url下载的数据,而不用担心如何打开和关闭url链接。因为这些都由父类实现了,子类只需要关注processURLData()方法中的InputStream对象,这样可以使得可以很方便的实现url数据处理的子类。

    下面是一个子类的实例:

    public class URLProcessorImpl extends URLProcessorBase {
    
        @Override
        protected void processURLData(InputStream input) throws IOException {
            int data = input.read();
            while(data != -1){
                System.out.println((char) data);
                data = input.read();
            }
        }
    }
    

    注意到子类仅仅只需要重写processURLData()方法,其他的方法直接从父类继承过来。

    下面是如何使用URLProcessorImpl类的实例:

    URLProcessorImpl urlProcessor = new URLProcessorImpl();
    
    urlProcessor.process(new URL("http://jenkov.com"));
    

    父类URLProcessorBase中实现的process()方法直接调用,然后会调用URLProcessorImpl类中的processURLData()方法

    抽象类和模板方法设计模式

    上面的URLProcessorBase类的实例代码实际上就是一种模板方法设计模式,模板方法设计模式提供了流程中一部分的实现逻辑,然后子类可以继承基类来完成全部的实现。


    译自:http://tutorials.jenkov.com/java/abstract-classes.html,能力有限,详细细节可以查阅原文。


    模板方法设计模式简述

    上面提到抽象类和模板方法设计模式的关系,这里也简单总结下模板设计方法。

    概述

    定义一个操作中的算法框架,而将具体的实现细节推迟到子类中实现。这样可以使子类不改变算法结构来重新定义算法的某些特定逻辑。

    角色

    抽象类:实现模板方法,定义算法框架。
    具体类:实现抽象类中的抽象方法,完成完整的算法逻辑。

    优缺点及适用场景

    1. 优点
    • 模板方法通过将不变的行为在基类中实现,去除了子类中的重复代码;
    • 在子类中负责具体的细节实现,有利于算法的扩展;
    • 通过父类调用子类实现的操作,通过子类扩展增加新的行为,符合“开闭原则”。
    1. 缺点
      每个不同的实现都需要定义一个子类,这会导致类个数的增加,设计更加抽象。
    2. 适用场景
    • 在某些类的算法中,用了相同的方法,造成代码的重复;
    • 控制子类的扩展,子类必须遵守算法的框架规则。

    代码示例

    抽象类:

    public abstract class AbstractClass {
        // 定义算法的一些抽象行为,让子类负责实现
        public abstract void step1();
        public abstract void step2();
        // 模板方法,定义算法的框架,调用抽象方法,他们将在子类中实现。
        public void TemplateMethod() {
            step1();
            step2();
            System.out.println("Completed.");
        }
    }
    

    具体类A:

    public class ConcreteClassA extends AbstractClass {
        @Override
        public void step1() {
            System.out.println("step1 of ConcreteClassA");
        }
        @Override
        public void step2() {
            System.out.println("step2 of ConcreteClassA");
        }
    }
    

    具体类B:

    public class ConcreteClassB extends AbstractClass {
        @Override
        public void step1() {
            System.out.println("step1 of ConcreteClassB");
        }
        @Override
        public void step2() {
            System.out.println("step2 of ConcreteClassB");
        }
    }
    

    调用:

    AbstractClass abstractClass = new ConcreteClassA();
    // 调用A的实现
    abstractClass.TemplateMethod();
    
    abstractClass = new ConcreteClassB();
    // 调用B的实现
    abstractClass.TemplateMethod();
    

    输出:

    step1 of ConcreteClassA
    step2 of ConcreteClassA
    Completed.
    step1 of ConcreteClassB
    step2 of ConcreteClassB
    Completed.
    

    与抽象工厂模式的区别

    模板方法是一种算法模板,针对算法的扩展重写;而抽象工厂模式是为了创建对象,针对对象的扩展重写。

    相关文章

      网友评论

        本文标题:详解java抽象类和模板方法设计模式

        本文链接:https://www.haomeiwen.com/subject/lbkyrttx.html