Sobes.tech
Senior

Czym jest DCI?

sobes.tech AI

Odpowiedź od AI

DCI (Data-Context-Interaction) to wzorzec architektoniczny, który koncentruje się na tym, jak obiekty wchodzą w interakcję w określonym kontekście, aby wykonać określone działanie. Ma na celu poprawę czytelności i utrzymania kodu, oddzielając dane (Data) od zachowania w kontekście (Interaction), które działa na nich w określonej sytuacji (Context).

  • Data (Dane): Reprezentuje stan obiektów. Mogą to być zwykłe klasy lub struktury zawierające dane.
  • Context (Kontekst): Opisuje konkretną sytuację lub scenariusz, w którym dochodzi do interakcji obiektów. Kontekst łączy uczestników (obiekty danych) z rolami.
  • Interaction (Interakcja): Definiuje zachowanie lub logikę stosowaną do uczestników kontekstu. Interakcja realizowana jest przez role, które odgrywają uczestnicy w tym kontekście.

Zalety DCI:

  • Lepsza czytelność: Kod opisujący interakcję jest oddzielony od definicji danych, co czyni go bardziej zrozumiałym.
  • Redukcja duplikacji kodu: Zachowanie specyficzne dla scenariusza jest enkapsulowane, co zmniejsza konieczność duplikowania logiki.
  • Większa elastyczność: Łatwiej jest modyfikować lub dodawać nowe scenariusze interakcji bez zmiany definicji danych.
  • Testowalność: Oddzielne komponenty (dane, kontekst, interakcje) są łatwiejsze do testowania w izolacji.

Przykład (konceptualny):

Wyobraźmy sobie scenariusz "przelewu pieniędzy" między dwoma kontami bankowymi.

  • Data: Klasa BankAccount z polami balance i accountNumber.
  • Kontekst: MoneyTransferContext, który łączy dwa obiekty BankAccount z rolami "Source" (nadawca) i "Destination" (odbiorca).
  • Interakcja: Logika przelewu pieniędzy: odjęcie od salda nadawcy i dodanie do salda odbiorcy. Ta logika jest realizowana przez metody ról "Source" i "Destination" w ramach MoneyTransferContext.
// Przykład kodu koncepcyjnego (niepełna implementacja DCI)
class BankAccount {
  constructor(public balance: number, public accountNumber: string) {}

  // Metody specyficzne dla danych
  getBalance(): number {
    return this.balance;
  }

  deposit(amount: number): void {
    this.balance += amount;
  }

  withdraw(amount: number): void {
    if (this.balance >= amount) {
      this.balance -= amount;
    } else {
      throw new Error("Niewystarczające środki");
    }
  }
}

// Kontekst i interakcja (tutaj wymieszane dla prostoty)
class MoneyTransferContext {
  constructor(private source: BankAccount, private destination: BankAccount) {}

  transfer(amount: number): void {
    try {
      // Interakcja przez role (niejawnie tutaj)
      this.source.withdraw(amount);
      this.destination.deposit(amount);
      console.log(`Pomyślnie przelano ${amount} z ${this.source.accountNumber} do ${this.destination.accountNumber}`);
    } catch (error: any) {
      console.error(`Nieudany transfer pieniędzy: ${error.message}`);
    }
  }
}

// Użycie
const account1 = new BankAccount(1000, "123");
const account2 = new BankAccount(500, "456");

const transferContext = new MoneyTransferContext(account1, account2);
transferContext.transfer(200);
transferContext.transfer(1500); // Błąd z powodu niewystarczających środków

W prawdziwym DCI, interakcja (logika transfer) byłaby realizowana w oddzielnych metodach "ról", które "wstrzykuje się" lub "dodaje" do obiektów BankAccount w kontekście MoneyTransferContext, nie zmieniając samej klasy BankAccount. Jest to osiągane różnymi technikami w zależności od języka programowania.