iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 21
0
Blockchain

Smart Contract 開發 - 使用 Solidity系列 第 21

開發智能合約 - 繼承、抽象合約 (Day21)

繼承 (Inheritance)

Solidity 透過複製程式碼和多型 (polymorphism),來支援多重繼承。

當一個合約繼承多個合約,實際上只會有一個合約被創建,合約會將所有繼承的合約的程式碼,複製到自己身上。

關於 Solidity 對於繼承的設計與 Python 非常相似,尤其多重繼承的部分。

下面的範例,有更詳細的解釋。

pragma solidity ^0.4.22;

contract owned {
    constructor() { owner = msg.sender; }
    address owner;
}

// 使用 `is` 關鍵字來繼承另一個合約,這樣就可以不需要透過 `this`,也可以存取所有非私有的狀態變數和函示。
contract mortal is owned {
    function kill() {
        if (msg.sender == owner) selfdestruct(owner);
    }
}

// 抽象合約,僅定義函示,但沒有實作函示。如果合約沒有實作所有的函示,那就只能使用介面 (interface)。
contract Config {
    function lookup(uint id) public returns (address adr);
}

contract NameReg {
    function register(bytes32 name) public;
    function unregister() public;
 }

// 可以使用多重繼承。雖然 named 合約跟 mortal 合約都有繼承 owned 合約,但實際 named 合約只有一個 owned 合約的實例 (instance),與 C++ 的虛擬繼承 (virtual inheritance) 原理一樣。
contract named is owned, mortal {
    constructor(bytes32 name) {
        Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970);
        NameReg(config.lookup(1)).register(name);
    }

    // 函示可以被另一個相同名字且相同參數數量的函示覆蓋 (override),如果 override 的函示有不同的回傳值型態會發生錯誤。
    function kill() public {
        if (msg.sender == owner) {
            Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970);
            NameReg(config.lookup(1)).unregister();

            // 可以用這個方式呼叫特定的 overridden 函示
            mortal.kill();
        }
    }
}

// 如果建構子接受傳入參數,則須要在合約的最前頭指定參數的值。
contract PriceFeed is owned, mortal, named("GoldFeed") {
   function updateInfo(uint newInfo) public {
      if (msg.sender == owner) info = newInfo;
   }

   function get() public view returns(uint r) { return info; }

   uint info;
}

抽象合約 (Abstract Contracts)

當合約被標註為抽象合約時,代表的是至少有一個函示為實現,下面範例無法編譯:

pragma solidity ^0.4.0;

contract Feline {
    function utterance() public returns (bytes32);
}

修正後,如下所示:

pragma solidity ^0.4.0;

contract Feline {
    function utterance() public returns (bytes32);
}

contract Cat is Feline {
    function utterance() public returns (bytes32) { return "miaow"; }
}

如果合約繼承抽象合約後,並沒有實現所有未實現的函示,那麼自己也會是一個抽象合約。

注意:沒有實現的函示與 Function Type 不同,即使它們的語法看起來很相似。

沒有實現的函示範例 (a function declaration):

function foo(address) external returns (address);

函示類型 (Function Type) 的範例:

function(address) external returns (address) foo;

抽象合約將合約定義和合約的實現解耦 (decouple),提供更好的擴充性、自我描述 (self-documentation) 或像 Template method 模式一樣,或減少重複的程式碼。

抽象合約和介面在定義方法的時候非常好用,這樣的設計就像是告訴所有繼承它的子合約,它應該要實作哪些方法 (method)。


上一篇
開發智能合約 - 實戰練習「簡易版 King of the Ether (2/2)」(Day20)
下一篇
開發智能合約 - 介面 (Day22)
系列文
Smart Contract 開發 - 使用 Solidity31

尚未有邦友留言

立即登入留言