Pular para o conteúdo

Fundamentos

Objeto, classe, instância, estado, identidade — e o que cada palavra significa em DataForge.

Antes da sintaxe, o vocabulário. Cada termo abaixo tem um nome próprio em DataForge, e saber qual é qual poupa a maior parte da confusão.

O que é orientação a objetos#

Um jeito de organizar programa juntando dados e o comportamento que age sobre eles. A alternativa — dados de um lado, funções de outro — funciona bem até o programa crescer; aí ninguém mais sabe quais funções podem mexer em quais dados.

OOP responde isso pondo os dois no mesmo lugar e controlando quem entra.

Classe e instância#

A classe é a forma; a instância é a peça. Em DataForge a classe é o blueprint — a planta — e a peça nasce com spawn:

dataforge
blueprint Conta:
    private saldo: Float := 0.0

    action setup(titular):
        self.titular := titular

    action depositar(valor):
        self.saldo += valor
        yield self.saldo

    get extrato():
        yield $"{self.titular}: {self.saldo}"

// a planta é uma; as peças são duas, e independentes
a := spawn Conta("Ana")
b := spawn Conta("Bia")
a.depositar(100)

assert a.extrato is "Ana: 100.0"
assert b.extrato is "Bia: 0.0"

Estado, comportamento, identidade#

Todo objeto tem três coisas, e elas se confundem com frequência:

O que éEm DataForge
estadoos valores que ele guarda agoraos campos: self.saldo
comportamentoo que ele sabe fazeros métodos: action depositar
identidadeo que faz ele ser ele, e não outro iguala referência — dois spawn dão dois objetos

A identidade é o que separa blueprint de record. Duas contas com o mesmo saldo não são a mesma conta; dois pontos (1,2) são o mesmo ponto:

dataforge
record Ponto:
    x: Integer
    y: Integer

blueprint Caixa:
    action setup(n):
        self.n := n

// record: igualdade estrutural — o conteúdo decide
assert Ponto(1, 2) is Ponto(1, 2)

// blueprint: identidade — cada spawn é um objeto
assert (spawn Caixa(1)) isnt (spawn Caixa(1))

Atributos e métodos#

Campos guardam; métodos agem. Ambos podem ser de instância (cada objeto tem o seu) ou estáticos (um só, da classe):

dataforge
blueprint Contador:
    static total: Integer := 0
    valor: Integer := 0

    action somar():
        self.valor += 1
        yield self.valor

    static action zerar():
        yield 0

a := spawn Contador()
b := spawn Contador()
a.somar()
a.somar()
b.somar()

assert a.valor is 2
assert b.valor is 1
assert Contador.zerar() is 0

Ver métodos estáticos para quando usar cada um.

Construtor#

setup é o construtor: roda uma vez, no spawn, e é onde o objeto nasce válido. Há duas formas:

dataforge
// forma explícita
blueprint Retangulo:
    action setup(largura, altura):
        given largura smaller_eq 0 or altura smaller_eq 0:
            trigger "as medidas precisam ser positivas"
        self.largura := largura
        self.altura := altura

// forma inline — os parâmetros viram campos
blueprint Circulo(raio):
    action area():
        yield 3.14159 * self.raio ** 2

assert (spawn Retangulo(3, 4)) isnt void
assert round((spawn Circulo(1)).area(), 3) is 3.142

`self`#

self é o objeto em que o método está rodando. Escrever `x` em vez de `self.x` lê a variável do escopo externo — é a armadilha mais comum de quem começa:

dataforge
valor := "de fora"

blueprint Errado:
    action setup():
        self.valor := "de dentro"

    action mostrar():
        yield valor          // lê o de FORA

blueprint Certo:
    action setup():
        self.valor := "de dentro"

    action mostrar():
        yield self.valor     // lê o campo

assert (spawn Errado()).mostrar() is "de fora"
assert (spawn Certo()).mostrar() is "de dentro"

Modificadores de acesso#

Três níveis, e eles valem de verdade — não por convenção de nome:

Quem alcança
(padrão)qualquer um
protectedo blueprint que declarou e seus herdeiros
privatesó o blueprint que declarou — nem o herdeiro

Ver campos e visibilidade e encapsulamento.