抽象類和模板方法設計模式

抽象方法和抽象類

抽象類:用abstract修飾符修飾的類,如:java

public abstract class GeneralService {
    
}

抽象方法:用abstract修飾符修飾的方法,抽象方法不能有方法體,如:算法

public abstract void service();

抽象類和抽象方法的規則以下:設計模式

  1. 必須用abstract修飾符修飾
  2. 抽象類不必定包含抽象方法,但含有抽象方法的類必定是抽象類
  3. 抽象類不能被實例化
  4. 抽象類的構造器不能用於建立對象,主要是用於被其子類調用

下面定義一個Shape抽象類:ide

/**
 * 定義一個抽象類,用於描述抽象概念的「形狀」
 */
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()ui

/**
 * 定義一個三角形類,繼承自形狀類
 */
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()方法,二者計算周長的公式不同。this

下面是測試類:設計

/**
 * 測試類
 */
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());
    }
}

輸出結果:code

三角形s1的顏色:黃色,周長:12.0
圓形s2的顏色:紅色,周長:18.84955592153876
  • 當使用abstract修飾類時,代表這個類是抽象類,只能被繼承;當使用abstract修飾方法時,代表這個方法必須由其子類實現(重寫)。
  • final修飾的類不能被繼承,final修飾的方法不能被重寫,所以final和abstract不能同時使用。
  • 當使用static修飾一個方式時,表示這個方法是類方法,能夠經過類直接調用而無需建立對象。但若是該方法被定義成抽象的,則將致使經過該類來調用該方法時出現錯誤(調用了一個沒有方法體的方法確定會引發錯誤),所以,static和abstract不能同時修飾某個方法。
  • abstract關鍵字修飾的方法必須由其子類重寫纔有意義,所以abstract方法不能定義成private訪問權限,即private和abstract不能同時修飾某個方法、

抽象類的做用

抽象類是從多個具體類中抽象出來的父類,它具備更高層次的抽象,描述了一組事物的共性。對象

抽象類做爲多個子類的通用模板,子類在抽象類的基礎上進行擴展、改造,但子類整體上會大體保留抽象類的行爲方式。

模板方法模式

若是編寫一個抽象父類,父類提供了多個子類的通用方法,並把一個或多個方法留給其子類去實現,這就是模板模式,是一種十分常見且簡單的設計模式。

稍微專業一點的定義就是:

模板方法模式,在一個方法中定義一個算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類能夠在不改變算法結構的狀況下,從新定義算法中的某些步驟。

下面再介紹一個模板方法模式的範例,在這個範例中,咱們把作菜這個過程分爲三個步驟:

  1. 備料
  2. 烹製
  3. 裝盤

這三部就是算法的骨架。然而作不一樣的菜,須要備的料,烹製的方法,以及如何裝盤都是不一樣的,作不一樣的菜時,須要有不同的實現。

先來寫一個抽象的作菜父類,代碼以下:

/**
 * 定義作菜抽象類
 */
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)中。也就是說,模板方法定義了一個算法的步驟,並容許子類爲一個或多個步驟提供實現

相關文章
相關標籤/搜索