资讯专栏INFORMATION COLUMN

抽象类和模板方法模式

only_do / 596人阅读

摘要:抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展改造,但子类总体上会大致保留抽象类的行为方式。稍微专业一点的定义就是模板方法模式,在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。

抽象方法和抽象类

抽象类:用abstract修饰符修饰的类,如:

public abstract class GeneralService {
    
}

抽象方法:用abstract修饰符修饰的方法,抽象方法不能有方法体,如:

public abstract void service();

抽象类和抽象方法的规则如下:

必须用abstract修饰符修饰

抽象类不一定包含抽象方法,但含有抽象方法的类一定是抽象类

抽象类不能被实例化

抽象类的构造器不能用于创建对象,主要是用于被其子类调用

下面定义一个Shape抽象类:

/**
 * 定义一个抽象类,用于描述抽象概念的“形状”
 */
public abstract class Shape {

    // 形状的    颜色
    private String color;

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    // 带参构造器
    public Shape(String color) {
        this.color = color;
    }

    // 定义一个计算周长的抽象方法
    public abstract double calPerimeter();
}

上面的Shape类中包含了一个抽象方法calPerimeter(),所以Shape类只能是抽象类。Shape类中既包含初始化块,又包含构造器,不过这些都不是在创建Shape对象时被调用的,而是在创建其子类对象时被调用。

下面定义一个Triangle类和一个Circle类,让他们继承Shape类,并实现Shape中的抽象方法calPerimeter()

/**
 * 定义一个三角形类,继承自形状类
 */
public class Triangle extends Shape {

    // 定义三角形的三条边
    private double a;
    private double b;
    private double c;

    public Triangle(String color, double a, double b, double c) {
        super(color);
        this.a = a;
        this.b = b;
        this.c = c;
    }

    @Override
    public double calPerimeter() {
        return a + b + c;
    }
}
/**
 * 定义一个圆形类,继承自形状类
 */
public class Circle extends Shape {

    // 定义圆的半径
    private double radius;

    public Circle(String color, double radius) {
        super(color);
        this.radius = radius;
    }

    @Override
    public double calPerimeter() {
        return 2 * Math.PI * this.radius;
    }
}

Shape(形状)类是一个抽象的概念,Triangle(三角形)类和Circle(圆形)类是Shape的具象,它们都各自实现了Shape的calPerimeter()方法,两者计算周长的公式不一样。

下面是测试类:

/**
 * 测试类
 */
public class Test {
    public static void main(String[] args) {
        Shape s1 = new Triangle("黄色", 3.0, 4.0, 5.0);
        Shape s2 = new Circle("红色", 3);
        System.out.println("三角形s1的颜色:" + s1.getColor() + ",周长:" + s1.calPerimeter());
        System.out.println("圆形s2的颜色:" + s2.getColor() + ",周长:" + s2.calPerimeter());
    }
}

输出结果:

三角形s1的颜色:黄色,周长:12.0
圆形s2的颜色:红色,周长:18.84955592153876

当使用abstract修饰类时,表明这个类是抽象类,只能被继承;当使用abstract修饰方法时,表明这个方法必须由其子类实现(重写)。

final修饰的类不能被继承,final修饰的方法不能被重写,因此final和abstract不能同时使用。

当使用static修饰一个方式时,表示这个方法是类方法,可以通过类直接调用而无需创建对象。但如果该方法被定义成抽象的,则将导致通过该类来调用该方法时出现错误(调用了一个没有方法体的方法肯定会引起错误),因此,static和abstract不能同时修饰某个方法。

abstract关键字修饰的方法必须由其子类重写才有意义,因此abstract方法不能定义成private访问权限,即private和abstract不能同时修饰某个方法、

抽象类的作用

抽象类是从多个具体类中抽象出来的父类,它具有更高层次的抽象,描述了一组事物的共性。

抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会大致保留抽象类的行为方式。

模板方法模式

如果编写一个抽象父类,父类提供了多个子类的通用方法,并把一个或多个方法留给其子类去实现,这就是模板模式,是一种十分常见且简单的设计模式。

稍微专业一点的定义就是:

模板方法模式,在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

下面再介绍一个模板方法模式的范例,在这个范例中,我们把做菜这个过程分为三个步骤:

备料

烹制

装盘

这三部就是算法的骨架。然而做不同的菜,需要备的料,烹制的方法,以及如何装盘都是不同的,做不同的菜时,需要有不一样的实现。

先来写一个抽象的做菜父类,代码如下:

/**
 * 定义做菜抽象类
 */
