Skip to content

Orientação a objetos

Leonel Sanches da Silva edited this page May 31, 2026 · 14 revisions

Delégua é uma linguagem puramente orientada a objetos, fortemente influenciada por outras linguagens orientadas a objetos, como C#, Kotlin e Smalltalk. Isso significa que toda variável, constante ou literal é considerada um objeto, herdando métodos de uma classe primordial chamada Objeto.

Objeto possui os seguintes métodos:

  • tipo(): devolve o tipo do objeto como texto. O tipo é o nome da classe;
  • paraTexto(): devolve uma representação do objeto como texto, o que pode ser útil para serialização de documentos. Também é o método chamado quando o objeto é mencionado em uma interpolação de texto;
  • éInstânciaDe() (ou eInstanciaDe()): verifica a herança de um projeto, comparando com alguma outra classe;
  • respondeA(): dada uma propriedade ou método passado como parâmetro (texto), devolve verdadeiro se a propriedade ou método existe, e falso em caso contrário.

Exemplos de uso:

classe Animal { }
lasse Cachorro herda Animal { }
var c = Cachorro()
escreva(c.tipo()) // "Cachorro"
escreva(c.eInstanciaDe(Animal)) // verdadeiro
escreva(c.eInstanciaDe(Objeto)) // verdadeiro

classe Carro { }
classe Moto { }
var ca = Carro()
escreva(ca.paraTexto()) // "<[ Carro métodos=[] propriedades=[] ]>"
escreva(ca.eInstanciaDe(Moto)) // falso

classe Caminhao {
    acelerar() { escreva("vrum") }
}

var cami = Caminhao()
escreva(cami.respondeA("acelerar")) // verdadeiro
escreva(cami.respondeA("frear")) // falso

Todos esses métodos podem ser sobrescritos. Por exemplo:

classe Ponto {
    x: numero
    y: numero

    construtor(x, y) {
        isto.x = x
        isto.y = y
    }

    paraTexto() {
        retorna "(" + isto.x + ", " + isto.y + ")"
    }
}

var p = Ponto(3, 4)
escreva(p.paraTexto())
p.x = 9
escreva("${p}") // (9, 4)

Por extensão, todas as primitivas - ou estruturas de dados básicas - como texto também são objetos. O código abaixo é perfeitamente válido:

escreva("TUDO EM MAIÚSCULO".minusculo()) // Imprimirá "tudo em maiúsculo"

O mecanismo de inferência de Delégua deduz que o literal "TUDO EM MAIÚSCULO" é do tipo texto. Uma vez resolvido o tipo, o método .minusculo(), pertencente à classe texto, pode ser chamado.

Classes

Classe é a descrição de um ou mais objetos. Classes em Delégua podem ser criadas e estendidas.

Criando uma classe

Uma classe é criada com a palavra reservada classe:

classe Teste {}

Instanciando um objeto de uma classe

A instância de uma classe, ou seja, um objeto, é criada a partir da chamada do construtor de uma classe, ou seja, uma função cujo nome é o nome da classe:

classe Teste {}

var teste = Teste();
escreva(teste); // escreve "<[ Teste métodos=[] propriedades=[] ]>"

Construtor

Construtor é um método-padrão que é automaticamente chamado ao instanciar uma classe. Em Delégua, esse método tem a palavra reservada construtor seguida de parênteses com argumentos.

classe Teste {
  construtor() {
    escreva("começou");
  }
}

var teste = Teste(); // escreve "começou"

Como qualquer outra função, um construtor pode ter ou não argumentos.

Métodos

Métodos são funções de uma classe. Diferentemente de funções, são criados sem a palavra-chave de função.

classe Teste {
  testeFuncao() {
    escreva("olá");
  }
}

Esses métodos podem ser acessados por meio da instância com ..

classe Teste {
  testeFuncao() {
    escreva("olá");
  }
}

var teste = Teste();
teste.testeFuncao(); // escreve "olá"

Propriedades

Uma propriedade (ou estado, em Égua) é uma variável dentro de uma classe. Toda propriedade usada por uma classe precisa ser declarada com algum tipo (especialmente a partir da versão 0.29.0), podendo ser um tipo elementar de Delégua ou uma outra classe previamente declarada no seu código.

