Menu
Coddy logo textTech

Encadenamiento de métodos

Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy — lección 81 de 107.

El encadenamiento de métodos (method chaining) es una técnica en la que los métodos devuelven el receptor, lo que permite vincular múltiples llamadas a métodos en una sola sentencia. Ya has visto esto en el patrón Builder, pero es un concepto independiente que resulta útil en muchos contextos.

La clave es devolver un puntero al struct desde cada método:

type Query struct {
    table   string
    columns []string
    where   string
    limit   int
}

func (q *Query) Select(cols ...string) *Query {
    q.columns = cols
    return q
}

func (q *Query) From(table string) *Query {
    q.table = table
    return q
}

func (q *Query) Where(condition string) *Query {
    q.where = condition
    return q
}

func (q *Query) Limit(n int) *Query {
    q.limit = n
    return q
}

Ahora puedes encadenar estas llamadas de forma fluida:

query := &Query{}
query.Select("name", "email").From("users").Where("active = true").Limit(10)

Esto se lee casi como un lenguaje natural en comparación con las sentencias por separado. El encadenamiento de métodos funciona mejor cuando las operaciones configuran o modifican un objeto sin necesidad de devolver otros valores. Se utiliza comúnmente en constructores de consultas, APIs de configuración y bibliotecas de aserciones de prueba.

Recuerda que cada método debe usar un receptor de puntero y devolver *StructType para que el encadenamiento funcione correctamente.

challenge icon

Desafío

Fácil

¡Vamos a construir un generador de elementos HTML fluido que utiliza el encadenamiento de métodos para construir etiquetas HTML con atributos y contenido! Esta es una aplicación práctica del encadenamiento de métodos: crearás una API que se lee casi como lenguaje natural al construir elementos HTML.

Organizarás tu código en dos archivos:

  • element.go: Define tu generador de elementos HTML con métodos encadenables.

    Crea una estructura Element que almacene el nombre de la etiqueta, un mapa de atributos (string a string) y el contenido interno. Construye estos métodos encadenables que devuelven cada uno *Element:

    • NewElement(tag string) - constructor que crea un nuevo Element con el nombre de etiqueta dado y un mapa de atributos inicializado (vacío)
    • Attr(key, value string) - añade un atributo al elemento
    • ID(id string) - método de conveniencia que establece el atributo "id"
    • Class(class string) - método de conveniencia que establece el atributo "class"
    • Content(text string) - establece el contenido interno del elemento

    Añade un método Render() que devuelva la cadena HTML. Los atributos deben aparecer en orden alfabético por clave. El formato es:

    <tag attr1="value1" attr2="value2">content</tag>

    Si no hay atributos, simplemente renderiza <tag>content</tag>. Si no hay contenido, renderiza <tag attr="value"></tag>.

  • main.go: Construye elementos HTML utilizando el encadenamiento de métodos.

    Lee el nombre de la etiqueta, luego lee un recuento de operaciones a realizar. Cada operación es una de las siguientes:

    • id seguido del valor del ID
    • class seguido del valor de la clase
    • attr seguido del nombre del atributo y el valor (dos líneas)
    • content seguido del texto del contenido

    Encadena todas las operaciones de forma fluida, luego imprime el HTML renderizado.

Se proporcionarán las siguientes entradas:

  • Línea 1: Nombre de la etiqueta
  • Línea 2: Número de operaciones
  • Líneas siguientes: Tipo de operación y valor(es)

Por ejemplo, dado:

div
3
id
container
class
main-content
content
Hello World

Tu salida debería ser:

<div class="main-content" id="container">Hello World</div>

Y dado:

a
3
attr
href
https://example.com
attr
target
_blank
content
Click here

Tu salida debería ser:

<a href="https://example.com" target="_blank">Click here</a>

Y dado:

input
2
attr
type
text
attr
placeholder
Enter name

Tu salida debería ser:

<input placeholder="Enter name" type="text"></input>

Y dado:

p
1
content
Simple paragraph

Tu salida debería ser:

<p>Simple paragraph</p>

Observa cómo el encadenamiento de métodos te permite construir elementos paso a paso de una manera legible. Cada método modifica el elemento y lo devuelve, permitiendo que la siguiente llamada al método se encadene directamente.

Hoja de referencia

El encadenamiento de métodos es una técnica en la que los métodos devuelven el receptor (normalmente *StructType), lo que permite vincular múltiples llamadas a métodos en una sola sentencia.

Cada método debe usar un receptor de puntero y devolver *StructType:

type Query struct {
    table   string
    columns []string
    where   string
    limit   int
}

func (q *Query) Select(cols ...string) *Query {
    q.columns = cols
    return q
}

func (q *Query) From(table string) *Query {
    q.table = table
    return q
}

func (q *Query) Where(condition string) *Query {
    q.where = condition
    return q
}

func (q *Query) Limit(n int) *Query {
    q.limit = n
    return q
}

Encadene llamadas a métodos de forma fluida:

query := &Query{}
query.Select("name", "email").From("users").Where("active = true").Limit(10)

El encadenamiento de métodos funciona mejor cuando las operaciones configuran o modifican un objeto sin necesidad de devolver otros valores. Se utiliza comúnmente en constructores de consultas (query builders), APIs de configuración y librerías de aserciones de prueba.

Pruébalo tú mismo

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)

	// Leer el nombre de la etiqueta
	scanner.Scan()
	tagName := scanner.Text()

	// Leer el número de operaciones
	scanner.Scan()
	numOps, _ := strconv.Atoi(scanner.Text())

	// Crear el elemento
	element := NewElement(tagName)

	// Procesar cada operación
	for i := 0; i < numOps; i++ {
		scanner.Scan()
		operation := scanner.Text()

		// TODO: Basado en el tipo de operación, encadenar el método apropiado
		// Las operaciones pueden ser: "id", "class", "attr", o "content"
		// - Para "id": leer la siguiente línea como el valor del ID
		// - Para "class": leer la siguiente línea como el valor de la clase
		// - Para "attr": leer las siguientes DOS líneas (nombre del atributo, luego el valor)
		// - Para "content": leer la siguiente línea como el texto del contenido
		// Usar el encadenamiento de métodos para construir el elemento de forma fluida

		_ = operation // Eliminar esta línea al implementar
	}

	// Imprimir el HTML renderizado
	fmt.Println(element.Render())
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos