-
Notifications
You must be signed in to change notification settings - Fork 47
Orientação a objetos
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 comotexto. O tipo é o nome da classe; -
paraTexto(): devolve uma representação do objeto comotexto, 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()(oueInstanciaDe()): verifica a herança de um projeto, comparando com alguma outra classe; -
respondeA(): dada uma propriedade ou método passado como parâmetro (texto), devolveverdadeirose a propriedade ou método existe, efalsoem 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")) // falsoTodos 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.
Classe é a descrição de um ou mais objetos. Classes em Delégua podem ser criadas e estendidas.
Uma classe é criada com a palavra reservada classe:
classe Teste {}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 é 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 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á"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();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 }
}
}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>"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 4Mé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) // ErroO bloco protegido {} é demonstrado a seguir, em 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();
}
}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()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) // ErroClasses abstratas são classes que não podem existir como instâncias:
classe abstrata Poligono {}
var p = Poligono() // ErroNormalmente 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`.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 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 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- Estruturas de dados elementares
- Entrada e saída
- Operadores
- Condicionais
- Laços de repetição
- Funções
- Funções nativas
- Escopos
- Exceções
- Tipos e Inferência de variáveis
- Orientação a objetos
- Decoradores
- Tradução para outras linguagens
- Compilação para código de máquina
- Dialetos suportados em Delégua
- Implementando Delégua na sua aplicação
- Estruturas de dados elementares
- Entrada e saída
- Operadores
- Condicionais
- Laços de repetição
- Funções
- Funções nativas
- Escopos
- Exceções
- Tipos e Inferência de variáveis
- Asserções e testes unitários
- Orientação a objetos
- Decoradores
- Tradução para outras linguagens
- Compilação para código de máquina
- Dialetos suportados em Delégua
- Implementando Delégua na sua aplicação