# Orientação a Objetos Falamos sobre funções e estruturas nas duas últimas seções, mas você já considerou usar funções como campos de uma estrutura? Nesta seção, vou apresentá-lo a outra forma de função que possui um receptor, que é chamado `method` (método). ## method Suponha que você definiu uma estrutura "rectangle" (retângulo) e quer calcular a sua área. Geralmente, usamos o seguinte código para atingir esse objetivo. package main import "fmt" type Rectangle struct { width, height float64 } func area(r Rectangle) float64 { return r.width*r.height } func main() { r1 := Rectangle{12, 2} r2 := Rectangle{9, 4} fmt.Println("Area of r1 is: ", area(r1)) fmt.Println("Area of r2 is: ", area(r2)) } O exemplo acima pode calcular a área de um retângulo. Usamos a função chamada `area`, mas ela não é um método da estrutura rectangle (como métodos de classe em linguagens orientadas a objetos clássicas). A função e a estrutura são duas coisas independentes, como você pode notar. Isto não é um problema. Entretanto, se você também tem que calcular a área de um circulo, quadrado, pentágono ou qualquer outro tipo de forma, você vai precisar adicionar funções adicionais com nomes muito semelhantes. ![](images/2.5.rect_func_without_receiver.png?raw=true) Figure 2.8 Relacionamento entre funções e estruturas Evidentemente, isso não é legal. Além disso, a área deve realmente ser a propriedade de um círculo ou retângulo. Por estas razões, temos o conceito de `method`. `method` é afiliado ao tipo. Ele possui a mesma sintaxe que as funções, exceto por um parâmetro adicional após a palavra-chave `func` chamada `receiver` (receptor), que é o corpo principal desse método. Usando o mesmo exemplo, `Rectangle.area()` pertence diretamente ao rectangle, em vez de ser uma função periférica. Mais especificamente, `length`, `width` e `area()` pertencem ao rectangle. Como Rob Pike disse. "Um método é um função com um primeiro argumento implícito, chamado receptor." Sintaxe do método. func (r ReceiverType) funcName(parameters) (results) Vamos mudar nosso exemplo usando `method` em vez disso. package main import ( "fmt" "math" ) type Rectangle struct { width, height float64 } type Circle struct { radius float64 } func (r Rectangle) area() float64 { return r.width*r.height } func (c Circle) area() float64 { return c.radius * c.radius * math.Pi } func main() { r1 := Rectangle{12, 2} r2 := Rectangle{9, 4} c1 := Circle{10} c2 := Circle{25} fmt.Println("Area of r1 is: ", r1.area()) fmt.Println("Area of r2 is: ", r2.area()) fmt.Println("Area of c1 is: ", c1.area()) fmt.Println("Area of c2 is: ", c2.area()) } Notas para o uso de métodos. - Se os nomes dos métodos são os mesmos, mas eles não compartilham os mesmos receptores, eles não são os mesmos método. - Métodos são capazes de acessar campos dentro de receptores. - Use `.` para chamar um método na estrutura, da mesma forma que os campos são chamados. ![](images/2.5.shapes_func_with_receiver_cp.png?raw=true) Figure 2.9 Métodos são diferentes em diferentes estruturas No exemplo acima, os métodos area() pertencem ao Rectangle e ao Circle respectivamente, então os receptores são Rectangle e Circle. Um ponto que vale notar é que o método com a linha pontilhada significa que o receptor é passado por valor, não por referência. A diferença entre eles é que um método pode mudar os valores do seu receptor quando o receptor é passado por referência, e outro recebe uma cópia do receptor quando o receptor é passado por valor. O receptor só pode ser uma estrutura? Claro que não. Qualquer tipo pode ser o receptor de um método. Você pode estar confuso sobre tipos customizados. Estrutura é um tipo especial de tipo customizado -há mais tipos customizado. Use o seguinte formato para definir um tipo customizado. type typeName typeLiteral Exemplos de tipos customizados: type ages int type money float32 type months map[string]int m := months { "January":31, "February":28, ... "December":31, } Espero que você saiba usar tipos customizados agora. Semelhante ao `typedef` em C, usamos `ages` para substituir `int` no exemplo acima. Vamos voltar a falar sobre `method`. Você pode usar quantos métodos quiser em tipos customizados. package main import "fmt" const( WHITE = iota BLACK BLUE RED YELLOW ) type Color byte type Box struct { width, height, depth float64 color Color } type BoxList []Box //uma slice de caixas func (b Box) Volume() float64 { return b.width * b.height * b.depth } func (b *Box) SetColor(c Color) { b.color = c } func (bl BoxList) BiggestsColor() Color { v := 0.00 k := Color(WHITE) for _, b := range bl { if b.Volume() > v { v = b.Volume() k = b.color } } return k } func (bl BoxList) PaintItBlack() { for i, _ := range bl { bl[i].SetColor(BLACK) } } func (c Color) String() string { strings := []string {"WHITE", "BLACK", "BLUE", "RED", "YELLOW"} return strings[c] } func main() { boxes := BoxList { Box{4, 4, 4, RED}, Box{10, 10, 1, YELLOW}, Box{1, 1, 20, BLACK}, Box{10, 10, 1, BLUE}, Box{10, 30, 1, WHITE}, Box{20, 20, 20, YELLOW}, } fmt.Printf("We have %d boxes in our set\n", len(boxes)) fmt.Println("The volume of the first one is", boxes[0].Volume(), "cm³") fmt.Println("The color of the last one is",boxes[len(boxes)-1].color.String()) fmt.Println("The biggest one is", boxes.BiggestsColor().String()) fmt.Println("Let's paint them all black") boxes.PaintItBlack() fmt.Println("The color of the second one is", boxes[1].color.String()) fmt.Println("Obviously, now, the biggest one is", boxes.BiggestsColor().String()) } Definimos algumas constantes e tipos customizados. - Usamos `Color` como apelido para `byte`. - Definimos uma estrutura `Box` que tem os campos height (altura), width (largura), length (comprimento) e color (cor). - Definimos uma estrutura `BoxList` que tem `Box` como seu campo. Então, definimos alguns métodos para os nossos tipos customizados. - Volume() usa Box como seu receptor e retorna o volume de Box. - SetColor(c Color) muda a cor de Box. - BiggestsColor() retorna a cor que possui o maior volume. - PaintItBlack() define as cores das Box em BoxList para preto. - String() usa Color como seu receptor e retorna a palavra formatada do nome da cor. É muito mais claro quando usamos palavras para descrever nosso requisitos? Frequentemente escrevemos nossos requisitos antes de começar a programar. ### Usando ponteiro como receptor Vamos dar uma olhada no método `SetColor`. Seu receptor é um ponteiro de Box. Sim, você pode usar `*Box` como um receptor. Por que usamos um ponteiro aqui? Porque queremos mudar as cores de Box neste método. Assim, se não usarmos um ponteiro, ele só mudará o valor dentro de um cópia de Box. Se vemos que um receptor é o primeiro argumento de um método, não é difícil entender como ele funciona. Você deve estar perguntando por que não estamos usando `(*b).Color=c` em vez de `b.Color=c` no método SetColor(). Qualquer um está OK aqui porque GO sabe como interpretar a atribuição. Você acha que Go é mais fascinante agora? Você também deve estar se perguntando se devemos usar `(&bl[i]).SetColor(BLACK)` em `PaintItBlack` porque passamos um ponteiro para `SetColor`. Novamente, qualquer um está OK porque Go sabe como interpretá-lo! ### Herança do Método Aprendemos sobre herança de campos na última seção. Similarmente, também temos herança de método em Go. Se um campo anônimo tiver métodos, então a estrutura que contém o campo terá todos os métodos dele também. package main import "fmt" type Human struct { name string age int phone string } type Student struct { Human // campo anônimo school string } type Employee struct { Human company string } // define um método em Human func (h *Human) SayHi() { fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone) } func main() { mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"} sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"} mark.SayHi() sam.SayHi() } ### Sobrecarga de Método Se quisermos que Employee tenha seu próprio método `SayHi`, podemos definir um método com o mesmo nome em Employee, e ele irá sobrescrever `SayHi` em Human quando nós o chamarmos. package main import "fmt" type Human struct { name string age int phone string } type Student struct { Human school string } type Employee struct { Human company string } func (h *Human) SayHi() { fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone) } func (e *Employee) SayHi() { fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name, e.company, e.phone) //Sim, você pode dividir em 2 linhas aqui. } func main() { mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"} sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"} mark.SayHi() sam.SayHi() } Você é capaz de escrever um programa Orientado a Objetos agora, e os métodos usam a regra de capital letter (letra maiúscula) para decidir se é público ou privado também. ## Links - [Sumário](preface.md) - Seção Anterior: [Estrutura](02.4.md) - Próxima Seção: [Interface](02.6.md)