Expectativas
Nessa aula vamos aprender como declarar, e importar um pacote dentro do seu projeto, alguns padrões de projetos como o Wrapper, e um padrão de instanciação do Go que se assemelha muito ao que usamos na Programação Orientada a Objeto.
Ponto de partida desse post:
On GitHub#/tree/lesson-02
go-mentorship
Recapitulando
Na aula anterior, nós criamos um código que recebia do usuário uma string, e criava um QR Code, porém nós deixamos nossa main()
muito poluída, na nossa aula de hoje nós vamos criar refatorar nosso código seguindo a filosofia de manter o código mais limpo, e seguindo princípio de responsabilidade única.
Vamos começar criando nossas pastas:
mkdir services
mkdir services/generators
touch services/generators/qrcode.go
mkdir services/io
touch services/io/cli.go
Lidando com IO
Vamos trabalhar no arquivo services/io/cli.go
. A responsabilidade desse serviço será cuidar do IO
da aplicação, apesar de óbvio, isso pode significar que ele será um logger em arquivo, um logger em CLI, ou qualquer outro tipo de log. Nessa aplicação por ser simples, ele irá ser um log em CLI, vamos começar a escrever nosso código.
IO é um termo que referencia entrada e saída de dados, ou seja, dados que são lidos ou escritos. O I vem de Input, e O vem de Output.
Vamos começar o arquivo declarando o pacote, e adicionando os imports.
package services
import "fmt"
Agora, talvez você não conheça esse tipo de declaração, mas se você está habituado com Programação Orientada a Objeto, você irá pegar rápido, mas basicamente nós iremos definir uma Struct, e dizer algumas funções que ela terá.
type CLI struct{}
func NewCLI() *CLI {
return &CLI{}
}
Começamos declarando, o Read
, ele seguirá a mesma ideia que existia antes. Utilizaremos o fmt
para capturar o valor do usuário, e retornar o valor. E logo em seguida declaramos o Write
, que irá escrever o valor no console.
func (io *CLI) Read() string {
var input string
fmt.Scanln(&input)
return input
}
func (io *CLI) Write(output string) {
fmt.Println(output)
}
Criando um QR Code
Vamos trabalhar no arquivo services/generators/qrcode.go
.
Vamos seguir mais ou menos a mesma ideia do IO.
package services
import (
"fmt"
"image/png"
"io"
"github.com/boombuler/barcode"
"github.com/boombuler/barcode/qr"
)
type QRCode struct {
barcode *barcode.Barcode
width int
height int
}
func NewQRCode() *QRCode {
return &QRCode{
width: 200,
height: 200,
}
}
Agora vamos declarar dois métodos, SetBarcode
, e ToPNG
.
A ideia, do SetBarcode
, é utilizando nossa struct armazenar o Barcode, e facilitar a escrita desse barcode eventualmente num PNG, ou em qualquer outro formato.
func (generator *QRCode) SetBarcode(input string) *QRCode {
qrCode, err := qr.Encode(input, qr.M, qr.Auto)
if err != nil {
return generator
}
qrCode, err = barcode.Scale(qrCode, generator.width, generator.height)
if err != nil {
return generator
}
generator.barcode = &qrCode
return generator
}
Já o ToPNG
, ele irá escrever o PNG dado um IO Writer.
func (generator *QRCode) ToPNG(w io.Writer) error {
if generator.barcode == nil {
return fmt.Errorf("barcode is nil")
}
err := png.Encode(w, *generator.barcode)
return err
}
Você entende por quê é melhor envolver o Barcode na nossa struct? Isso é um Wrapper, é um padrão de software, que utilizamos para envolver valores que vêm de libs externas, pois caso precisemos mudar a lib, nós só mudamos o Wrapper, e não o código que consome ele. Não é necessário sempre, porém no nosso caso, eventualmente nós vamos mudar a assinatura do
ToPNG
, então vai ser importante fazer isso agora.
Vamos atualizar nosso main.go
para utilizar nosso código:
Perceba que estamos utilizando um padrão de importação que é nomeado, nós estamos atribuindo um nome ao pacote que importamos, já que nós não estamos importando diretamente de
services
, e simservices/io
, faz sentido nomearmos a importação. Consulte o conteúdo adicional para mais detalhes.
package main
import (
"os"
generator "github.com/joaomarcuslf/qr-generator/services/generators"
io "github.com/joaomarcuslf/qr-generator/services/io"
)
func main() {
file, err := os.Create("qrcode.png")
defer file.Close()
if err != nil {
panic(err)
}
cli := io.NewCLI()
qr := generator.NewQRCode()
cli.Write("Enter your string: ")
qr.SetBarcode(cli.Read()).ToPNG(file)
}
Concluindo
Nessa aula nós só refatoramos o código, na próxima nós vamos adicionar um servidor HTTP, e vamos mudar os inputs, e outputs para algo mais interessante.