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 es una técnica en la que los métodos devuelven el receptor, lo que permite enlazar varias llamadas a métodos en una sola instrucción. Ya has visto esto en el patrón Builder, pero es un concepto independiente útil en muchos contextos.
La key es devolver un puntero a la estructura 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 lenguaje natural en comparación con declaraciones separadas. El chaining de métodos funciona mejor cuando las operaciones configuran o modifican un objeto sin necesidad de devolver otros valores. Se usa comúnmente en constructores de consultas, API de configuración y bibliotecas de aserciones para pruebas.
Recuerda que cada método debe usar un receptor puntero y devolver *StructType para que la cadena funcione correctamente.
Desafío
Fácil¡Construyamos un generador fluido de elementos HTML que use 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
Elementque almacene el nombre de la etiqueta, un mapa de atributos (de string a string) y el contenido interno. Crea estos métodos encadenables, cada uno de los cuales devuelve*Element:NewElement(tag string)- constructor que crea un nuevo Element con el nombre de etiqueta indicado y un mapa de atributos inicializado (vacío)Attr(key, value string)- añade un atributo al elementoID(id string)- método práctico que establece el atributo "id"Class(class string)- método práctico 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 representa
<tag>content</tag>. Si no hay contenido, representa<tag attr="value"></tag>.main.go: Construye elementos HTML usando el encadenamiento de métodos.Lee el nombre de la etiqueta y después lee un recuento de operaciones que se realizarán. Cada operación es una de las siguientes:
idseguido del valor del IDclassseguido del valor de la claseattrseguido del nombre y el valor del atributo (dos líneas)contentseguido del texto del contenido
Encadena todas las operaciones de forma fluida y después 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, dada la siguiente entrada:
div
3
id
container
class
main-content
content
Hello WorldTu salida debe ser:
<div class="main-content" id="container">Hello World</div>Y dada la siguiente entrada:
a
3
attr
href
https://example.com
attr
target
_blank
content
Click hereTu salida debe ser:
<a href="https://example.com" target="_blank">Click here</a>Y dada la siguiente entrada:
input
2
attr
type
text
attr
placeholder
Enter nameTu salida debe ser:
<input placeholder="Enter name" type="text"></input>Y dada la siguiente entrada:
p
1
content
Simple paragraphTu salida debe ser:
<p>Simple paragraph</p>Observa cómo el encadenamiento de métodos te permite construir elementos paso a paso de forma legible. Cada método modifica el elemento y lo devuelve, lo que permite encadenar directamente la siguiente llamada al método.
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: Según el tipo de operación, encadena el método apropiado
// Las operaciones pueden ser: "id", "class", "attr" o "content"
// - Para "id": lee la siguiente línea como el valor del ID
// - Para "class": lee la siguiente línea como el valor de la clase
// - Para "attr": lee las siguientes DOS líneas (nombre del atributo, luego el valor)
// - Para "content": lee la siguiente línea como el texto del contenido
// Usa el encadenamiento de métodos para construir el elemento de forma fluida
_ = operation // Elimina esta línea al implementar
}
// Imprime el HTML renderizado
fmt.Println(element.Render())
}
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
1Fundamentos de POO en Go
Archivos externosEspacio de trabajo y módulos de GoPaquetes e importacionesNombres exportados vs. no exportadosIntroducción a la POO en GoStructs como clasesDefinición de métodos en structsReceptores de puntero vs. de valorInicialización de structsFunciones constructorasResumen - Calculadora simple4Interfaces
Introducción a las interfacesImplementación implícitaLa interfaz como contratoInterfaz vacía (any)Type AssertionType SwitchComposición de interfacesInterfaces Stringer y ErrorResumen: Calculadora de figuras7Encapsulamiento
Campos exportados vs. no exportadosEncapsulamiento a nivel de paqueteMétodos Getter y SetterOcultamiento de información en GoResumen - Registros de estudiantes10Genéricos (Go 1.18+)
Introducción a los GenéricosParámetros de tipoRestricciones de tipoStructs genéricosSolución alternativa para métodos genéricosResumen - Colección genérica13Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón Abstract FactoryPatrón ObserverPatrón Strategy2Inmersión profunda en Tipos y Structs
Tipos básicos y compuestosDefiniciones de tipos personalizadosEtiquetas de StructsStructs anónimosStructs anidadosValores cero y por defectoRepaso - Libreta de contactos5Composición sobre herencia
Por qué Go no tiene herenciaConceptos básicos de incrustación de structsPromoción de métodosIncrustación de múltiples structsIncrustación vs AgregaciónShadowing de métodos incrustadosResumen - Jerarquía de empleados8Manejo de Errores y OOP
La interfaz errorTipos de errores personalizadosEnvoltura de errores (fmt.Errorf)Errores centinelaerrors.Is() y errors.As()Panic, Defer y RecoverResumen - Analizador de archivos3Punteros y Memoria
Fundamentos de punteros en GoPunteros a StructsPaso por valor vs referenciaLa función new()Garbage Collection en GoResumen - Constructor de Linked List6Polimorfismo en Go
Polimorfismo mediante interfacesDuck Typing en GoReglas de satisfacción de interfacesColecciones polimórficasInyección de dependenciasResumen - Procesador de pagos9Concurrencia y POO
Fundamentos de GoroutinesChannels y comunicaciónChannels con buffer vs. sin bufferSentencia Selectsync.Mutex y sync.RWMutexsync.WaitGroupDiseño de estructuras Thread-SafeResumen - Worker Pool12Conceptos avanzados de POO
Patrón Functional OptionsPatrón Builder en GoEncadenamiento de métodosAlias de tipos vs. definicionesFundamentos de ReflectionDescripción general de generación de códigoPractica por tu cuenta: Compilador de Go online