public abstract class DodishTemplate {    
    /**
     * 模板方法,封装了做菜的算法
     * 用final关键字进行修饰,避免子类修改算法的顺序
     * 模板方法定义了一连窜的步骤,每一个步骤由一个方法代表
     */
    protected final void dodish(){
        this.preparation();
        this.doing();
        this.sabot();
    }
    
    /**
     * 备料
     */
    public abstract void preparation();
    
    /**
     * 烹制
     */
    public abstract void doing();
    
    /**
     * 装盘
     */
    public abstract void sabot();
}

下面再定义做番茄炒蛋类和做红烧肉类并实现父类中的抽象方法:

/**
 * 做番茄炒蛋类
 */
public class EggsWithTomato extends DodishTemplate{

    @Override
    public void preparation() {
        System.out.println("洗并切西红柿,打鸡蛋。");
    }

    @Override
    public void doing() {
        System.out.println("鸡蛋倒入锅里,然后倒入西红柿一起炒。");
    }

    @Override
    public void sabot() {
        System.out.println("将炒好的番茄炒蛋装入碟子里,撒上香葱。");
    }
}
/**
 * 做红烧肉类
 */
public class Bouilli extends DodishTemplate{

    @Override
    public void preparation() {
        System.out.println("切猪肉和土豆。");
    }

    @Override
    public void doing() {
        System.out.println("将切好的猪肉倒入锅中炒一会然后倒入土豆连炒带炖。");
    }

    @Override
    public void sabot() {
        System.out.println("将做好的红烧肉盛进碗里,撒上白芝麻");
    }
}

在测试类中我们来做菜:

public class App {
    public static void main(String[] args) {
        DodishTemplate eggsWithTomato = new EggsWithTomato();
        eggsWithTomato.dodish();
        
        System.out.println("-----------------------------");
        
        DodishTemplate bouilli = new Bouilli();
        bouilli.dodish();
    }
}

运行结果:

洗并切西红柿,打鸡蛋。
鸡蛋倒入锅里,然后倒入西红柿一起炒。
将炒好的番茄炒蛋装入碟子里,撒上香葱。
-----------------------------
切猪肉和土豆。
将切好的猪肉倒入锅中炒一会然后倒入土豆连炒带炖。
将做好的红烧肉盛进碗里,撒上白芝麻

从这个案例我们可以看到,DodishTemplate类里定义了做菜的通用算法,而一些具体的实现细节则推迟到了其子类(EggsWithTomato和Bouilli)中。也就是说,模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/74189.html

相关文章

  • 7. 初步理解面向对象 【连载 7】

    摘要:是一种典型的面向对象编程语言。这篇文章主要是来初步理解一下面向对象的思维为下面的内容先给一个基础。针对面向对象编程的更多内容,会在后面的文章里面详细解释。他们都称之为对象。之后,我们再用编程语言,把这种映射编写出来,就是的面向对象编程啦。 showImg(https://segmentfault.com/img/remote/1460000012983458?w=900&h=500);...

    keelii 评论0 收藏0
  • 抽象类和接口的区别

    摘要:抽象类是对整个类整体进行抽象,包括属性行为,但是接口却是对类局部行为进行抽象。因此最好的解决办法是单独将报警设计为一个接口,包含行为设计为单独的一个抽象类,包含和两种行为。 抽象类和接口的区别 1.语法层面上的区别   1)抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法;   2)抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是pub...

    ThinkSNS 评论0 收藏0
  • java抽象类和接口总结

    摘要:如果一个非抽象类遵循了某个接口,就必须实现该接口中的所有方法。抽象类是对整个类整体进行抽象,包括属性行为,但是接口却是对类局部行为进行抽象。因此最好的解决办法是单独将报警设计为一个接口,包含行为设计为单独的一个抽象类,包含和两种行为。 一、抽象类 二、接口 三、抽象类和接口的区别 一、抽象类 在了解抽象类之前,先来了解一下抽象方法。抽象方法是一种特殊的方法:它只有声明,而没有具体的实现...

    荆兆峰 评论0 收藏0
  • Java抽象类与接口的区别

    摘要:很多常见的面试题都会出诸如抽象类和接口有什么区别,什么情况下会使用抽象类和什么情况你会使用接口这样的问题。在讨论它们之间的不同点之前,我们先看看抽象类接口各自的特性。抽象类抽象类是用来捕捉子类的通用特性的。 很多常见的面试题都会出诸如抽象类和接口有什么区别,什么情况下会使用抽象类和什么情况你会使用接口这样的问题。本文我们将仔细讨论这些话题。 在讨论它们之间的不同点之前,我们先看看抽象类...

    Ethan815 评论0 收藏0

发表评论

0条评论

only_do

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<