Menu
Coddy logo textTech

Domieszka a interfejs

Część sekcji Programowanie obiektowe ścieżki Dart w Coddy. Lekcja 55 z 110.

Wiesz już, czym miksy różnią się od dziedziczenia. Teraz porównajmy miksy z interfejsami, ponieważ oba rozwiązania pozwalają pracować z wieloma typami. Kluczowa różnica polega na tym, co zapewniają: interfejsy definiują kontrakt, a miksy zapewniają implementację.

Gdy implementujesz interfejs, zobowiązujesz się udostępnić określone metody, ale cały kod piszesz samodzielnie. Gdy używasz mixina, otrzymujesz gotowy kod za darmo.

// Podejście z interfejsem – musisz zaimplementować wszystko
abstract class Printable {
  void printInfo();
}

class Report implements Printable {
  @override
  void printInfo() {
    print('Printing report...');  // To musisz napisać samodzielnie
  }
}

// Podejście z mixinem – implementacja jest dołączona
mixin Printable {
  void printInfo() {
    print('Printing...');  // To już zostało napisane za Ciebie
  }
}

class Report with Printable {}  // Automatycznie uzyskuje printInfo()

Kiedy więc używać każdego z nich? Używaj interfejsu, gdy różne klasy potrzebują tych samych sygnatur metod, ale różnych implementacji. Używaj mixina, gdy klasy powinny współdzielić dokładnie to samo zachowanie.

FunkcjaInterfejsMiksyna
ZapewniaTylko kontraktKontrakt + kod
ImplementacjaPiszesz ją samodzielnieDołączona
Słowo kluczoweimplementswith
Najlepsze zastosowanieRóżne zachowania, to samo APIWspólne zachowanie

Możesz nawet połączyć oba podejścia — używać interfejsów do określania, co może robić klasa, a miksinów do dostarczania wspólnych implementacji tam, gdzie to odpowiednie. W Dart, łącząc oba podejścia, zawsze umieszczaj with przed implements:

class MyClass with SomeMixin implements SomeInterface {
  // with występuje przed implements
}
challenge icon

Wyzwanie

Łatwy

Zbudujmy system powiadomień, który pokazuje, kiedy używać interfejsów, a kiedy mikserów. Utworzysz interfejs dla powiadomień wymagających różnych implementacji oraz mikser dla współdzielonego zachowania związanego z logowaniem, które powinno być identyczne dla wszystkich typów powiadomień.

Podzielisz kod na trzy pliki:

  • notifiable.dart: Zdefiniuj klasę abstrakcyjną Notifiable, która pełni rolę interfejsu. Powinna deklarować dwie metody abstrakcyjne: send(String message) oraz getChannel(), która zwraca wartość typu String. Różne typy powiadomień zaimplementują te metody na różne sposoby, zależnie od konkretnego kanału.
  • logging.dart: Utwórz mikser Logging, który zapewnia współdzielone zachowanie, jakie powinny mieć wszystkie powiadomienia. Dodaj metodę logSent(String channel, String message), która wypisuje [LOG] Sent via [channel]: [message], oraz metodę logTimestamp(), która wypisuje [LOG] Timestamp recorded. To zachowanie jest identyczne dla wszystkich typów powiadomień, dlatego idealnie nadaje się do miksera.
  • main.dart: Zaimportuj oba pliki i utwórz dwie klasy powiadomień, które łączą interfejs z mikserem:
    • Klasa EmailNotification, która używa miksera Logging i implementuje Notifiable. Jej metoda getChannel() powinna zwracać 'Email', a metoda send() powinna wypisywać Sending email: [message], następnie wywoływać logSent() z kanałem i wiadomością, a potem wywoływać logTimestamp()
    • Klasa SMSNotification, która używa miksera Logging i implementuje Notifiable. Jej metoda getChannel() powinna zwracać 'SMS', a metoda send() powinna wypisywać Sending SMS: [message], następnie wywoływać logSent() z kanałem i wiadomością, a potem wywoływać logTimestamp()
    Następnie utwórz obiekt EmailNotification i wywołaj send('Hello World'), wypisz pusty wiersz, utwórz obiekt SMSNotification i wywołaj send('Quick update')

Zwróć uwagę, że interfejs wymusza na każdej klasie napisanie własnych implementacji metod send() i getChannel() (różne zachowanie), podczas gdy mikser zapewnia metody logowania, które działają identycznie w obu klasach (współdzielone zachowanie).

Oczekiwany wynik:

Sending email: Hello World
[LOG] Sent via Email: Hello World
[LOG] Timestamp recorded

Sending SMS: Quick update
[LOG] Sent via SMS: Quick update
[LOG] Timestamp recorded

Spróbuj swoich sił

import 'notifiable.dart';
import 'logging.dart';

// TODO: Utwórz klasę EmailNotification, która:
//       - używa miksinu Logging (with)
//       - implementuje Notifiable (implements)
//       - getChannel() zwraca 'Email'
//       - send() wypisuje 'Sending email: [message]', następnie wywołuje logSent(), a potem logTimestamp()

// TODO: Utwórz klasę SMSNotification, która:
//       - używa miksinu Logging (with)
//       - implementuje Notifiable (implements)
//       - getChannel() zwraca 'SMS'
//       - send() wypisuje 'Sending SMS: [message]', następnie wywołuje logSent(), a potem logTimestamp()

void main() {
  // TODO: Utwórz obiekt EmailNotification i wywołaj send('Hello World')
  
  // TODO: Wypisz pustą linię
  
  // TODO: Utwórz obiekt SMSNotification i wywołaj send('Quick update')
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Dart online