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

原文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.

与抽象工厂模式的区别

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容

  • 设计模式汇总 一、基础知识 1. 设计模式概述 定义:设计模式(Design Pattern)是一套被反复使用、多...
    MinoyJet阅读 3,884评论 1 15
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,531评论 18 399
  • 1 场景问题# 1.1 登录控制## 几乎所有的应用系统,都需要系统登录控制的功能,有些系统甚至有多个登录控制的功...
    七寸知架构阅读 1,928评论 3 53
  • 设计模式基本原则 开放-封闭原则(OCP),是说软件实体(类、模块、函数等等)应该可以拓展,但是不可修改。开-闭原...
    西山薄凉阅读 3,739评论 3 13
  • Iterator模式 (迭代器) 一个一个遍历 一个集合类可以遵守 Iterator 协议,并实现一个 Itera...
    SSBun阅读 1,822评论 0 15