Scala for Backend Engineering & Functional Programming · Lekcja

Inteligentne konstruktory

Konstrukcja z walidacją

Lekcja 2 z 413 kroki

Inteligentne konstruktory to bezpłatna lekcja Scala for Backend Engineering & Functional Programming na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Scala for Backend Engineering & Functional Programming, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Scala for Backend Engineering & Functional Programming zawiera 4 lekcji w sumie.

Tworzenie z walidacją

Inteligentny konstruktor to fabryka, która sprawdza dane wejściowe przed utworzeniem wartości. Gwarantuje, że każda posiadana instancja spełnia niezmienniki typu.

  • Surowy konstruktor jest ukryty.
  • Publiczne jest tylko tworzenie z walidacją.
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))

Prywatny konstruktor

Oznaczenie konstruktora głównego jako private uniemożliwia wywołującym pominięcie walidacji. Obiekt towarzyszący staje się jedynym punktem wejścia.

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

Zwracanie Option

Zwracanie Option sygnalizuje, że tworzenie może się nie powieść. Wywołujący musi obsłużyć None, dzięki czemu niepoprawne dane nigdy nie przepływają po cichu dalej.

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

Zwracanie Either ze szczegółami błędu

Jeśli trzeba wyjaśnić, dlaczego walidacja się nie powiodła, należy zwrócić Either[Error, T]. Lewa gałąź zawiera opisowy komunikat.

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

Inteligentne konstruktory z case class

case class z prywatnym konstruktorem również działa. Należy pamiętać, że wygenerowane metody apply i copy także trzeba kontrolować, dlatego należy zdefiniować własną fabrykę i pozostawić konstruktor prywatny.

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

Łączenie zwalidowanych wartości

Ponieważ inteligentne konstruktory zwracają Option lub Either, można łączyć je za pomocą konstrukcji for, aby tworzyć większe zwalidowane obiekty.

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

Normalizowanie danych wejściowych

Inteligentny konstruktor może również normalizować dane, na przykład usuwać białe znaki lub zamieniać litery na małe, dzięki czemu wszystkie instancje mają postać kanoniczną.

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

Niezmienniki obowiązują zawsze

Gdy wartość przejdzie przez inteligentny konstruktor, jej niezmiennik jest gwarantowany przez cały czas jej istnienia. Kod korzystający z tej wartości może jej ufać bez ponownego sprawdzania.

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

Łączenie z typami niejawnymi

Aby uzyskać zwalidowane wartości bez kosztu w czasie działania, połącz inteligentny konstruktor z typem niejawnym. Nie jest tworzony obiekt opakowujący, a walidacja nadal kontroluje proces tworzenia.

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

Wiele reguł walidacji

Zastosuj kilka sprawdzeń po kolei. Pierwsza niespełniona reguła przerywa działanie i zwraca jasny błąd.

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

Kiedy używać inteligentnych konstruktorów

Należy ich używać zawsze, gdy typ ma niezmienniki, które mogłoby naruszyć surowe tworzenie.

  • Liczby z ograniczeniami, niepuste ciągi znaków, identyfikatory w określonym formacie.
  • Zwracaj Option w prostych przypadkach powodzenia lub niepowodzenia, a Either, gdy potrzebne jest uzasadnienie.
  • Zachowaj surowy konstruktor jako prywatny.
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))

Szybkie sprawdzenie

Sprawdź swoją wiedzę na temat inteligentnych konstruktorów.

Podsumowanie

Poznał(a) Pan/Pani inteligentne konstruktory.

  • Waliduj dane wejściowe w fabryce obiektu towarzyszącego.
  • Ustaw surowy konstruktor jako prywatny.
  • Zwracaj Option lub Either, aby wymusić obsługę niepowodzenia.
  • Opcjonalnie normalizuj dane wejściowe do postaci kanonicznej.
  • Niezmienniki będą wtedy obowiązywać przez cały czas istnienia wartości.
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))
Bezpłatny start

Ucz się Scala dzięki korepetycjom AI — za darmo

Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.

Kursy
39
Lekcje
143

Często zadawane pytania

Czy lekcja „Inteligentne konstruktory” jest bezpłatna?

Tak — pełny tekst „Inteligentne konstruktory” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Scala for Backend Engineering & Functional Programming, przejdź na CoddyKit PRO. Kurs Scala for Backend Engineering & Functional Programming zawiera 4 lekcji w sumie.

Co nauczysz się w „Inteligentne konstruktory”?

Konstrukcja z walidacją Ćwiczysz Scala for Backend Engineering & Functional Programming z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Scala for Backend Engineering & Functional Programming?

Nie wymagamy żadnego doświadczenia. Scala for Backend Engineering & Functional Programming w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „Inteligentne konstruktory”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Scala for Backend Engineering & Functional Programming?

Tak. Każda lekcja Scala for Backend Engineering & Functional Programming zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Modelowanie za pomocą ADT
  2. Inteligentne konstruktory
  3. Newtype
  4. Komponowanie domen
← Powrót do Scala for Backend Engineering & Functional Programming