| title | 13. Herança | ||||
|---|---|---|---|---|---|
| tags |
|
Recentemente, tenho estudado Solidity novamente para revisar os detalhes e escrever um "WTF Solidity Introdução Simples" para iniciantes (programadores experientes podem procurar outros tutoriais). Serão lançadas de 1 a 3 aulas por semana.
Twitter: @0xAA_Science
Comunidade: Discord|Grupo do WeChat|Site oficial wtf.academy
Todo o código e tutoriais estão disponíveis no GitHub: github.com/AmazingAng/WTF-Solidity
Nesta aula, vamos falar sobre herança no Solidity, incluindo herança simples, herança múltipla e herança de modificadores e construtores.
A herança é uma parte importante da programação orientada a objetos, pois permite reduzir significativamente a repetição de código. Se considerarmos os contratos como objetos, o Solidity também é uma linguagem de programação orientada a objetos e suporta herança.
-
virtual: Se você deseja que uma função do contrato pai possa ser sobrescrita pelo contrato filho, é necessário adicionar a palavra-chavevirtualà função do contrato pai. -
override: Se o contrato filho sobrescrever uma função do contrato pai, é necessário adicionar a palavra-chaveoverrideà função do contrato filho.
Observação: Ao usar override para modificar uma variável pública, a função getter com o mesmo nome será sobrescrita. Por exemplo:
mapping(address => uint256) public override balanceOf;Vamos começar escrevendo um contrato avô simples chamado Yeye, que contém um evento Log e três funções: hip(), pop() e yeye(), todas elas retornam "Yeye".
contract Yeye {
event Log(string msg);
function hip() public virtual {
emit Log("Yeye");
}
function pop() public virtual {
emit Log("Yeye");
}
function yeye() public virtual {
emit Log("Yeye");
}
}Agora, vamos definir um contrato pai chamado Baba, que herda o contrato Yeye. A sintaxe para isso é contract Baba is Yeye, que é bastante intuitiva. No contrato Baba, vamos sobrescrever as funções hip() e pop() adicionando a palavra-chave override e alterando a saída para "Baba". Também adicionaremos uma nova função chamada baba, que também retorna "Baba".
contract Baba is Yeye {
function hip() public virtual override {
emit Log("Baba");
}
function pop() public virtual override {
emit Log("Baba");
}
function baba() public virtual {
emit Log("Baba");
}
}Ao implantar o contrato, podemos ver que o contrato Baba possui quatro funções. As funções hip() e pop() foram sobrescritas com sucesso para retornar "Baba", enquanto a função yeye() herdada ainda retorna "Yeye".
Os contratos no Solidity podem herdar de vários contratos. Aqui estão as regras:
-
A ordem de herança deve ser da classe mais alta para a mais baixa. Por exemplo, se quisermos criar um contrato chamado
Erzique herda os contratosYeyeeBaba, devemos escrevercontract Erzi is Yeye, Babae nãocontract Erzi is Baba, Yeye, caso contrário, ocorrerá um erro. -
Se uma função estiver presente em vários contratos pai, como as funções
hip()epop()no exemplo, ela deve ser sobrescrita no contrato filho, caso contrário, ocorrerá um erro. -
Ao sobrescrever uma função que tem o mesmo nome em vários contratos pai, a palavra-chave
overridedeve ser seguida pelos nomes de todos os contratos pai, por exemplo,override(Yeye, Baba).
Exemplo:
contract Erzi is Yeye, Baba {
function hip() public virtual override(Yeye, Baba) {
emit Log("Erzi");
}
function pop() public virtual override(Yeye, Baba) {
emit Log("Erzi");
}
}Podemos ver que o contrato Erzi sobrescreve as funções hip() e pop(), alterando a saída para "Erzi" e também herda as funções yeye() e baba() dos contratos Yeye e Baba, respectivamente.
Os modificadores no Solidity também podem ser herdados. O uso é semelhante à herança de funções, basta adicionar as palavras-chave virtual e override nos locais apropriados.
contract Base1 {
modifier exactDividedBy2And3(uint _a) virtual {
require(_a % 2 == 0 && _a % 3 == 0);
_;
}
}
contract Identifier is Base1 {
// Calcula os valores de um número dividido por 2 e por 3, mas o parâmetro deve ser um múltiplo de 2 e 3
function getExactDividedBy2And3(uint _dividend) public exactDividedBy2And3(_dividend) pure returns(uint, uint) {
return getExactDividedBy2And3WithoutModifier(_dividend);
}
// Calcula os valores de um número dividido por 2 e por 3
function getExactDividedBy2And3WithoutModifier(uint _dividend) public pure returns(uint, uint){
uint div2 = _dividend / 2;
uint div3 = _dividend / 3;
return (div2, div3);
}
}O contrato Identifier pode usar diretamente o modificador exactDividedBy2And3 do contrato pai no código ou substituí-lo usando a palavra-chave override:
modifier exactDividedBy2And3(uint _a) override {
_;
require(_a % 2 == 0 && _a % 3 == 0);
}Existem duas maneiras de um contrato filho herdar o construtor do contrato pai. Vamos dar um exemplo simples: o contrato pai A possui uma variável de estado a que é determinada pelos parâmetros do construtor:
// Herança de construtores
abstract contract A {
uint public a;
constructor(uint _a) {
a = _a;
}
}-
Ao herdar, declaramos os parâmetros do construtor pai, por exemplo:
contract B is A(1) -
No construtor do contrato filho, declaramos os parâmetros do construtor pai, por exemplo:
contract C is A { constructor(uint _c) A(_c * _c) {} }
Existem duas maneiras de um contrato filho chamar funções do contrato pai: chamada direta e uso da palavra-chave super.
-
Chamada direta: O contrato filho pode chamar diretamente uma função do contrato pai usando o formato
nomeDoContratoPai.nomeDaFuncao(), por exemploYeye.pop().function callParent() public { Yeye.pop(); }
-
Palavra-chave
super: O contrato filho pode usar a palavra-chavesuper.nomeDaFuncao()para chamar a função do contrato pai mais próximo. A ordem de herança no Solidity é da direita para a esquerda na declaração, por exemplo,contract Erzi is Yeye, Baba, entãoBabaé o contrato pai mais próximo esuper.pop()chamaráBaba.pop()em vez deYeye.pop():function callParentSuper() public { // Chamará a função do contrato pai mais próximo, Baba.pop() super.pop(); }
Na programação orientada a objetos, herança de diamante (ou herança em diamante) refere-se a uma classe derivada que tem duas ou mais classes base.
Ao usar a palavra-chave super em uma cadeia de herança múltipla de diamante, é importante observar que o uso de super chamará todas as funções relevantes em cada contrato da cadeia de herança, e não apenas a função do contrato pai mais próximo.
Vamos começar escrevendo um contrato chamado God, em seguida, escreveremos dois contratos Adam e Eve que herdam o contrato God. Por fim, criaremos um contrato chamado people que herda Adam e Eve, e cada contrato terá duas funções: foo e bar.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
/* Cadeia de herança:
God
/ \
Adam Eve
\ /
people
*/
contract God {
event Log(string message);
function foo() public virtual {
emit Log("God.foo called");
}
function bar() public virtual {
emit Log("God.bar called");
}
}
contract Adam is God {
function foo() public virtual override {
emit Log("Adam.foo called");
super.foo();
}
function bar() public virtual override {
emit Log("Adam.bar called");
super.bar();
}
}
contract Eve is God {
function foo() public virtual override {
emit Log("Eve.foo called");
super.foo();
}
function bar() public virtual override {
emit Log("Eve.bar called");
super.bar();
}
}
contract people is Adam, Eve {
function foo() public override(Adam, Eve) {
super.foo();
}
function bar() public override(Adam, Eve) {
super.bar();
}
}
Neste exemplo, chamar super.bar() no contrato people chamará as funções Eve, Adam e, por fim, God.
Embora Eve e Adam sejam contratos filhos de God, o contrato God só será chamado uma vez durante todo o processo. Isso ocorre porque o Solidity adota uma abordagem semelhante ao Python, forçando um DAG (grafo acíclico direcionado) composto por classes base para garantir uma ordem específica. Para obter mais detalhes, consulte a documentação oficial do Solidity.
-
Exemplo de herança simples: observe que o contrato
Babapossui as funções herdadas deYeye -
Para o exemplo de herança múltipla, você pode seguir as etapas do exemplo de herança simples para implantar o contrato
Erzie observar as funções expostas e tentar chamá-las para verificar os logs. -
Exemplo de herança de modificadores
-
Exemplo de herança de construtores
-
Exemplo de chamada de função do contrato pai
-
Exemplo de herança de diamante
Nesta aula, apresentamos os conceitos básicos da herança no Solidity, incluindo herança simples, herança múltipla, herança de modificadores e construtores, chamada de funções do contrato pai e o problema da herança de diamante na herança múltipla.









