Наследование контракта
Объект контракта 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, но теперь мы можем повторно использовать эти функции в других контрактах без необходимости писать их заново. Повторное использование кода и модульность делают наш код чище, легче для чтения и аудита.