One place for hosting & domains

      métodos

      Cuatro métodos para realizar búsqueda a través de matrices en JavaScript


      En JavaScript, hay muchas maneras útiles de encontrar elementos en matrices. Siempre puede recurrir al bucle básico for, pero con ES6+ hay muchos métodos para recorrer la matriz y encontrar lo que necesita con facilidad.

      Con tantos métodos diferentes, ¿cuál se debería utilizar y en qué caso? Por ejemplo, al realizar una búsqueda en una matriz, ¿quiere saber si un elemento específico está en la matriz? ¿Necesita el índice del elemento o el elemento en sí?

      Con cada método diferente que cubriremos, es importante comprender que todos estos métodos están integrados en Array.prototype. Eso significa que simplemente necesita encadenarlos a cualquier matriz con notación de punto. Esto también significa que estos métodos no están disponibles en objetos o cualquier otra cosa que no sean matrices (aunque hay superposición con cadenas).

      Observemos los siguientes métodos de matriz:

      includes

      const alligator = ["thick scales", 80, "4 foot tail", "rounded snout"];
      
      alligator.includes("thick scales"); // returns true
      

      El método .includes() devuelve un valor booleano y es ideal para indicarle si un elemento existe o no en una matriz. Da una simple respuesta de verdadero true o falso false. Esta es la sintaxis básica:

      arr.includes(valueToFind, [fromIndex]);
      

      Como ve en nuestro ejemplo, solo obtuvimos un parámetro: valueToFind. Este es el valor que debe coincidir en la matriz. El valor opcional fromIndex es un número que indica qué índice debería comenzar a buscar (por defecto es 0, por lo que realiza una búsqueda en toda la matriz). Por lo tanto, dado que en nuestro ejemplo el elemento ‘thick scales’ está en el índice 0, lo siguiente sería falso: alligator.includes('thick scales', 1); ya que comienza a buscar desde el índice 1 en adelante.

      Ahora, hay algunas cosas importantes a tener en cuenta. Este método .includes() utiliza una comparación estricta. Eso significa que, del ejemplo anterior, lo siguiente debería devolver falso: alligator.includes('80'); eso se debe a que aunque 80 == '80' es verdadero, 80 === '80' es falso: diferentes tipos no pasarán la comparación estricta.

      find

      ¿Cuál es la diferencia entre los métodos .find() e includes()? Si en nuestro ejemplo solo cambiamos el texto “includes” por “find”, obtendremos este error:

      Uncaught TypeError: thick scales is not a function
      

      Eso se debe a que el método find requiere una función para ser aprobado. Esto se debe a que el método find no usa solo el operador de comparación simple como en el caso del método “includes()”. En vez de eso, pasará cada elemento a su función y verá si devuelve verdadero o falso. Aunque esto funcione: alligator.find(() => 'thick scales');, probablemente sea recomendable poner su propio operador de comparación en la función para que devuelva cualquier valor relevante.

      const alligator = ["thick scales", 80, "4 foot tail", "rounded snout"];
      
      alligator.find(el => el.length < 12); // returns '4 foot tail'
      

      Esta sencilla función en nuestro método find busca cada elemento de la matriz, con el alias de ‘el’ que se le asigne y se detiene cuando encuentra el primero que sea verdadero. En nuestro caso, verdadero tiene una propiedad de longitud inferior a 12 (los números no tienen una propiedad de longitud). Por supuesto, puede hacer que esta función sea tan compleja como sea necesario, haciendo que su condición de verdadero se ajuste a sus necesidades.

      Observe también que esto no devolvió true. El método find no devuelve un booleano, pero en vez de eso devuelve el primer elemento que coincida. Si no hay un elemento que coincida, ya que no hay nada que cumpla con los criterios definidos en su función, devolverá undefined. También tenga en cuenta que devuelve solo la primera coincidencia, por lo que si hay más de un elemento en la matriz que cumpla con los criterios, solo obtendrá la primera de ellas. En nuestro ejemplo, si hubiera otra cadena de longitud inferior a 12 después de ‘4 foot tall’, no cambiaría nuestro resultado.

      En nuestro ejemplo, solo usamos la devolución de llamada con un parámetro. También puede añadir parámetros para hacer referencia al índice del elemento actual. Otro parámetro puede ser toda la matriz por sí misma, pero se usa de manera muy poco frecuente. Aquí hay un ejemplo usando el índice:

      alligator.find((el, idx) => typeof el === "string" && idx === 2); // returns '4 foot tall'
      

      Sabemos que en nuestra matriz, hay 3 elementos diferentes que cumplen la primera condición (typeof el === ‘string’). Si esa fuera nuestra única condición, devolverá la primera, ‘thick scales’. Pero la diferencia es que solo uno tiene el índice de 2, es decir ‘4 foot tall’.

      Hablando de índices, un método de matriz similar es .findIndex(). Este método también recibe una función, pero como se podrá imaginar, devuelve el índice del elemento que coincida en vez del elemento en sí.

      indexOf

      const alligator = ["thick scales", 80, "4 foot tail", "rounded snout"];
      
      alligator.indexOf("rounded snout"); // returns 3
      

      Al igual que el método .includes(), .indexOf() utiliza una comparación estricta, no una función como en el caso del método .find(). Pero, a diferencia de includes(), devuelve el índice del elemento, en vez de un booleano. También puede indicar a partir de qué índice en la matriz comenzar a buscar.

      indexOf() es un método muy útil. Es rápido y fácil, puede decirle dónde se encuentra el elemento en la matriz y puede indicar si dicho elemento existe. ¿Cómo le indica si el elemento existe? Básicamente, podemos saber que el elemento existe si devuelve un número positivo y si devuelve -1, indicaría que el elemento no existe.

      alligator.indexOf("soft and fluffy"); // returns -1
      alligator.indexOf(80); // returns 1
      alligator.indexOf(80, 2); // returns -1
      

      Como se puede ver, si bien podríamos usar los métodos find() o findIndex() para darnos la misma información, en este método se escribe mucho menos. No tenemos que escribir una función para realizar la comparación, ya que ya está dentro del método indexOf.

      Al igual que los demás métodos, indexOf() también devuelve el índice del primer elemento que encuentra. JavaScript nos proporciona un método de matriz alternativo .lastIndexOf(). Como se podrá imaginar, este método hace lo mismo que indexOf(), pero a partir del último índice de la matriz y funciona al revés. También puede especificar un segundo parámetro, pero recuerde que los índices no cambian solo por el hecho de usar un método diferente.

      const alligator = ["thick scales", 80, "4 foot tail", "rounded snout", 80];
      
      alligator.indexOf(80); // returns 1
      alligator.lastIndexOf(80); // returns 4
      alligator.indexOf(80, 2); // returns 4
      alligator.lastIndexOf(80, 4); // returns 4
      alligator.lastIndexOf(80, 3); // returns 1
      

      Bonus: filter

      const alligator = ["thick scales", 80, "4 foot tail", "rounded snout", 80];
      
      alligator.filter(el => el === 80); //returns [80, 80]
      

      El método filter() es como el método find(), en el sentido de que requiere una función pasada y una condición para lo que se devolverá. La principal diferencia es que filter() siempre devuelve una matriz, incluso si solo hay un elemento que coincida. Pero devolverá todos los elementos que coincidan, mientras que find() solo devuelve la primera coincidencia.

      Lo importante de filter es que devuelve todos los elementos que coinciden con sus criterios. Podría ser mi punto de vista, pero uno se podría confundir pensando “estos son los elementos que quiero filtrar out”, cuando realmente está indicando los elementos que desea filtrar in.

      Conclusión

      El método que me parece más sencillo de utilizar al realizar una búsqueda es el método find(), pero, como podrá ver, en realidad depende de su caso.

      • ¿Solo necesita saber si existe? Utilice .includes().
      • ¿Necesita obtener el elemento en sí? Utilice .find() o .filter() para buscar varios elementos.
      • ¿Necesita encontrar el índice del elemento? Utilice .indexOf() o findIndex() para realizar una búsqueda más compleja.

      Las matrices en los ejemplos presentados aquí fueron muy sencillas. Es posible que se encuentre con una matriz de objetos. A continuación, presentamos algunos ejemplos básicos para navegar por la jungla de objetos anidados:

      const jungle = [
        { name: "frog", threat: 0 },
        { name: "monkey", threat: 5 },
        { name: "gorilla", threat: 8 },
        { name: "lion", threat: 10 }
      ];
      
      // break the object down in order to use .includes() or .indexOf()
      const names = jungle.map(el => el.name); // returns ['frog', 'monkey', 'gorilla', 'lion']
      console.log(names.includes("gorilla")); // returns true
      console.log(names.indexOf("lion")); // returns 3 - which corresponds correctly assuming no sorting was done
      
      // methods we can do on the array of objects
      console.log(jungle.find(el => el.threat == 5)); // returns object - {name: "monkey", threat: 5}
      console.log(jungle.filter(el => el.threat > 5)); // returns array - [{name: "gorilla", threat: 8}, {name: 'lion', threat: 10}]
      



      Source link

      Quatro métodos de pesquisa através de arrays em JavaScript


      Em JavaScript, existem muitas maneiras úteis de encontrar itens em Arrays. Você pode sempre recorrer ao loop for básico, mas com ES6+ existem muitos métodos para fazer loop sobre um array e encontrar o que você precisa com facilidade.

      Com tantos métodos diferentes, qual deles você usa e em qual caso? Por exemplo, ao pesquisar através de um array, você deseja saber se o elemento está na matriz? Você precisa do índice do elemento ou do elemento em si?

      Com cada método diferente que vamos abordar, é importante entender que todos eles são métodos integrados em Array.prototype. Isso significa que você simplesmente precisa encadeá-los em um array com notação de ponto. Isso também significa que esses métodos não estão disponíveis em objetos ou qualquer outra coisa além de Arrays (embora haja sobreposição com Strings).

      Veremos os seguintes métodos de Array:

      includes

      const alligator = ["thick scales", 80, "4 foot tail", "rounded snout"];
      
      alligator.includes("thick scales"); // returns true
      

      O método .includes() retorna um valor booleano e é perfeito para lhe dizer se um elemento existe ou não em um array. Ele dá uma resposta simples true ou false. Esta é a sintaxe básica:

      arr.includes(valueToFind, [fromIndex]);
      

      Agora, como você pode ver em nosso exemplo, tínhamos apenas um parâmetro – o valueToFind. Este é o valor a ser pesquisado na matriz. O fromIndex opcional é um número, indicando de qual índice você deseja começar a procurar (o padrão é 0, então todo o array é pesquisado). Assim, como em nosso exemplo o item ‘thick scales’ está no índice 0, o seguinte seria false: alligator.includes('thick scales', 1); uma vez que ele começa a pesquisar a partir do índice 1 em diante.

      Agora, há algumas coisas importantes a notar. Esse método .includes() usa comparação estrita. Isso significa que, a partir do exemplo acima, o seguinte retornaria false: alligator.includes('80'); isso é porque embora 80 == '80' seja verdadeiro, 80 === '80' é falso – diferentes tipos não passarão em uma comparação estrita.

      find

      Como o método.find() difere do método includes()? Se em nosso exemplo alterarmos o texto “includes” para “find”, obteremos esse erro:

      Uncaught TypeError: thick scales is not a function
      

      Isso é porque o método find requer que uma função seja passada. Isso é porque o método find não irá usar o operador de comparação simples como o “includes()” faz. Em vez disso, ele irá passar cada elemento em sua função e ver se ela retorna true ou false. Assim, embora isso funcione: alligator.find(() => 'thick scales'); você provavelmente gostaria de colocar seu próprio operador de comparação na função para que ele retorne qualquer coisa relevante.

      const alligator = ["thick scales", 80, "4 foot tail", "rounded snout"];
      
      alligator.find(el => el.length < 12); // returns '4 foot tail'
      

      Essa função simples em nosso método find faz uma busca em cada elemento da array, com o alias ‘el’ que atribuímos e termina ao encontrar o primeiro resultado verdadeiro. Em nosso caso, verdadeiro é ter uma propriedade de comprimento menor que 12 (os números não têm uma propriedade de comprimento). É claro que você poderia tornar essa função tão complexa quanto necessário, fazendo com que sua condição verdadeira atendesse às suas necessidades.

      Observe também, isso não retornou true. O método find não retorna um booleano, mas, em vez disso, retorna o primeiro elemento .correspondente. Se não houver um elemento de correspondência – como em nada existe que atenda aos critérios definidos em sua função – ele irá retornar undefined. Observe também que ele retorna o primeiro, então se houver mais de um elemento no array que atende aos critérios, ele irá pegar apenas a primeira instância. Em nosso exemplo, se houvesse outra sequência de comprimento menor que 12 após ‘4 feet tall’ isso não alteraria nosso resultado.

      Em nosso exemplo, usamos o callback apenas com um parâmetro. Pode-se também adicionar parâmetros para fazer referência ao índice do elemento atual. Outro parâmetro pode ser o array inteiro em si, mas eu acho que isso raramente é usado. Aqui está um exemplo usando o index:

      alligator.find((el, idx) => typeof el === "string" && idx === 2); // returns '4 foot tall'
      

      Sabemos que em nosso array, existem 3 elementos diferentes que atendem à primeira condição (typeof el === ‘string’). Se essa fosse a nossa única condição, ele retornaria a primeira, ‘thick scales’. Mas a diferença é que, apenas um tem o índice de 2 e ele é ‘4 foot tall’.

      Por falar em índices, um método de array similar é .findIndex(). Esse método também recebe uma função, mas como você pode imaginar, ele retorna o índice do elemento correspondente em vez do próprio elemento.

      indexOf

      const alligator = ["thick scales", 80, "4 foot tail", "rounded snout"];
      
      alligator.indexOf("rounded snout"); // returns 3
      

      Como o método .includes(), o .indexOf() usa comparação estrita e não uma função como vimos com o método .find(). Mas, ao contrário do includes(), ele retorna o índice do elemento, em vez de um booleano. Você também pode indicar em qual índice do array iniciar a pesquisa.

      Eu acho que indexOf() é muito útil. Ele é rápido e fácil, pode lhe dizer onde o elemento está no array e se o elemento existe. Como ele lhe diz se o elemento existe? Basicamente, podemos saber que o elemento existe se ele retorna um número positivo, e se ele retornar -1, sabemos que o elemento não existe.

      alligator.indexOf("soft and fluffy"); // returns -1
      alligator.indexOf(80); // returns 1
      alligator.indexOf(80, 2); // returns -1
      

      E como você pode ver, embora pudéssemos usar os métodos find() ou findIndex() para nos dar a mesma informação, isso é muito menos para escrever. Não precisamos escrever uma função para comparação, pois ela já está dentro do método indexOf.

      Agora, assim como os outros, indexOf() também retorna o índice do primeiro elemento de correspondência que ele encontra. O JavaScript nos dá um método de array alternativo .lastIndexOf(). Como você pode imaginar, isso faz a mesma coisa que indexOf(), mas a partir do último índice do array e trabalhando para trás. Você também pode especificar um segundo parâmetro, mas lembre-se que os índices não alteram, só porque você está usando um método diferente.

      const alligator = ["thick scales", 80, "4 foot tail", "rounded snout", 80];
      
      alligator.indexOf(80); // returns 1
      alligator.lastIndexOf(80); // returns 4
      alligator.indexOf(80, 2); // returns 4
      alligator.lastIndexOf(80, 4); // returns 4
      alligator.lastIndexOf(80, 3); // returns 1
      

      Bônus: filter

      const alligator = ["thick scales", 80, "4 foot tail", "rounded snout", 80];
      
      alligator.filter(el => el === 80); //returns [80, 80]
      

      O método filter() é como o método find(), na medida em que ele requer uma função passada e uma condição para o que será retornado. A diferença principal é que, filter() retorna sempre um array, mesmo que haja apenas um elemento correspondente. Mas ele irá retornar todos os elementos correspondentes, enquanto que find() retorna apenas a primeira correspondência.

      O importante com o filtro é que ele retorna todos os elementos que correspondem aos seus critérios. Só poderia ser eu, mas posso ficar confuso, pensando “esses são os elementos que quero filtrar out”, quando sinceramente, você está indicando os elementos que você deseja filtrar in.

      Conclusão

      O método mais fácil que acho para usar ao pesquisar por algo é o método find(), mas como você pode ver, depende realmente do seu caso.

      • Você precisa saber apenas se ele existe? Use .includes().
      • Você precisa obter o elemento em si? Use .find(), ou .filter() para vários itens.
      • Você precisa encontrar o índice do elemento? Use .indexOf() ou findIndex() para uma pesquisa mais complexa.

      Os arrays nos exemplos aqui foram muito simples. Você pode encontrar-se com um array de objetos. Aqui abaixo estão alguns exemplos básicos para navegar pela selva de objetos aninhados:

      const jungle = [
        { name: "frog", threat: 0 },
        { name: "monkey", threat: 5 },
        { name: "gorilla", threat: 8 },
        { name: "lion", threat: 10 }
      ];
      
      // break the object down in order to use .includes() or .indexOf()
      const names = jungle.map(el => el.name); // returns ['frog', 'monkey', 'gorilla', 'lion']
      console.log(names.includes("gorilla")); // returns true
      console.log(names.indexOf("lion")); // returns 3 - which corresponds correctly assuming no sorting was done
      
      // methods we can do on the array of objects
      console.log(jungle.find(el => el.threat == 5)); // returns object - {name: "monkey", threat: 5}
      console.log(jungle.filter(el => el.threat > 5)); // returns array - [{name: "gorilla", threat: 8}, {name: 'lion', threat: 10}]
      



      Source link

      Definindo métodos em Go


      Introdução

      As funções permitem que você organize a lógica em procedimentos repetíveis que possam usar diferentes argumentos sempre que forem executados. Durante o processo de definição das funções, com frequência você verá que várias funções podem operar na mesma parte dos dados a cada vez. O Go reconhece esse padrão e permite que você defina funções especiais, chamadas de methods (métodos), cujo objetivo é operar em instâncias de um tipo específico, chamado de receiver (receptor). A adição dos métodos aos tipos permite que você comunique não apenas sobre do que se tratam os dados, mas também como esses dados devem ser usados.

      Definindo um método

      A sintaxe para definir um método é similar à sintaxe para definir uma função. A única diferença é a adição de um parâmetro extra após a palavra-chave func, para especificar o receptor do método. O receptor é uma declaração do tipo que você deseja para definir o método. O exemplo a seguir define um método em um tipo struct:

      package main
      
      import "fmt"
      
      type Creature struct {
          Name     string
          Greeting string
      }
      
      func (c Creature) Greet() {
          fmt.Printf("%s says %s", c.Name, c.Greeting)
      }
      
      func main() {
          sammy := Creature{
              Name:     "Sammy",
              Greeting: "Hello!",
          }
          Creature.Greet(sammy)
      }
      

      Se executar este código, o resultado será:

      Output

      Sammy says Hello!

      Criamos um struct chamado Creature, com os campos da string para Name e Greeting. Este Creature tem um método único definido, o Greet. Dentro da declaração do receptor, atribuímos a instância do Creature à variável c para que pudéssemos referir-nos aos campos do Creature, à medida que agrupamos a mensagem de saudação no fmt.Printf.

      Em outras linguagens,normalmente, referimo-nos ao receptor das invocações de métodos por meio de uma palavra-chave (p. ex., this ou self). O Go considera o receptor como uma variável como qualquer outra. Assim, você pode dar a ele o nome que quiser. O estilo preferido pela comunidade para esse parâmetro é a versão do tipo de receptor com o primeiro caractere escrito em letra minúscula. Neste exemplo, usamos o c porque o tipo de receptor era o Creature.

      Dentro do corpo do main, criamos uma instância de Creature e especificamos os valores para seus campos Name e Greetings. Aqui, invocamos o método Greet, juntando o nome do tipo e o nome do método com um ., fornecendo a instância de Creature como o primeiro argumento.

      A linguagem Go oferece outras maneiras mais convenientes de chamar os métodos em instâncias de um struct, como mostramos neste exemplo:

      package main
      
      import "fmt"
      
      type Creature struct {
          Name     string
          Greeting string
      }
      
      func (c Creature) Greet() {
          fmt.Printf("%s says %s", c.Name, c.Greeting)
      }
      
      func main() {
          sammy := Creature{
              Name:     "Sammy",
              Greeting: "Hello!",
          }
          sammy.Greet()
      }
      

      Se executar isso, o resultado será o mesmo que o do exemplo anterior:

      Output

      Sammy says Hello!

      Esse exemplo é idêntico ao anterior. Porém, desta vez usamos dot notation para invocar o método Greet usando o Creature armazenado na variável sammy como o receptor. Esta é uma notação abreviada para a invocação da função no primeiro exemplo. A preferência que a biblioteca padrão e a comunidade Go têm por esse estilo é tanta que raramente veremos o estilo de invocação de função mostrado anteriormente.

      O próximo exemplo mostra um motivo pelo qual a notação de ponto é a mais prevalente:

      package main
      
      import "fmt"
      
      type Creature struct {
          Name     string
          Greeting string
      }
      
      func (c Creature) Greet() Creature {
          fmt.Printf("%s says %s!n", c.Name, c.Greeting)
          return c
      }
      
      func (c Creature) SayGoodbye(name string) {
          fmt.Println("Farewell", name, "!")
      }
      
      func main() {
          sammy := Creature{
              Name:     "Sammy",
              Greeting: "Hello!",
          }
          sammy.Greet().SayGoodbye("gophers")
      
          Creature.SayGoodbye(Creature.Greet(sammy), "gophers")
      }
      

      Se executar esse código, o resultado ficará parecido com este:

      Output

      Sammy says Hello!! Farewell gophers ! Sammy says Hello!! Farewell gophers !

      Nós modificamos os exemplos anteriores para introduzir um outro método chamado SayGoodbye e mudamos o Greet para retornar um Creature para que possamos invocar outros métodos naquela instância. No corpo do main, chamamos os métodos Greet e SayGoodbye na variável sammy, primeiro usando a notação de ponto e, depois, utilizando o estilo de invocação de função.

      Ambos os estilos produzem os mesmos resultados, mas o exemplo que usa a notação de ponto é muito mais legível. A cadeia de pontos também nos diz a sequência na qual os métodos serão invocados, onde o estilo funcional inverte essa sequência. A adição de um parâmetro à chamada SayGoodbye torna confusa a ordem das chamadas de método. A clareza da notação de ponto é o motivo deste ser o estilo preferido para invocar os métodos no Go: seja na biblioteca padrão ou entre pacotes de terceiros, você o encontrará por todo o ecossistema Go.

      Definir métodos sobre tipos, ao invés de definir funções que operam em algum valor, têm outra importância especial para a linguagem de programação Go. Métodos são o conceito fundamental por trás das interfaces.

      Interfaces

      Ao definir um método em qualquer tipo no Go, tal método será adicionado ao method set do tipo. O conjunto de métodos é a coleção de funções associadas àquele tipo como métodos, sendo usada pelo compilador do Go para determinar se algum tipo pode ser atribuído a uma variável com um tipo de interface. Um tipo de interface consiste na especificação dos métodos que o compilador utiliza para garantir que um tipo forneça as implementações para esses métodos. Qualquer tipo que possua métodos com nome, parâmetros e valores de retorno iguais àqueles encontrados na definição de uma interface são reconhecidos por implementar tal interface e podem ser atribuídos às variáveis com o mesmo tipo daquela interface. A definição da interface fmt.Stringer da bilbioteca padrão é a seguinte:

      type Stringer interface {
        String() string
      }
      

      Para que um tipo implemente a interface fmt.Stringer, ele precisa fornecer um método String() que retorna uma string. Implementar essa interface permitirá que o seu tipo seja impresso exatamente como quiser (por vezes chamado de “pretty-printed”(com estilo de formatação)) quando passar as instâncias do seu tipo para as funções definidas no pacote fmt. O exemplo a seguir define um tipo que implementa esta interface:

      package main
      
      import (
          "fmt"
          "strings"
      )
      
      type Ocean struct {
          Creatures []string
      }
      
      func (o Ocean) String() string {
          return strings.Join(o.Creatures, ", ")
      }
      
      func log(header string, s fmt.Stringer) {
          fmt.Println(header, ":", s)
      }
      
      func main() {
          o := Ocean{
              Creatures: []string{
                  "sea urchin",
                  "lobster",
                  "shark",
              },
          }
          log("ocean contains", o)
      }
      

      Quando executar o código, você verá este resultado:

      Output

      ocean contains : sea urchin, lobster, shark

      Esse exemplo define um novo tipo de struct chamado Ocean. O Ocean é conhecido por_ implementar_ a interface fmt.Stringer, uma vez que o Ocean define um método chamado String, o qual não precisa de parâmetros e retorna uma string. No main, definimos um novo Ocean e o passamos para uma função log, a qual toma uma string para imprimir primeiro, seguido de qualquer coisa que implemente o fmt.Stringer. Neste ponto, o compilador Go nos permite passar um o porque o Ocean implementa todos os métodos solicitados pelo fmt.Stringer. Dentro do log, usamos o fmt.Println, que chama o método String do Ocean quando ele encontra um fmt.Stringer como um dos seus parâmetros.

      Se o Ocean não fornecesse um método String(), o Go produziria um erro de compilação, pois o métodologsolicita um fmt.Stringer como seu argumento. O erro se parece com este:

      Output

      src/e4/main.go:24:6: cannot use o (type Ocean) as type fmt.Stringer in argument to log: Ocean does not implement fmt.Stringer (missing String method)

      O Go também irá certificar-se que o método String() fornecido corresponda exatamente ao que foi solicitado pela interface do fmt.Stringer. Se não o fizer, ele produzirá um erro que se parece com este:

      Output

      src/e4/main.go:26:6: cannot use o (type Ocean) as type fmt.Stringer in argument to log: Ocean does not implement fmt.Stringer (wrong type for String method) have String() want String() string

      Até agora, nos exemplos, definimos os métodos no receptor de valor. Ou seja, se usarmos a invocação funcional de métodos, o primeiro parâmetro – referindo-se ao tipo em que o método foi definido – será um valor desse tipo, em vez de um ponteiro. Consequentemente, quaisquer modificações que fizermos na instância fornecida para o método será descartada quando o método completar a execução, pois o valor recebido é uma cópia dos dados. Também é possível definir métodos no receptor ponteiro para um tipo.

      Ponteiros receptores

      A sintaxe para definir métodos no ponteiro receptor é quase idêntica à usada para definir os métodos no receptor de valor. A diferença é a prefixação do nome do tipo na declaração do receptor com um asterisco (*). O exemplo a seguir define um método no ponteiro receptor para um tipo:

      package main
      
      import "fmt"
      
      type Boat struct {
          Name string
      
          occupants []string
      }
      
      func (b *Boat) AddOccupant(name string) *Boat {
          b.occupants = append(b.occupants, name)
          return b
      }
      
      func (b Boat) Manifest() {
          fmt.Println("The", b.Name, "has the following occupants:")
          for _, n := range b.occupants {
              fmt.Println("t", n)
          }
      }
      
      func main() {
          b := &Boat{
              Name: "S.S. DigitalOcean",
          }
      
          b.AddOccupant("Sammy the Shark")
          b.AddOccupant("Larry the Lobster")
      
          b.Manifest()
      }
      

      Você verá o seguinte resultado quando executar este exemplo:

      Output

      The S.S. DigitalOcean has the following occupants: Sammy the Shark Larry the Lobster

      Esse exemplo definiu um tipo Boat (barco) com um Name (Nome) e os occupants (ocupantes). Queremos forçar o código em outros pacotes para adicionar apenas os ocupantes com o método AddOccupant. Assim, tornamos o campo campo occupants não exportado, deixando a primeira letra do nome do campo em letra minúscula. Também queremos garantir que chamar o AddOccupant fará a instância Boat ser modificada, motivo pelo qual definimos o AddOccupant no ponteiro receptor. Os ponteiros atuam como referência para uma uma instância específica de um tipo e não como uma cópia daquele tipo. Saber que o AddOccupant será chamado usando um ponteiro para o Boat garante que quaisquer modificações irão persistir.

      Dentro do main, definimos uma nova variável, b, a qual reterá um ponteiro para um Boat (*Boat). Invocamos o método AddOccupant duas vezes nessa instância para adicionar dois passageiros. O método Manisfest é definido no valor Boat porque, em sua definição, o receptor foi especificado como (b Boat). No main, ainda conseguimos chamar o Manifest porque o Go consegue desreferenciar automaticamente o ponteiro para obter o valor de Boat. O b.Manifest() é equivalente ao (*b). Manifest().

      O fato de um método ser definido em um ponteiro receptor ou em um receptor de valor tem implicações importantes ao se tentar atribuir valores para variáveis que sejam tipos de interface.

      Ponteiros receptores e interfaces

      Quando você atribuir um valor a uma variável com um tipo de interface, o compilador Go examinará o conjunto de métodos do tipo que está sendo atribuído para garantir que ele tenha os métodos que a interface espera. Os conjuntos de métodos para o ponteiro receptor e para o receptor de valor são diferentes pois os métodos – que recebem um ponteiro – podem modificar o seu receptor onde os que recebem um valor não podem.

      O exemplo a seguir demonstra a definição de dois métodos: uma no ponteiro receptor de um tipo e em seu receptor de valor. No entanto, apenas o ponteiro receptor poderá satisfazer a interface – também definida neste exemplo:

      package main
      
      import "fmt"
      
      type Submersible interface {
          Dive()
      }
      
      type Shark struct {
          Name string
      
          isUnderwater bool
      }
      
      func (s Shark) String() string {
          if s.isUnderwater {
              return fmt.Sprintf("%s is underwater", s.Name)
          }
          return fmt.Sprintf("%s is on the surface", s.Name)
      }
      
      func (s *Shark) Dive() {
          s.isUnderwater = true
      }
      
      func submerge(s Submersible) {
          s.Dive()
      }
      
      func main() {
          s := &Shark{
              Name: "Sammy",
          }
      
          fmt.Println(s)
      
          submerge(s)
      
          fmt.Println(s)
      }
      

      Quando executar o código, você verá este resultado:

      Output

      Sammy is on the surface Sammy is underwater

      Esse exemplo definiu uma interface chamada Submersible que espera tipos que possuam o método Dive(). Na sequência, definimos um tipo Shark com um campo Name e um método isUnderwater para monitorar o estado do Shark. Definimos um método Dive() no ponteiro receptor para o Shark, o qual modificou o isUnderwater para true. Também definimos o método String() do receptor do valor, de modo que ele pudesse imprimir claramente o estado do Shark, usando o fmt.Println, através da interface fmt.Stringer – aceita pelo fmt.Println, conforme examinamos anteriormente. Também usamos uma função submerge que recebe um parâmetro Submersible.

      Usar a interface Submersible em vez de uma *Shark permite que a função submerge dependa apenas do comportamento fornecido por um tipo. Isso torna a função submerge mais reutilizável, uma vez que você não teria que escrever novas funções submerge para um Submarine, uma Whale, ou qualquer outro habitante aquático no futuro – sobre os quais ainda nem pensamos. Contanto que eles definam um método Dive(), eles podem ser usados com a função submerge.

      Dentro do main definimos uma variável s que é um ponteiro para um Shark e que imprimiu um s imediatamente com o fmt.Println. Isso demonstra a primeira parte do resultado, Sammy is on the surface. Passamos o s para submerge e, então, chamamos o fmt.Println novamente com o s como seu argumento, a fim de verificar a segunda parte do resultado impresso, Sammy is underwater.

      Se nós mudássemos o s para ser um Shark, em vez de um *Shark, o compilador Go produziria o erro:

      Output

      cannot use s (type Shark) as type Submersible in argument to submerge: Shark does not implement Submersible (Dive method has pointer receiver)

      O lado bom é que o compilador Go nos diz que o Shark de fato tem um método Dive, o qual foi definido apenas no ponteiro receptor. Quando você ver essa mensagem em seu código, a solução é passar um ponteiro para o tipo de interface, usando o operador & antes da variável onde o tipo de valor estiver atribuído.

      Conclusão

      No final das contas, declarar os métodos no Go não é diferente de se definir as funções que recebem diferentes tipos de variáveis. Aplicam-se ao caso as mesmas regras encontradas em Trabalhando com ponteiros. O Go fornece algumas conveniências para essa definição de função extremamente comum e as coleta em conjuntos de métodos que podem ser fundamentados por tipos de interface. O uso de métodos de maneira eficaz permitirá que você trabalhe com interfaces em seu código, a fim de melhorar a capacidade de teste e deixando uma organização melhor para os futuros leitores do seu código.

      Se quiser aprender mais sobre a linguagem de programação Go de maneira geral, confira nossa série de artigos sobre Como codificar em Go.



      Source link