classe Teste {
  a: número
}

O acesso a propriedades (obter e definir) pode ser feito usando a palavra reservada isto.

classe Teste {
  a: número

  testeFuncao() {
    isto.a = 100
    escreva(isto.a) // escreve "100"
  }
}

var teste = Teste();
teste.testeFuncao();

Customizando o acesso a propriedades

Implicitamente, cada propriedade declarada possui dois métodos internos: obter() e definir(valor: qualquer). Voltando ao exemplo mais simples de declaração de propriedades:

classe Teste {
  a: número
}

É equivalente a:

classe Teste {
  a: número { obter; definir; }
}

Ou seja, automaticamente, uma propriedade poderia ser expressa internamente da seguinte forma:

classe Teste {
  privado {
    _a: número
  }
  
  a: número { 
    obter() { retorna isto._a } 
    definir(valor) { isto._a = valor }
  }
}

Isto

Dentro de cada instância de método, existe a palavra reservada isto, que representa a própria instância da classe, ou o objeto propriamente dito.

classe Teste {
  construtor() {
    escreva(isto);
  }
}

var teste = Teste(); // escreve "<Teste instância>"

Níveis de proteção

Classes em Delégua trabalham com três níveis de proteção para propriedades e métodos: público (ou publico), privado e protegido. público é o padrão.

Se uma propriedade ou método é pública, pode ser acessada por fora da declaração da classe. No exemplo abaixo, x e y são propriedades públicas:

classe Ponto {
    x: numero
    y: numero

    construtor(x, y) {
        isto.x = x
        isto.y = y
    }
}

var p = Ponto(3, 4)
escreva(p.x, p.y) // 3 4

Métodos e propriedades, quando privados, não podem ser acessados por fora da declaração de classe. São declarados dentro de um bloco privado {}:

classe Banco {
    privado {
        saldo: numero
    }

    construtor(s) { isto.saldo = s }
    obterSaldo() { retorna isto.saldo }
}

var b = Banco(500)
escreva(b.obterSaldo()) // 500
escreva(b.saldo) // Erro

O bloco protegido {} é demonstrado a seguir, em herança.

Herança

Em Delégua, é possível criar uma classe descendente a partir de uma classe ancestral, mantendo as suas características iniciais e permitindo a inclusão de novos elementos de classe, como propriedades, construtores e métodos.

classe Animal {}
classe Cachorro herda Animal {}

A primeira linha poderia ser escrita assim:

classe Animal herda Objeto {}

No entanto, tal construção é desnecessária, dado que toda classe automaticamente herda da classe primordial Objeto.

Métodos públicos definidos na classe ancestral podem ser chamados por classes descendentes:

classe Animal {
  corre() {
    escreva("correndo");
  }
}

classe Cachorro herda Animal {}
var thor = Cachorro();
thor.corre();

É possível também fazer a chamada de um método pertencente a uma classe ancestral na classe descendente usando a declaração super.

classe A {
  data(data) {
    escreva(data);
  }
}

classe B herda A {
  construtor(data) {
    super.data(data);
  }
}

var a = B("13/12/1981");

A partir da versão 0.27.0, para chamar o construtor da classe ancestral, use:

classe B herda A {
  construtor() {
    super();
  }
}

Herança múltipla

Delégua suporta herança múltipla:

classe Classe1 {
    meuMetodo() {
        escreva("Em Classe1")
    }
}

classe Classe2 herda Classe1 {
    meuMetodo() {
        escreva("Em Classe2")
        super().meuMetodo()
    }
}

classe Classe3 herda Classe1 {
    meuMetodo() {
        escreva("Em Classe3")
        super().meuMetodo()
    }
}

classe Classe4 herda Classe2, Classe3 {
    meuMetodo() {
        escreva("Em Classe4")
        super().meuMetodo()
    }
}

var classe4 = Classe4()
classe4.meuMetodo()

Nível de acesso protegido

O bloco protegido {} pode ser usado para marcar propriedades e métodos como protegidos. Propriedades e métodos protegidos podem ser acessados por classes derivadas, mas não por fora dessas classes.

classe Animal {
    protegido {
        energia: numero
    }
}

