WEB3DEV

Cover image for Herança em Smart Contracts com uso da metodologia orientada a objetos
Fatima Lima
Fatima Lima

Posted on • Atualizado em

Herança em Smart Contracts com uso da metodologia orientada a objetos

Quando fazemos deploy de smart contracts na blockchain, o código se torna imutável e permanente. Pode ser executado repetidamente, de forma “Turing-completude”. Uma vez que a EVM
Máquina Virtual da Ethereum)
executa o contrato, ela vai executar as operações necessárias ao custo de gas da conta que o chama.

Os smart contracts podem herdar propriedades de outros contratos, chamadas de atributos, seguindo um projeto orientado a objetos (OOP). Existe um contrato base que é a classe principal que contém o código que pode ser passado para outros contratos. Este é chamado parent contract (contrato pai). Os outros contratos são derivados desse e são chamados child contracts (contratos filho). O filho herda do pai e um pai pode ter vários filhos.

O pai contém funções, com métodos e rotinas que um filho herda e usa. Quando um contrato herda de um outro contrato diz-se que é uma herança única. Também é possível, como em muitos casos, que um filho herde de vários pais e isso é chamado de herança múltipla.

Ordem de Herança

Existe uma ordem específica em que os atributos são herdados de pai para filho. Um pai pode passar qualquer atributo que possua para um filho ou filhos. Entretanto, a relação é de via única, o que significa que um pai não pode herdar do seu próprio filho o que já foi transmitido.

Vamos dizer que tenhamos um contrato base chamado Contrato A, que será o pai. Também temos um Contrato B e um Contrato C. Tanto o Contrato B quanto o Contrato C herdam do Contrato A e são considerados seus filhos.

Image description

Figura 1. A relação dos contratos A, B e C.

O Contrato C também herda do Contrato B, de forma que tenha dois pais. O Contrato C é derivado tanto de A quanto de B. Ele é, então, considerado o mais derivado dentre os três contratos. O Contrato A seria o mais básico já que não é derivado de nenhum outro contrato.

Em termos de avaliar a ordem da herança, sempre se faz do mais básico para o mais derivado, indo da esquerda para a direita. Então, podemos escrever a ordem assim:

A — > B — > C
Enter fullscreen mode Exit fullscreen mode

Herança Única

