Interfaz sort.Interface
Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy — lección 74 de 107.
El paquete sort proporciona otro excelente ejemplo de diseño basado en interfaces. Para ordenar una colección personalizada, su tipo debe implementar la sort.Interface:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}Estos tres métodos le dan al algoritmo de ordenación todo lo que necesita: la longitud de la colección, una forma de comparar elementos y una forma de intercambiarlos. Aquí se explica cómo hacer que un slice de structs personalizados sea ordenable:
type Person struct {
Name string
Age int
}
type ByAge []Person
func (a ByAge) Len() int { return len(a) }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func main() {
people := []Person{
{"Alice", 30},
{"Bob", 25},
{"Carol", 35},
}
sort.Sort(ByAge(people))
fmt.Println(people)
// [{Bob 25} {Alice 30} {Carol 35}]
}La idea clave es crear un tipo con nombre (ByAge) basado en tu slice. Esto te permite definir diferentes comportamientos de ordenación para los mismos datos. Podrías crear ByName con una implementación de Less diferente para ordenar alfabéticamente en su lugar.
Una vez que su tipo satisfaga sort.Interface, funciona con sort.Sort(), sort.Reverse() y sort.IsSorted() automáticamente.
Desafío
Fácil¡Vamos a construir un sistema de inventario de productos ordenable que demuestre el poder de sort.Interface de Go! Crearás un tipo Product e implementarás múltiples estrategias de ordenación, permitiendo que la misma colección se ordene de diferentes maneras.
Organizarás tu código en dos archivos:
product.go: Define tu tipo de producto y las implementaciones de ordenación.Crea un struct
Productcon tres campos:Name(string),Price(float64) yQuantity(int).Crea dos tipos con nombre basados en
[]Product:ByPrice- para ordenar productos por precio en orden ascendenteByQuantity- para ordenar productos por cantidad en orden descendente (la cantidad más alta primero)
Cada tipo debe implementar los tres métodos requeridos por
sort.Interface:Len(),Less(i, j int)ySwap(i, j int). El métodoLessdetermina el orden de clasificación para cada tipo.main.go: Construye y ordena tu inventario de productos.Lee un modo de ordenación (
priceoquantity), luego lee un recuento seguido de los detalles del producto. Cada producto se proporciona en tres líneas: nombre, precio y cantidad.Crea un slice de productos, ordénalos usando el tipo de ordenación apropiado según el modo, luego imprime cada producto en este formato:
[Name]: $[Price] (x[Quantity])Muestra los precios con dos decimales.
Se proporcionarán las siguientes entradas:
- Línea 1: Modo de ordenación (
priceoquantity) - Línea 2: Número de productos
- Líneas siguientes: Detalles del producto (nombre, precio, cantidad - tres líneas por producto)
Por ejemplo, dado:
price
3
Laptop
999.99
5
Mouse
29.99
50
Keyboard
79.99
25Tu salida debería ser:
Mouse: $29.99 (x50)
Keyboard: $79.99 (x25)
Laptop: $999.99 (x5)Y dado:
quantity
3
Laptop
999.99
5
Mouse
29.99
50
Keyboard
79.99
25Tu salida debería ser:
Mouse: $29.99 (x50)
Keyboard: $79.99 (x25)
Laptop: $999.99 (x5)Observa cómo los mismos datos de productos pueden ordenarse de manera diferente simplemente usando un tipo con nombre distinto. Una vez que tus tipos satisfacen sort.Interface, funcionan perfectamente con sort.Sort() de la biblioteca estándar.
Hoja de referencia
El paquete sort requiere que los tipos implementen sort.Interface para poder ser ordenados:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}Para hacer que un slice personalizado sea ordenable, cree un tipo con nombre e implemente los tres métodos requeridos:
type Person struct {
Name string
Age int
}
type ByAge []Person
func (a ByAge) Len() int { return len(a) }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
// Ordenar la colección
sort.Sort(ByAge(people))Puede crear múltiples tipos con nombre para el mismo slice para implementar diferentes comportamientos de ordenación. Cada tipo define su propio método Less para determinar el orden.
Una vez que un tipo satisface sort.Interface, funciona con sort.Sort(), sort.Reverse() y sort.IsSorted().
Pruébalo tú mismo
package main
import (
"bufio"
"fmt"
"os"
"sort"
"strconv"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Leer el modo de ordenación
var mode string
fmt.Fscanln(reader, &mode)
// Leer el número de productos
var count int
fmt.Fscanln(reader, &count)
// Leer productos
products := make([]Product, count)
for i := 0; i < count; i++ {
name, _ := reader.ReadString('\n')
name = name[:len(name)-1] // Eliminar el salto de línea
priceStr, _ := reader.ReadString('\n')
priceStr = priceStr[:len(priceStr)-1]
price, _ := strconv.ParseFloat(priceStr, 64)
qtyStr, _ := reader.ReadString('\n')
qtyStr = qtyStr[:len(qtyStr)-1]
quantity, _ := strconv.Atoi(qtyStr)
products[i] = Product{Name: name, Price: price, Quantity: quantity}
}
// TODO: Ordenar productos según el modo
// Si el modo es "price", usar el tipo ByPrice
// Si el modo es "quantity", usar el tipo ByQuantity
// Usar sort.Sort() con el tipo apropiado
// TODO: Imprimir cada producto en el formato:
// [Name]: $[Price] (x[Quantity])
// Usar fmt.Printf con %.2f para el formato del precio
}
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 archivos11Biblioteca estándar y POO
io.Reader e io.WriterInterfaz sort.InterfaceInterfaz fmt.Stringerencoding/json con StructsInterfaz http.HandlerRepaso - Modelos de REST API3Punteros 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 Pool