Наследование контракта

Объект контракта Solidity поддерживает наследование, которое представляет собой механизм расширения базового контракта дополнительной функциональностью. Чтобы использовать наследование, укажите родительский контракт с помощью ключевого слова is:

contract Child is Parent {
    ...
}

При такой конструкции дочерний контракт наследует все методы, функциональность и переменные родительского. Solidity также поддерживает множественное наследование, которое может быть задано именами контрактов, разделенными запятыми, после ключевого слова is:

contract Child is Parent1, Parent2 {
    ...
}

Наследование контрактов позволяет нам писать контракты таким образом, чтобы достичь модульности, расширяемости и повторного использования. Мы начинаем с простых контрактов, реализующих наиболее общие возможности, а затем расширяем их, наследуя эти возможности в более специализированных контрактах.

В нашем контракте "Кран" мы представили конструктор и деструктор, а также контроль доступа для владельца, назначаемого при строительстве. Эти возможности являются довольно общими: они будут у многих контрактов. Мы можем определить их как общие контракты, а затем использовать наследование, чтобы распространить их на контракт Faucet.

Мы начинаем с определения базового контракта Owned, который имеет переменную-владельца, устанавливая ее в конструкторе контракта:

contract Owned {
	address owner;

	// Contract constructor: set owner
	constructor() {
		owner = msg.sender;
	}

	// Access control modifier
	modifier onlyOwner {
	    require(msg.sender == owner);
	    _;
	}
}

Далее мы определяем базовый контракт Mortal, который наследует Owned:

contract Mortal is Owned {
	// Contract destructor
	function destroy() public onlyOwner {
		selfdestruct(owner);
	}
}

Как вы можете видеть, контракт Mortal может использовать модификатор функции onlyOwner, определенный в Owned. Косвенно он также использует адресную переменную owner и конструктор, определенный в Owned. Наследование делает каждый контракт более простым и сфокусированным на его конкретной функциональности, позволяя нам управлять деталями модульным способом.

Теперь мы можем еще больше расширить контракт Owned, унаследовав его возможности в Faucet:

contract Faucet is Mortal {
    // Give out ether to anyone who asks
    function withdraw(uint withdraw_amount) public {
        // Limit withdrawal amount
        require(withdraw_amount <= 0.1 ether);
        // Send the amount to the address that requested it
        msg.sender.transfer(withdraw_amount);
    }
    // Accept any incoming amount
    receive () external payable {}
}

Наследуя Mortal, который, в свою очередь, наследует Owned, контракт Faucet теперь имеет функции конструктора и уничтожения, а также определенного владельца. Функциональность такая же, как и в случае, когда эти функции были в Faucet, но теперь мы можем повторно использовать эти функции в других контрактах без необходимости писать их заново. Повторное использование кода и модульность делают наш код чище, легче для чтения и аудита.