Orientação o objetos em Go
Na linguagem Go é possível programar utilizando o paradigma de Orientação a objetos.
Todavia, existem peculiaridades que tornam Go uma linguagem um pouco diferente de outras orientadas a objeto como o C Sharp e o Java.
Algumas diferenças
Para começar, em Go não existem classes como no Java e C Sharp. Go também não faz uso de protótipos (prototypes) como no Javascript (antes da implementação classes no mesmo).
Todavia, é possível criar objetos. Também é possível atribuir métodos aos objetos, de modo que possamos interagir com os mesmos.
Criação e uso de objetos em Go
Uma forma de criar objetos em Go é através do uso de Structs, mas isso será abordado em um próximo artigo.
Antes disso, vamos entender melhor como funciona a orientação a objetos em GO de forma mais conceitual, criando variáveis que são objetos mais simples.
Um exemplo de POO em Go
Para facilitar o entendimento, utilizaremos como exemplo um programa de conversão de temperatura.
Nosso conversor de temperatura irá converter a temperatura de 300 graus na escala kelvin para as escalas celsius e fahrenheit.
Para ilustrar de forma mais clara os conceitos que serão apresentados, nosso conversor de exemplo não fará a entrada do valor via console.
Isso adicionaria mais complexidade, pois teríamos que tratar possíveis erros na entrada de dados pelo usuário.
Nosso objetivo aqui é mostrar como funciona orientação a objetos em GO.
Então vamos lá!
Conversor de temperatura - Parte 1
Definindo tipos de objeto
Em Go, é possível que o programador defina seus próprios tipos de objeto.
Isso é interessante pois melhora a legibilidade do programa e evita que erros bobos aconteçam, ao fazer com que um objeto somente aceite dados de um determinado tipo.
Em Go, o tipo de objeto criado pelo programador deve herdar as propriedades de algum tipo primitivo, como int (inteiros), float64 (valor de ponto flutuante de 64 bits) string (cadeia literal de caracteres).
Os tipos primitivos nada mais são do que formas “padrão” de se armazenar dados na memória de um computador para utilização.
Ao invés de utilizamos tipos primitivos, vamos criar alguns tipos de objeto para nosso conversor de temperatura:
// PARTE 1
// tipos de objetos do tipo float64
type fahrenheit float64
type celsius float64
type kelvin float64
Na parte 1 de nosso programa, criamos 3 tipos de objetos: celsius, farhenheit e kelvin. Esses tipos de objeto, só podem armazenar valores do tipo float64.
Discussão:
Aqui há uma questão interessante a ser observada. Vejamos o trecho de código abaixo. Vamos supor que tenhamos:
type kelvin float64
var x kelvin = 10.0
var y float64 = 10.0
Se comparamos, por exemplo: if x == y o resultado será verdadeiro ou falso?
A resposta é: falso!
Isso se dá porque x é uma variável do tipo kelvin e y é uma variável do tipo float64. Tratam-se de tipos diferentes embora ambas variáveis possuam características do tipo primitivo float64.
Fazendo uma analogia, banana é um tipo de fruta diferente do tipo de fruta maçã, embora ambas sejam tipos de frutas.
Conversor de temperatura - Parte 2
Vamos voltar ao nosso programa exemplo. Já criamos nossos “tipos” de temperaturas. Agora vamos criar métodos para interagir com objetos desses tipos.
A idéia aqui é a seguinte: quando criarmos um objeto do tipo kelvin, teremos um método para interagir com ele. O mesmo vale para os outros tipos de objeto. A interação desejada será fazer o cálculo (conversão) de um “objeto” temperatura.
Vamos lá:
// PARTE 2
// Criação de métodos para os tipos de objeto criados
// Cria método celsius para interagir com objetos do tipo fahrenheit
func (f fahrenheit) celsius() celsius {
return celsius((f - 32) * 5 / 9)
}
// Cria método celsius para interagir com objetos do tipo kelvin
func (k kelvin) celsius() celsius {
return celsius(k - 273.15)
}
// Cria método fahrenheit para interagir com objetos do tipo celsius
func (c celsius) fahrenheit() farenheit {
return fahrenheit(c*9/5) + 32
}
// Cria método celsius para interagir com objetos do tipo fahrenheit
func (f fahrenheit) celsius() celsius {
return celsius((f - 32) * 5 / 9)
}
// Cria método celsius para interagir com objetos do tipo kelvin
func (k kelvin) celsius() celsius {
return celsius(k - 273.15)
}
// Cria método fahrenheit para interagir com objetos do tipo celsius
func (c celsius) fahrenheit() farenheit {
return fahrenheit(c*9/5) + 32}
Anatomia de um método:
Em Go, um método é parecido com uma função. Essa função pode ter um ou vários parâmetros.
Mas o importante aqui é que o método aceita somente um determinado TIPO de receptor como parâmetro.
Se for passada à função algum argumento que não seja do TIPO de receptor determinado, ocorrerá erro de compilação.
Para ilustrar nossa discussão, vejamos o trecho de um código abaixo, onde criamos 2 tipos de objeto (celsius e fahrenheit) e a seguir, criamos um método fahrenheit que somente pode receber objetos do tipo celsius (receiver).
Esse método retornará como resultado (no caso a conversão de temperatura), um valor do tipo fahrenheit.
type celsius float64
type fahrenheit float64
func (c celsius) fahrenheit() farenheit {
return fahrenheit(c*9/5) + 32}
Resumindo, criamos um método fahrenheit para objetos do tipo celsius.
Discussão:
Aqui temos uma das principais diferenças entre a orientação a objetos em Go e outras linguagens:
Em algumas linguagens nós temos classes (que são modelos de objetos) e dentro das classes nós temos os métodos.
Quando criamos um objeto de uma determinada classe, aquele objeto já empacota o método para interagir com ele.
Em Go, criamos um tipo de objeto e associamos a ele um método, que vai interagir com aquele tipo. Quando o objeto do tipo determinado é criado, há um método para interagir com ele.
A grande diferença é: em outras linguagens, você declara o método dentro da classe que está criando.
Em Go, o método é declarado fora da classe e se assemelha mais a uma declaração de função.
Conversor de temperatura - Parte 3
Vamos à próxima parte de nosso conversor de temperatura.
O que fizemos até agora:
Já declaramos tipos de objetos que queremos criar quando nosso programar for executado.
Também criamos métodos que irão interagir com os objetos baseados nos tipos de objeto que criamos.
Agora vamos criar nossos objetos propriamente ditos e vamos interagir com eles através dos seus métodos durante a execução do programa:
// PARTE 3
// ponto de ínicio para execução do programa.
func main() {
// Cria objeto (Variável) do tipo kelvin e atribui valor 300
var k kelvin = 300.0
// cria objetos (variáveis) e deixa Go inferir de que tipo
// no momento da atribu
c := k.celsius()
f := fahrenheit(c)
// imprime resultado com 2 casas decimais
fmt.Printf("%.2f kelvin = %.2f celsius = %.2f fahrenheit", k, c, f)
}
Primeiro criamos um objeto de nome k (no caso nosso objeto é uma variável) do tipo kelvin. Esse objeto vai armazenar o valor 300.
Depois criamos um objeto de nome c e atribuímos a ele o mesmo valor que guardamos em k, porém chamamos o método celsius, que nós criamos para objetos do tipo kelvin (lembrando que k é um objeto do tipo kelvin).
Logo, Go irá inferir que c é do tipo celsius, pois já convertemos k para celsius, usando do método celsius.
O mesmo raciocínio vale para o objeto f.
Por fim, utilizamos a função printf do pacote fmt e realizamos a impressão do resultado, como argumentos os nossos 3 objetos criados: k, c, f.
Para melhorar a legibilidade, ainda estamos mostrando os resultados com apenas 2 casas decimais.
Se não fizéssemos isso, o resultado apresentado teria tantas casas decimais quanto são permitidas em váriáveis do tipo float64 para os valores armazenados.
Vamos ao programa completo:
// início do programa definindo como pacote que será executado
package main
// importação do pacote fmt (format), para realizarmos impressão
import "fmt"
// Definição de 3 tipos de objetos do tipo float64
type fahrenheit float64
type celsius float64
type kelvin float64
// Definição de métodos p/ interagir com objetos dos tipos definidos
// método celsius para objetos do tipo fahrenheit
func (f fahrenheit) celsius() celsius {
return celsius((f - 32) * 5 / 9)
}
// metodo celsius para objetos do tipo kelvin
func (k kelvin) celsius() celsius {
return celsius(k - 273.15)
}
// metodo fahrenheit para objetos do tipo celsius
func (c celsius) fahrenheit() farenheit {
return fahrenheit(c*9/5) + 32
}
// inicio do programa / ponto de entrada
func main() {
// cria objeto (Variável) do tipo kelvin e atribui valor 300
var k kelvin = 300.0
// cria variáveis e deixa Go inferir o tipo de objeto durante a atribuição
c := k.celsius()
f := fahrenheit(c)
// imprime resultado com 2 casas decimais
fmt.Printf("%.2f kelvin = %.2f celsius = %.2f fahrenheit", k, c, f)
}
O mesmo programa, porém sem comentários:
package main
import "fmt"
type fahrenheit float64
type celsius float64
type kelvin float64
func (f fahrenheit) celsius() celsius {
return celsius((f - 32) * 5 / 9)
}
func (k kelvin) celsius() celsius {
return celsius(k - 273.15)
}
func (c celsius) fahrenheit() farenheit {
return fahrenheit(c*9/5) + 32
}
func main() {
var k kelvin = 300.0
c := k.celsius()
f := fahrenheit(c)
fmt.Printf("%.2f kelvin = %.2f celsius = %.2f fahrenheit", k, c, f)
}
Finalizando
Nesse artigo exemplificamos o básico sobre orientação a objetos em Go, mostrando como definir tipos de objetos e como criar métodos para interagir com objetos desses tipos.
Os objetos criados aqui são váriaveis, mas poderiam ser qualquer outro tipo de estrutura de armazenamento de dados disponível em Go.
Em um outro momento, apresentaremos a criação de objetos mais complexos como structs, que se assemelham mais ao uso prático de classes de outras linguagens, porém com maior flexibilidade.
Espero que tenham gostado e até a próxima.