classe Cachorro herda Animal {
    comer() {
        isto.energia += 10
    }

    mostrarEnergia() {
        escreva("Au Au ${isto.energia}")
    }
}

var a = Animal()
var c = Cachorro()
c.comer()
c.mostrarEnergia() // "Au Au 10"
escreva(a.energia) // Erro

Abstrata

Classes abstratas são classes que não podem existir como instâncias:

classe abstrata Poligono {}
var p = Poligono() // Erro

Normalmente são usadas para herança:

classe abstrata Poligono {
    protegido {
        arestas: dupla[]
    }
}

classe Triangulo herda Poligono {
    construtor(a1, a2, a3) {
        isto.arestas = [a1, a2, a3]
    }
}


classe Retangulo herda Poligono {
    construtor(a1, a2, a3, a4) {
        isto.arestas = [a1, a2, a3, a4]
    }
}

var tri = Triangulo((0, 0), (2, 4), (4, 4))
escreva(tri.arestas[2]) // Erro, porque `arestas` é `protegido`.

Composição

Em alguns casos, herança pode não ser a melhor solução para derivar classes. Por isso, Delégua também implementa o mecanismo de composição de classes, em que a classe derivada apenas copia propriedades e métodos de outras classes, evitando problemas comuns, principalmente de herança múltipla:

classe Logavel {
    log() { escreva("log") }
}

classe Servico mescla Logavel {
    executar() { escreva("executar") }
}

var s = Servico()
s.executar()
s.log()

Classes Estrangeiras

Classes estrangeiras são usadas para mapear implementações feitas em outras linguagens de programação dentro de Delégua. O framework Liquido, por exemplo, implementa classes de requisição, resposta, e núcleo do framework em TypeScript, mas a utilização ocorre nos arquivos de rotas, que são em Delégua. Delégua sabe como utilizar essas classes através do mapeamento de suas definições correspondentes.

Classes estrangeiras pedem um decorador @definicao, que explica ao núcleo de Delégua onde procurar pela origem da classe estrangeira. Por exemplo, a classe Requisicao de Líquido:

/**
 * @veja fontes\infraestrutura\requisicao.ts
 */
@definicao
classe estrangeira Requisicao {
    corpo: dicionário;
    parametros: dicionário;
    parametrosPesquisa: dicionário;
    parametrosCaminho: dicionário;
}

@definicao é mencionado sem argumentos adicionais, e propriedades e métodos não requerem decoradores. Neste caso, o núcleo de Delégua espera que a implementação da classe será inserida no interpretador em algum momento, e que sua implementação é em TypeScript/JavaScript. Mais detalhes de como isso é feito podem ser encontrados aqui.

Já no caso de chamar bibliotecas que sejam parte de sistemas operacionais, @decorador precisa ter informações adicionais, como o nome da biblioteca e prefixo para a classe estrangeira, e o símbolo correspondente para cada método:

```delegua
@definicao(biblioteca="m", prefixo="")
classe estrangeira LibM {
    @definicao(simbolo="cos")
    cosseno(x: numero): numero

    @definicao(simbolo="sin")
    seno(x: numero): numero

    @definicao(simbolo="sqrt")
    raizQuadrada(x: numero): numero

    @definicao(simbolo="pow")
    potencia(base: numero, expoente: numero): numero

    @definicao(simbolo="fabs")
    valorAbsoluto(x: numero): numero
}

Mais informações podem ser encontradas no artigo sobre Interface de Funcionalidade Estrangeira desta Wiki.

Interfaces

Interfaces são contratos entre classes e demais componentes de Delégua, e normalmente seguidos por bibliotecas para implementações específicas. Por exemplo, diferentes tecnologias de bancos de dados seguem o mesmo padrão de métodos, mas cada uma implementa esses métodos à sua maneira.

Um exemplo mais simples pode ser algo como:

interface Identificavel {
    id: numero
    identificar(): texto
}

classe Produto implementa Identificavel {
    id: numero
    nome: texto
    construtor(i, n) { isto.id = i \n isto.nome = n }
    identificar() { retorna isto.nome }
}

var p: Identificavel = Produto(1, "Caneta")
escreva(p.identificar()) // "Caneta"
escreva(p.id) // 1

Clone this wiki locally