Introdução
Uma das coisas mais comuns em códigos escritos na linguagem Go é o uso de tipos customizados utilizando structs
. Geralmente usamos estes tipos para declarar entidades, transportar valores de forma estruturada e etc. Por exemplo, O código acima é muito comum em muitas aplicações:
type Person struct { Name string Document string Email string }
Outros tipos customizados
Mas assim como structs
, podemos utilizar outros tipos primitivos da linguagem para criar tipos customizados, abrindo assim um leque de oportunidades. O processo de criação é idêntico ao mostrado anteriormente, mas usando outro tipo primitivo como bases. Aqui vão alguns exemplos:
// Tipo customizado baseado em string type MyCustomString string // Baseado em int type MyCustomInt int // Baseado em float type MyCustomFloat64 float64 // Baseado em boolean type MyCustomBool bool // Baseado em slice type MyCustomSlice []string // Baseado em... já deu para entender, né!?
Mas como eu uso?
Para usar esses tipos novos é tão simples quanto você está pensa. Eles operam da mesma forma que seus tipos base, assim como a struct
. Dá uma olhada:
func main() { // Declarando variáveis com tipos personalizados var str MyCustomString = "example" var num MyCustomInt = 42 var flt MyCustomFloat64 = 3.14 var boolean MyCustomBool = true var slice = MyCustomSlice{"example1", "example2"} // Operando com as variáveis str += " string" num += 10 flt *= 2.0 boolean = !boolean slice = append(slice, "example3") // Exibindo os valores e tipos das variáveis log.Printf("Value of str: %s, type: %T", str, str) //Value of str: example string, type: main.MyCustomString log.Printf("Value of num: %d, type: %T", num, num) //Value of num: 52, type: main.MyCustomInt log.Printf("Value of flt: %.2f, type: %T", flt, flt) //Value of flt: 6.28, type: main.MyCustomFloat64 log.Printf("Value of boolean: %t, type: %T", boolean, boolean) //Value of boolean: false, type: main.MyCustomBool log.Printf("Value of boolean: %s, type: %T", slice, slice) //Value of boolean: [example1 example2 example3], type: main.MyCustomSlice }
Legal, né!?
Adicionando métodos
Uma possibilidade legal que esta abordagem nos traz é a capacidade de as variáveis criadas a partir destes tipo chamarem métodos customizados. Isso pode ter várias aplicações interessantes. Olha só:
type MyCustomInt int func (my MyCustomInt) Positive() bool { return my > 0 } func main() { var num MyCustomInt = 42 log.Println("num Positive:", num.Positive()) // num Positive: true }
Olhando aquele nosso exemplo inicial da struct Person
, podemos aplicar esses princípios para os campos Email
e Document
ao criar um novo tipo para cada um:
type Document string func (d Document) Validate() bool { return len(d) > 0 } type Email string func (e Email) Validate() bool { if len(e) == 0 { return false } return strings.Contains(string(e), "@") && strings.Contains(string(e), ".") }
Assim cada tipo sabe como fazer sua própria validação. A Person
fica assim então:
type Person struct { Name string Document Document Email Email }
Olha como o uso fica legal:
person := Person{ Name: "John Doe", Document: "123456789", Email: "johndoe@test.com", } log.Printf("Person: %+v", person) // Person: {Name:John Doe Document:123456789 Email:johndoe@test.com} log.Printf("Person Document valid: %t", person.Document.Validate()) //Person Document valid: true log.Printf("Person Email valid: %t", person.Email.Validate()) //Person Email valid: true
Dá pra usar interfaces
Para melhorar ainda mais nosso exemplo, podemos fazer a pergunta: E se eu quiser usar o mesmo campo Document
para vários tipos de documento (digamos que RG e CPF)?
Podemos então mudar o tipo de Document
para interface
, e criar os tipos dos outros documentos que implementam esta nova interface. Melhor mostrando, né?
type Document interface { Validate() bool } type DocumentRG string type DocumentCPF string func (d DocumentRG) Validate() bool { log.Println("RG validation logic") return len(d) > 0 } func (d DocumentCPF) Validate() bool { log.Println("CPF validation logic") return len(d) > 0 }
Na Person
nada muda, tá?!
Mas se liga aqui como fica o uso:
person := Person{ Name: "John Doe", Document: DocumentCPF("123456789"), Email: "johndoe@test.com", } person2 := Person{ Name: "Maike Doe", Document: DocumentRG("0987654321"), Email: "maikedoe@test.com", } log.Printf("Person1: %+v", person) // Person1: {Name:John Doe Document:123456789 Email:johndoe@test.com} log.Printf("Person1 Document valid: %t", person.Document.Validate()) // CPF validation logic // Person1 Document valid: true log.Printf("Person2: %+v", person2) // Person2: {Name:Maike Doe Document:0987654321 Email:maikedoe@test.com} log.Printf("Person2 Document valid: %t", person2.Document.Validate()) //RG validation logic //Person2 Document valid: true
Dessa forma, você pode ter vários tipos de documentos, e quem vai implementar é quem decide qual vai usar.
Utilizando enums
Para fecharmos, podemos fazer a pergunta: E se eu quiser ter um campo que indique o tipo de documento?
A gente pode usar Enums para isso. Espia:
type DocumentType uint const ( DocumentTypeRG DocumentType = iota DocumentTypeCPF DocumentTypeCNH DocumentTypePassaporte ) func (d DocumentType) String() string { switch d { case DocumentTypeRG: return "RG" case DocumentTypeCPF: return "CPF" case DocumentTypeCNH: return "CNH" case DocumentTypePassaporte: return "Passaporte" default: return "Unknown" } }
Adicionamos então o campo DocumentType
em Person
:
type Person struct { Name string Document Document DocumentType DocumentType Email Email }
E para usar também é bem simples:
person := Person{ Name: "John Doe", Document: DocumentCPF("123456789"), DocumentType: DocumentTypeCPF, Email: "johndoe@test.com", } person2 := Person{ Name: "Maike Doe", Document: DocumentRG("0987654321"), DocumentType: DocumentTypeRG, Email: "maikedoe@test.com", } log.Printf("Person1: %+v", person) // Person1: {Name:John Doe Document:123456789 DocumentType:CPF Email:johndoe@test.com} log.Println("Document Type:", person.DocumentType) // Document Type: CPF log.Printf("Person2: %+v", person2) // Person2: {Name:Maike Doe Document:0987654321 DocumentType:RG Email:maikedoe@test.com} log.Println("Document Type:", person2.DocumentType) // Document Type: RG
E voilà!
Vimos aqui então que podemos criar tipos customizados baseados em tipos primitivos, chamar métodos através deles, implementar interfaces e até utilizar Enums.
E aí, o que achou dessas dicas? Deixe aí nos comentários.
Obs. Cover image criada com IA.
Top comments (0)