我們在前幾篇有介紹到介面的用途,都知道介面可以制定規格,建議可以先複習一下這一篇「
【開發智能合約 — Solidity系列】實作篇Ep.10 — 標準化的介面(Interfaces)」,而這次來介紹一個非常抽象的概念,名為「抽象化合約」,果然如其名! 不太容易理解,這種合約跟介面非常相似,都可以用來制定規格,而不同之處在於「抽象合約」可以被繼承,且「抽象合約」可以實作一些共同方法,但介面是不能實作的,也就是說介面有點設計的意味在,以「車」為例,介面僅設計出草圖,後續交由各種汽車、卡車…,進行不同的實作,只要遵照著規格走就好,怎麼實作就由各家決定,但「抽象合約」還可以先設計出「驅動」的方法,並藉由繼承的方式讓不同的車種皆擁有一樣的「驅動」方式,具有減少共同實作邏輯的特性。
以介面來實現,功能雖廣泛,但…
舉例來說,我們可能會設計一個「車」的介面,這個介面規格規定必須有「驅動」的功能,以燃油車來說會去實作「車」的介面,除了具備「驅動」以外,我們在燃油車的類別還可以實作「添加燃油」的功能,以符合燃油車的特性,那麼假設要製造其他燃油車型時,我們只要繼承「燃油車」這個類別就能基於燃油車進行擴充,以solidity語法設計如下:
// 車的介面
interface ICar {
// 驅動
function drive() external;
}// 燃油車
contract FuelCar is ICar {
// 實作燃油車的驅動方式
function drive() public override {
...
} // 添加燃油
function fill() public {
...
}
}// 一般燃油轎車
contract GeneralCar is FuelCar {
...更多燃油車的方法
}
但…,有沒有發現,這樣的方式雖然非常直觀,但也帶來了層層實作繼承的複雜度與設計規劃的高水準。
我們用抽象合約來實現看看?
我們可以發現到,捨去介面後會由三層降為兩層,主要是因為抽象合約除了可以制定規格以外,也能實現共同方法,讓繼承的類別除了自行實作之外,亦可承襲共同方法。
abstract contract FuelCar {
// 定義驅動的方法,讓繼承類別各自實作
function drive() public virtual; function fill(uint amount) public {
// 添加燃油...
}
}// 一般燃油轎車
contract Car is FuelCar {
function drive() public override {
// 實作一般燃油轎車的驅動方法
}
}
除了能夠操作實作的「驅動」之外,亦可操作到繼承的添加函數。
結語
一開始在看介面與抽象類別時真的是傻傻分不清楚,兩者實在太相似了,因此有些程式語言就乾脆移除抽象類別的使用方式,但又會有別的派系支持抽象類別的使用,才會發現到市面上各種程式語言皆提倡不同的理念,有的程式語言甚至乾脆移除抽象類別的使用情境,例如: Golang…等,對我們來說多學一個念也無彷拉,只要知道其中的核心理念即可得心應手。