0Pricing
Scala for Backend Engineering & Functional Programming · Lezione

Costruttori intelligenti

Costruzione con validazione

Costruttori intelligenti è una lezione Scala for Backend Engineering & Functional Programming gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Scala for Backend Engineering & Functional Programming, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Scala for Backend Engineering & Functional Programming include 4 lezioni in totale.

Costruzione validata

Un costruttore intelligente è una factory che valida gli input prima di produrre un valore. Garantisce che ogni istanza posseduta soddisfi le invarianti del tipo.

  • Il costruttore grezzo è nascosto.
  • Solo la creazione validata è pubblica.
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))

Costruttore privato

Contrassegnare il costruttore principale come private impedisce ai chiamanti di aggirare la validazione. L'oggetto companion diventa l'unico punto di accesso.

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))

Restituire Option

Restituire Option segnala che la costruzione potrebbe fallire. Il chiamante deve gestire None, quindi i dati non validi non proseguono mai silenziosamente.

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))

Restituire Either per i dettagli dell'errore

Quando desidera spiegare perché la validazione è fallita, restituisca Either[Error, T]. Il ramo Left contiene un messaggio descrittivo.

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))

Costruttori intelligenti con case class

Anche una case class con costruttore privato funziona. Tenga presente che anche i metodi apply e copy generati devono essere controllati; definisca quindi una factory personalizzata e mantenga privato il costruttore.

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"))

Comporre valori validati

Poiché i costruttori intelligenti restituiscono Option o Either, è possibile comporli con le comprehension for per costruire oggetti validati più grandi.

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"))

Normalizzare l'input

Un costruttore intelligente può anche normalizzare i dati, ad esempio eliminando gli spazi superflui o convertendo in minuscolo, in modo che tutte le istanze condividano una forma canonica.

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  "))

Le invarianti restano valide per sempre

Una volta superato il costruttore intelligente, il valore mantiene la propria invariante per tutta la sua durata. Il codice a valle può fidarsi del valore senza verificarlo nuovamente.

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)))

Combinare con i tipi opachi

Per ottenere valori validati a costo nullo, abbini un costruttore intelligente a un tipo opaco. Non viene allocato alcun oggetto wrapper, ma la validazione continua a controllare la costruzione.

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))

Più regole di validazione

Applichi diversi controlli in sequenza. La prima regola che fallisce interrompe la sequenza e restituisce un errore chiaro.

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"))

Quando usare i costruttori intelligenti

Li utilizzi ogni volta che un tipo presenta invarianti che la costruzione grezza potrebbe violare.

  • Numeri entro un intervallo, stringhe non vuote, id formattati.
  • Restituisca Option per un semplice sì/no, Either per indicare i motivi.
  • Mantenga privato il costruttore grezzo.
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))

Verifica rapida

Verifichi la sua comprensione dei costruttori intelligenti.

Riepilogo

Ha imparato a usare i costruttori intelligenti.

  • Validi gli input in una factory companion.
  • Renda privato il costruttore grezzo.
  • Restituisca Option o Either per imporre la gestione degli errori.
  • Normalizzi facoltativamente l'input in una forma canonica.
  • Le invarianti saranno quindi valide per tutta la durata del valore.
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))

Domande Frequenti

La lezione «Costruttori intelligenti» è gratuita?

Sì — il testo completo di «Costruttori intelligenti» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Scala for Backend Engineering & Functional Programming, passa a CoddyKit PRO. Il corso Scala for Backend Engineering & Functional Programming include 4 lezioni in totale.

Cosa imparerò in «Costruttori intelligenti»?

Costruzione con validazione Eserciti Scala for Backend Engineering & Functional Programming con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Scala for Backend Engineering & Functional Programming?

Non è richiesta alcuna esperienza precedente. Scala for Backend Engineering & Functional Programming su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.

Quanto tempo richiede la lezione «Costruttori intelligenti»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Scala for Backend Engineering & Functional Programming?

Sì. Ogni lezione Scala for Backend Engineering & Functional Programming include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Modellazione con gli ADT
  2. Costruttori intelligenti
  3. Newtype
  4. Composizione dei domini
← Torna a Scala for Backend Engineering & Functional Programming