0Pricing
Scala for Backend Engineering & Functional Programming · Lección

Constructores inteligentes

Construcción validada

Constructores inteligentes es una lección gratuita de Scala for Backend Engineering & Functional Programming en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Scala for Backend Engineering & Functional Programming, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Scala for Backend Engineering & Functional Programming incluye 4 lecciones en total.

Construcción validada

Un constructor inteligente es una fábrica que valida las entradas antes de producir un valor. Garantiza que cualquier instancia que conserve cumpla las invariantes del tipo.

  • El constructor original está oculto.
  • Solo se expone la creación validada.
class Age private (val value: Int)
object Age:
  def of(v: Int): Option[Age] =
    if v >= 0 then Some(new Age(v)) else None

object Main:
  def main(args: Array[String]): Unit =
    println(Age.of(30).map(_.value))

Constructor privado

Marcar el constructor principal como private impide que los clientes omitan la validación. El objeto acompañante se convierte en el único punto de entrada.

class Percentage private (val value: Int)
object Percentage:
  def of(v: Int): Option[Percentage] =
    if v >= 0 && v <= 100 then Some(new Percentage(v)) else None

object Main:
  def main(args: Array[String]): Unit =
    println(Percentage.of(50).map(_.value))
    println(Percentage.of(150))

Devolver Option

Devolver Option indica que la construcción puede fallar. El cliente debe gestionar None, por lo que los datos no válidos nunca siguen circulando silenciosamente.

class NonEmpty private (val value: String)
object NonEmpty:
  def of(s: String): Option[NonEmpty] =
    if s.nonEmpty then Some(new NonEmpty(s)) else None

object Main:
  def main(args: Array[String]): Unit =
    println(NonEmpty.of("").map(_.value))
    println(NonEmpty.of("hi").map(_.value))

Devolver Either para detallar el error

Cuando quiera explicar por qué falló la validación, devuelva Either[Error, T]. El valor Left contiene un mensaje descriptivo.

class Username private (val value: String)
object Username:
  def of(s: String): Either[String, Username] =
    if s.isEmpty then Left("empty")
    else if s.length > 10 then Left("too long")
    else Right(new Username(s))

object Main:
  def main(args: Array[String]): Unit =
    println(Username.of(""))
    println(Username.of("ada").map(_.value))

Constructores inteligentes con case class

Una case class con un constructor privado sigue funcionando. Tenga en cuenta que sus apply y copy generados también deben controlarse, por lo que debe definir una fábrica personalizada y mantener privado el constructor.

case class Email private (value: String)
object Email:
  def of(s: String): Option[Email] =
    if s.contains("@") then Some(Email(s)) else None

object Main:
  def main(args: Array[String]): Unit =
    println(Email.of("a@b.com"))
    println(Email.of("bad"))

Encadenar valores validados

Como los constructores inteligentes devuelven Option o Either, puede combinarlos mediante expresiones for para construir objetos validados más grandes.

case class Name private (value: String)
object Name:
  def of(s: String): Option[Name] =
    if s.nonEmpty then Some(Name(s)) else None

case class Person(name: Name, age: Int)

object Main:
  def make(n: String): Option[Person] =
    for nm <- Name.of(n) yield Person(nm, 20)

  def main(args: Array[String]): Unit =
    println(Main.make("Bob"))

Normalizar la entrada

Un constructor inteligente también puede normalizar los datos, por ejemplo, eliminando espacios en blanco o convirtiendo el texto a minúsculas, para que todas las instancias compartan una forma canónica.

case class Tag private (value: String)
object Tag:
  def of(s: String): Option[Tag] =
    val clean = s.trim.toLowerCase
    if clean.nonEmpty then Some(Tag(clean)) else None

object Main:
  def main(args: Array[String]): Unit =
    println(Tag.of("  Scala  "))

Las invariantes se mantienen siempre

Una vez que un valor supera el constructor inteligente, su invariante queda garantizada durante toda su vida útil. El código posterior puede confiar en el valor sin volver a comprobarlo.

case class PositiveInt private (value: Int)
object PositiveInt:
  def of(n: Int): Option[PositiveInt] =
    if n > 0 then Some(PositiveInt(n)) else None

object Main:
  def doubleIt(p: PositiveInt): Int = p.value * 2 // always positive

  def main(args: Array[String]): Unit =
    PositiveInt.of(5).foreach(p => println(Main.doubleIt(p)))

Combinar con tipos opacos

Para obtener valores validados sin coste adicional, combine un constructor inteligente con un tipo opaco. No se asigna ningún objeto envoltorio, pero la validación sigue controlando la construcción.

object Domain:
  opaque type Score = Int
  def of(n: Int): Option[Score] =
    if n >= 0 && n <= 100 then Some(n) else None
  extension (s: Score) def value: Int = s

object Main:
  def main(args: Array[String]): Unit =
    println(Domain.of(88).map(_.value))

Varias reglas de validación

Aplique varias comprobaciones en secuencia. La primera regla que falla interrumpe el proceso y devuelve un error claro.

object Password:
  def of(s: String): Either[String, String] =
    if s.length < 8 then Left("too short")
    else if !s.exists(_.isDigit) then Left("need a digit")
    else Right(s)

object Main:
  def main(args: Array[String]): Unit =
    println(Password.of("abc"))
    println(Password.of("abcdef12"))

Cuándo usar constructores inteligentes

Úselos siempre que un tipo tenga invariantes que la construcción directa pueda incumplir.

  • Números acotados, cadenas no vacías e identificadores con formato.
  • Devuelva Option para un resultado simple de sí o no y Either para explicar los motivos.
  • Mantenga privado el constructor original.
case class Port private (value: Int)
object Port:
  def of(n: Int): Option[Port] =
    if n >= 1 && n <= 65535 then Some(Port(n)) else None

object Main:
  def main(args: Array[String]): Unit =
    println(Port.of(8080))
    println(Port.of(70000))

Comprobación rápida

Compruebe su comprensión de los constructores inteligentes.

Resumen

Ha aprendido los constructores inteligentes.

  • Valide las entradas en una fábrica del objeto acompañante.
  • Haga privado el constructor original.
  • Devuelva Option o Either para obligar a gestionar los fallos.
  • Normalice opcionalmente la entrada a una forma canónica.
  • De este modo, las invariantes se mantienen durante toda la vida útil del valor.
case class Even private (value: Int)
object Even:
  def of(n: Int): Option[Even] =
    if n % 2 == 0 then Some(Even(n)) else None

object Main:
  def main(args: Array[String]): Unit =
    println(Even.of(4))
    println(Even.of(5))

Preguntas frecuentes

¿La lección «Constructores inteligentes» es gratis?

Sí — el texto completo de «Constructores inteligentes» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Scala for Backend Engineering & Functional Programming, actualiza a CoddyKit PRO. El curso de Scala for Backend Engineering & Functional Programming incluye 4 lecciones en total.

¿Qué aprenderé en «Constructores inteligentes»?

Construcción validada Practicas Scala for Backend Engineering & Functional Programming con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Scala for Backend Engineering & Functional Programming?

No se requiere experiencia previa. Scala for Backend Engineering & Functional Programming en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.

¿Cuánto tiempo toma la lección «Constructores inteligentes»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Scala for Backend Engineering & Functional Programming?

Sí. Cada lección de Scala for Backend Engineering & Functional Programming incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Modelado con ADT
  2. Constructores inteligentes
  3. Newtypes
  4. Composición de dominios
← Volver a Scala for Backend Engineering & Functional Programming