Aqui está um código exemplo da herança única usando o Contrato A e o Contrato B (todos os códigos desse exemplos estão na Solidity).

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract A {
   function foo() public pure virtual returns (string memory) {
              return "Foo Contract A";
       }

   function bar() public pure returns (string memory) {
             return "Bar Contract A";
       }
   contract B is A {
   function foo() public pure override returns (string memory) {
            return "Foo Contract B";
       }
    }
Enter fullscreen mode Exit fullscreen mode

O Contrato B está herdando do Contrato A. Para permitir a herança, a palavra chave is é adicionada à declaração do contrato. Isso significa:

contract B is A {}
Enter fullscreen mode Exit fullscreen mode

No contrato B filho, temos uma função que também é chamada de foo(). E se nós quisermos ignorar o valor de A? Deve-se usar a palavra chave override. Para que isso funcione, você deve declarar a função em A que B herdar com a palavra chave virtual. Isso permite que B estabeleça seu próprio valor para foo(), que não é o mesmo que A. B pode retornar o valor “Foo Contract B” ao invés de “Foo Contract A”.

B também vai herdar de A a função bar(). Não há necessidade de fazer um override para isso, ele apenas herda a função como is. Isso consequentemente retorna o valor “Bar Contract A”. Se A tivesse outras funções, estas também seriam herdadas por B. B também pode override (ignorar) estas outras funções.

Herança Múltipla

Agora, considere que haja mais contratos com múltiplas heranças. Temos os contratos A, B, C e D. Precisamos levar em consideração a ordem da herança.

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract A {
function foo() public pure virtual returns (string memory) {
    return "Foo Contract A";
    }
function bar() public pure virtual returns (string memory) {
  return "Bar Contract A";
    }
function baz() public pure returns (string memory) {
  return "Baz Contract A";
    }
}
contract B is A {
   function foo() public pure virtual override returns (string      memory) {
   return "Foo Contract B";
   }
   function bar() public pure virtual override returns (string memory) {
   return "Bar Contract B";
   }
}
contract C is A, B {
   function bar() public pure virtual override(A, B) returns
    (string memory) {
   return "Bar Contract C";
   }
   function foo() public pure virtual override(A, B) returns (string memory) {
   return "Foo Contract C";
   }
}
contract D is A, B, C {
   function bar() public pure override(A, B, C) returns (string memory) {
   return "Bar Contract D";
   }
   function foo() public pure override(A, B, C) returns (string memory) {
   return "Foo Contract D";
   }
}
Enter fullscreen mode Exit fullscreen mode

O diagrama abaixo mostra as relações.

Image description

Figura 2. A relação de A, B, C, D.

A é o mais básico, enquanto que D é o mais derivado. D vai herdar todos os atributos de A, B e C. Para que um filho faça uma substituição de outro filho, a palavra chave virtual override deve ser aplicada à função. Isso vai permitir que D herde a função dos seus pais, mas defina seu próprio valor. Como se pode ver pelo exemplo, D herda a função bar(). Ao invés de herdar o “Bar Contract D”.

Em um contrato em que os filhos herdam de múltiplos pais, os contratos devem ser especificados na ordem do mais básico para o mais derivado. Esta é a razão pela qual temos no Contrato D:

    contract D is A, B, C {}
Enter fullscreen mode Exit fullscreen mode

A função também deve declarar os pais que ela quer substituir na declaração:

    function bar() public pure override(A, B, C)
Enter fullscreen mode Exit fullscreen mode

D vai herdar de A a função baz(), mesmo que ela não tenha sido declarada. Isso ocorre por padrão, já que você herda todos os atributos de um pai.

As relações também podem ser transitive (transitivas) por natureza. Considere o próximo diagrama.

Image description

Figura 3. Uma relação transitiva permite a D herdar ainda de A, mesmo que eles não estejam diretamente conectados.

Tiramos a ligação direta de A com D. Entretanto, como D está conectado com B e C, ele ainda tem relação com A. Isso significa que a relação pode ser transitiva.

O contrato D pode ser escrito assim:

contract D is B, C {
   function bar() public pure override(B, C) returns (string memory) 
{
   return "Bar Contract D";
   }
   function foo() public pure override(B, C) returns (string memory) 
{
   return "Foo Contract D";
   }
}
Enter fullscreen mode Exit fullscreen mode

Não há necessidade de declarar A. Os atributos de A estarão presentes em D porque ele herdou de B e C os atributos que esses herdaram de A.

Agora, a herança pode ser transitiva entre filhos? Por exemplo, considere o próximo diagrama.

Image description

Figura 4. Relações entre filhos são não-transitivas se não tiver nenhum caminho (path). Siga as setas para determinar a transitividade.

Se retirarmos a relação entre C e D, D ainda vai herdar C pelo caminho A ou B? Não. Se você olhar a direção das setas, não existe um caminho de C para D no diagrama. Então, D não pode herdar de C.

Resumo

Das metodologias discutidas, os desenvolvedores podem aplicar técnicas mais avançadas como, ordem da inicialização de variáveis e funções de chamada. Um pai compartilha suas funções para todos os filhos e as relações podem ser transitivas. Nos smart contracts, essa é uma forma de permitir que desenvolvedores usem funções de outros contratos que já estejam disponíveis. É uma forma de economizar tempo e uma prática recomendada para usar código que já foi experimentado, testado e que esteja certo.

Esse artigo foi escrito por Vincent Tabora, publicado em OxCODE e traduzido por Fátima Lima. Seu original pode ser lido aqui.

Latest comments (1)

Collapse
 
ianvex profile image
Ian Vexani

muito bom!!