Interfejsy w C# – kontrakty określające umiejętności

Nauczysz się definiować „co obiekt potrafi” bez określania „jak to robi”. Interfejsy to klucz do elastycznego, rozszerzalnego kodu i wielokrotnej „dziedziczenia” zachowań!

interface implementacja wielokrotna IDisposable polimorfizm
01

Problem – tylko jedna klasa bazowa

W poprzednich materiałach poznaliśmy dziedziczenie i klasy abstrakcyjne. Ale jest ograniczenie: w C# klasa może dziedziczyć tylko po jednej klasie. Co jeśli potrzebujemy więcej?

cs/ProblemDziedziczenia.cs C#
// Mamy różne "umiejętności" jako klasy abstrakcyjne
public abstract class Plywajacy
{
    public abstract void Plyn();
}

public abstract class Latajacy
{
    public abstract void Lec();
}

public abstract class Chodzacy
{
    public abstract void Idz();
}

// Kaczka pływa, lata I chodzi... ale to nie zadziała!
public class Kaczka : Plywajacy, Latajacy, Chodzacy  // ❌ BŁĄD!
{
    // "Class 'Kaczka' cannot have multiple base classes"
}

// Samolot lata, ale nie pływa ani nie chodzi
public class Samolot : Latajacy  // OK, ale tylko jedna umiejętność
{
    public override void Lec() { }
}

// Amfibia pływa I jeździ... też nie zadziała!
public class Amfibia : Plywajacy, Chodzacy  // ❌ BŁĄD!
{ }
Dlaczego C# nie pozwala na wielokrotne dziedziczenie klas?

Problem zwany „diamentem śmierci” (Diamond Problem):

  • Co jeśli obie klasy bazowe mają metodę o tej samej nazwie?
  • Którą wersję odziedziczyć?
  • Języki jak C++ to pozwalają, ale prowadzi to do komplikacji

C# rozwiązuje to interfejsami – możesz implementować wiele interfejsów!

❌ Wielokrotne dziedziczenie KLAS
class Kaczka : A, B, C
NIEDOZWOLONE w C#!
✅ Wielokrotna implementacja INTERFEJSÓW
class Kaczka : IA, IB, IC
DOZWOLONE!
02

Co to jest interfejs?

Interfejs to „kontrakt” – lista metod i właściwości, które klasa obiecuje zaimplementować. Interfejs mówi CO obiekt potrafi, ale nie JAK to robi.

Analogia: Certyfikat umiejętności

Pomyśl o interfejsie jak o certyfikacie:

  • Certyfikat „Kierowca” = osoba umie prowadzić pojazd
  • Certyfikat „Ratownik” = osoba umie udzielić pierwszej pomocy
  • Certyfikat „Programista” = osoba umie pisać kod

Jedna osoba może mieć wiele certyfikatów! Podobnie klasa może implementować wiele interfejsów.

Analogia: Gniazdko elektryczne

Inna analogia:

  • Interfejs = standard gniazdka (np. europejskie typu E)
  • Klasy implementujące = różne urządzenia (laptop, telewizor, toster)

Każde urządzenie „implementuje” interfejs gniazdka – ma pasującą wtyczkę. Gniazdko nie wie, czy podłączony jest laptop czy toster – wie tylko, że „coś co pobiera prąd”.

Kluczowe cechy interfejsu

CechaOpis
Tylko deklaracjeInterfejs mówi CO, nie JAK (brak implementacji*)
Brak pólNie może mieć zmiennych (tylko właściwości)
Brak konstruktoraNie można utworzyć instancji interfejsu
Wszystko publiczneNie ma modyfikatorów dostępu (domyślnie public)
Wielokrotna implementacjaKlasa może implementować wiele interfejsów
Nazwa zaczyna się od IKonwencja: IComparable, IDisposable, IEnumerable

* Od C# 8.0 interfejsy mogą mieć domyślne implementacje – to zaawansowany temat.

interface IPlywa
void Plyn();
────────────
Tylko deklaracja!
Brak implementacji
↓ implementują
Kaczka
Plyn() → „Kwa kwa, płynę!”
Ryba
Plyn() → „Machanie płetwami”
Lodz
Plyn() → „Silnik pracuje”
03

Podstawowa składnia

Interfejs definiujemy słowem kluczowym interface. Nazwa interfejsu w C# zaczyna się od wielkiej litery I (konwencja).

cs/SkladniaInterface.cs C#
// Definicja interfejsu
public interface IPlywa
{
    // Tylko DEKLARACJA metody – brak ciała!
    void Plyn();                    // Bez public – domyślnie publiczne
    
    // Metoda z parametrem
    void PlynDoCelu(string cel);
    
    // Metoda zwracająca wartość
    double PobierzPredkosc();
}

public interface ILatajacy
{
    void Lec();
    void Laduj();
    int PobierzWysokosc();
}

public interface IJadalny
{
    int PobierzKalorie();
    bool CzySwiezy();
}
Czego NIE może mieć interfejs?
public interface IBledny
{
    // ❌ POLA – niedozwolone!
    private int pole;
    
    // ❌ KONSTRUKTOR – niedozwolone!
    public IBledny() { }
    
    // ❌ IMPLEMENTACJA (w starszych wersjach C#)
    void Metoda() { Console.WriteLine("Coś"); }
}

Interfejs to czysta deklaracja – mówi CO, nie JAK.

Porównanie składni

Klasa abstrakcyjna

public abstract class Plywajacy
{
    // Ma pola
    protected int predkosc;
    
    // Ma konstruktor
    public Plywajacy() { }
    
    // Ma implementację
    public void Info() { }
    
    // Metoda abstrakcyjna
    public abstract void Plyn();
}

Interfejs

public interface IPlywa
{
    // Brak pól!
    
    // Brak konstruktora!
    
    // Brak implementacji!
    
    // Tylko deklaracje
    void Plyn();
    int PobierzPredkosc();
}
04

Implementacja interfejsu

Klasa implementuje interfejs używając dwukropka (tak jak przy dziedziczeniu). Musi zaimplementować wszystkie metody zadeklarowane w interfejsie.

cs/Implementacja.cs C#
public interface IPlywa
{
    void Plyn();
    double PobierzPredkosc();
}

// Klasa implementuje interfejs
public class Kaczka : IPlywa
{
    private double predkosc = 5.0;
    
    // MUSI zaimplementować Plyn()
    public void Plyn()
    {
        Console.WriteLine("Kaczka płynie, machając łapkami 🦆");
    }
    
    // MUSI zaimplementować PobierzPredkosc()
    public double PobierzPredkosc()
    {
        return predkosc;
    }
    
    // Może mieć własne metody
    public void Kwacz()
    {
        Console.WriteLine("Kwa kwa!");
    }
}

public class Lodka : IPlywa
{
    public int MocSilnika { get; set; }
    
    public void Plyn()
    {
        Console.WriteLine($"Łódka płynie z mocą {MocSilnika} KM ⛵");
    }
    
    public double PobierzPredkosc()
    {
        return MocSilnika * 2.5;  // Prędkość zależy od mocy
    }
}
cs/UzycieImplementacji.cs C#
Kaczka kaczka = new Kaczka();
kaczka.Plyn();               // "Kaczka płynie, machając łapkami 🦆"
kaczka.Kwacz();              // "Kwa kwa!"

Lodka lodka = new Lodka { MocSilnika = 50 };
lodka.Plyn();                // "Łódka płynie z mocą 50 KM ⛵"

Console.WriteLine(kaczka.PobierzPredkosc());  // 5
Console.WriteLine(lodka.PobierzPredkosc());   // 125
Bez override!

Zauważ, że przy implementacji interfejsu nie używamy słowa override. Używamy go tylko przy nadpisywaniu metod virtual lub abstract z klas.

// Interfejs – BEZ override:
public void Plyn() { }

// Klasa abstrakcyjna – Z override:
public override void Plyn() { }
05

Wiele interfejsów naraz – główna siła!

To jest główna zaleta interfejsów: klasa może implementować wiele interfejsów jednocześnie. To rozwiązuje problem z sekcji 1!

cs/WieleInterfejsow.cs C#
// Trzy różne interfejsy = trzy różne umiejętności
public interface IPlywa
{
    void Plyn();
}

public interface ILata
{
    void Lec();
}

public interface IChodzi
{
    void Idz();
}

// Kaczka implementuje WSZYSTKIE TRZY! 🎉
public class Kaczka : IPlywa, ILata, IChodzi
{
    public string Imie { get; set; }
    
    public void Plyn()
    {
        Console.WriteLine($"{Imie} płynie po stawie 🦆");
    }
    
    public void Lec()
    {
        Console.WriteLine($"{Imie} leci na południe 🦆✈️");
    }
    
    public void Idz()
    {
        Console.WriteLine($"{Imie} kaczo spaceruje 🦆🚶");
    }
}

// Ryba – tylko pływa
public class Ryba : IPlywa
{
    public void Plyn()
    {
        Console.WriteLine("Ryba płynie pod wodą 🐟");
    }
}

// Samolot – tylko lata
public class Samolot : ILata
{
    public void Lec()
    {
        Console.WriteLine("Samolot leci na wysokości 10km ✈️");
    }
}

// Hydroplan – pływa I lata!
public class Hydroplan : IPlywa, ILata
{
    public void Plyn()
    {
        Console.WriteLine("Hydroplan kołuje po wodzie 🛩️");
    }
    
    public void Lec()
    {
        Console.WriteLine("Hydroplan startuje z wody i leci! 🛩️");
    }
}
IPlywa
Plyn()
ILata
Lec()
IChodzi
Idz()
↓ wszystkie trzy!
🦆 Kaczka : IPlywa, ILata, IChodzi
Ma wszystkie trzy umiejętności!
Interfejsy + klasa bazowa

Klasa może jednocześnie dziedziczyć po jednej klasie i implementować wiele interfejsów:

// Klasa bazowa + interfejsy
public class Kaczka : Ptak, IPlywa, ILata, IChodzi
{
    // Dziedziczy z Ptak + implementuje 3 interfejsy
}

Kolejność: najpierw klasa bazowa, potem interfejsy (oddzielone przecinkami).

06

Interfejs jako typ zmiennej (polimorfizm)

Interfejs może być typem zmiennej, parametru lub kolekcji. Pozwala to traktować różne obiekty jednakowo, jeśli implementują ten sam interfejs.

cs/InterfejsJakoTyp.cs C#
// Zmienna typu interfejsowego
IPlywa cos = new Kaczka();  // Kaczka implementuje IPlywa
cos.Plyn();                   // ✅ OK – metoda z interfejsu
// cos.Kwacz();               // ❌ BŁĄD – Kwacz() nie jest w IPlywa

// Możemy przypisać INNY obiekt implementujący IPlywa
cos = new Lodka();            // Łódka też implementuje IPlywa
cos.Plyn();                   // ✅ OK – inna implementacja

cos = new Ryba();             // Ryba też!
cos.Plyn();                   // ✅ OK – jeszcze inna implementacja

Tablica/lista obiektów implementujących interfejs

cs/KolekcjaInterfejsow.cs C#
// Lista WSZYSTKIEGO co pływa!
List<IPlywa> plywajace = new List<IPlywa>();

plywajace.Add(new Kaczka { Imie = "Donald" });
plywajace.Add(new Ryba());
plywajace.Add(new Lodka { MocSilnika = 100 });
plywajace.Add(new Hydroplan());

// Każdy pływa PO SWOJEMU!
foreach (IPlywa obiekt in plywajace)
{
    obiekt.Plyn();
}

// Wyświetli:
// Donald płynie po stawie 🦆
// Ryba płynie pod wodą 🐟
// Łódka płynie z mocą 100 KM ⛵
// Hydroplan kołuje po wodzie 🛩️

Interfejs jako parametr metody

cs/InterfejsJakoParametr.cs C#
// Metoda przyjmuje COKOLWIEK co pływa
public void WypuscNaWode(IPlywa obiekt)
{
    Console.WriteLine("Wypuszczam na wodę...");
    obiekt.Plyn();
}

// Użycie – działa z każdym obiektem implementującym IPlywa
WypuscNaWode(new Kaczka());    // ✅
WypuscNaWode(new Lodka());     // ✅
WypuscNaWode(new Hydroplan()); // ✅
// WypuscNaWode(new Samolot()); // ❌ Samolot nie implementuje IPlywa!
Siła polimorfizmu interfejsów

Jedna metoda WypuscNaWode() działa z kaczką, łódką, rybą, hydroplanem – z czymkolwiek co pływa. Nie musisz pisać osobnej metody dla każdego typu!

07

Ewolucja kodu – od problemu do rozwiązania

Zobaczmy pełną transformację – jak interfejsy rozwiązują problem z sekcji 1.

❌ PROBLEM: Bez interfejsów

cs/BezInterfejsow.cs C#
// Musimy wybierać JEDNĄ klasę bazową
public abstract class Zwierze
{
    public string Nazwa { get; set; }
}

// Kaczka dziedziczy po Zwierze – OK
public class Kaczka : Zwierze
{
    // Ale jak dodać pływanie, latanie, chodzenie?
    // Musimy wszystko wpisać ręcznie, bez żadnego kontraktu
    
    public void Plyn() { }     // Pisane "z pamięci"
    public void Lec() { }      // Może się pomylić nazwa
    public void Idz() { }      // Brak gwarancji spójności
}

// Chcemy metodę dla wszystkiego co pływa... ale jak?
public void WypuscNaWode(/* jaki typ? */)
{
    // Kaczka pływa, Łódka pływa, Ryba pływa...
    // Ale nie mają wspólnego typu!
}

✅ ROZWIĄZANIE: Z interfejsami

cs/ZInterfejsami.cs C#
// Interfejsy definiują "umiejętności"
public interface IPlywa { void Plyn(); }
public interface ILata { void Lec(); }
public interface IChodzi { void Idz(); }

// Klasa bazowa dla wspólnych cech
public abstract class Zwierze
{
    public string Nazwa { get; set; }
    public abstract void WydajDzwiek();
}

// Kaczka: dziedziczy po Zwierze + implementuje 3 interfejsy!
public class Kaczka : Zwierze, IPlywa, ILata, IChodzi
{
    public override void WydajDzwiek() => Console.WriteLine("Kwa!");
    
    // Kompilator WYMUSZA implementację wszystkich metod z interfejsów:
    public void Plyn() => Console.WriteLine($"{Nazwa} płynie");
    public void Lec() => Console.WriteLine($"{Nazwa} leci");
    public void Idz() => Console.WriteLine($"{Nazwa} idzie");
}

// Łódka: NIE jest Zwierzęciem, ale PŁYWA
public class Lodka : IPlywa
{
    public void Plyn() => Console.WriteLine("Łódka płynie");
}

// Teraz możemy napisać uniwersalną metodę!
public void WypuscNaWode(IPlywa obiekt)
{
    obiekt.Plyn();  // Działa z Kaczką, Łódką, Rybą...
}

❌ Bez interfejsów

  • Tylko jedna klasa bazowa
  • Brak wspólnego typu dla „pływających”
  • Brak wymuszenia implementacji
  • Metody pisane „z pamięci”
  • Trudno o polimorfizm

✅ Z interfejsami

  • Klasa bazowa + wiele interfejsów
  • IPlywa = wspólny typ
  • Kompilator wymusza implementację
  • Spójne nazwy metod
  • Pełny polimorfizm!
08

Popularne interfejsy w .NET

.NET Framework zawiera wiele gotowych interfejsów, które warto znać. Implementując je, Twoje klasy zyskują nowe możliwości!

InterfejsDo czego służy?Metoda do implementacji
IComparable<T>Porównywanie, sortowanieCompareTo(T other)
IEquatable<T>Sprawdzanie równościEquals(T other)
IDisposableZwalnianie zasobówDispose()
IEnumerable<T>Iterowanie foreachGetEnumerator()
ICloneableKopiowanie obiektówClone()

Przykład: IComparable – sortowanie

cs/IComparablePrzyklad.cs C#
public class Produkt : IComparable<Produkt>
{
    public string Nazwa { get; set; }
    public decimal Cena { get; set; }
    
    // Implementacja IComparable – sortowanie po cenie
    public int CompareTo(Produkt other)
    {
        if (other == null) return 1;
        return Cena.CompareTo(other.Cena);
    }
    
    public override string ToString() => $"{Nazwa}: {Cena:C}";
}

// Użycie:
List<Produkt> produkty = new List<Produkt>
{
    new Produkt { Nazwa = "Laptop", Cena = 3500 },
    new Produkt { Nazwa = "Mysz", Cena = 50 },
    new Produkt { Nazwa = "Monitor", Cena = 1200 }
};

produkty.Sort();  // Działa dzięki IComparable!

foreach (var p in produkty)
    Console.WriteLine(p);

// Wyświetli (posortowane po cenie):
// Mysz: 50,00 zł
// Monitor: 1 200,00 zł
// Laptop: 3 500,00 zł

Przykład: IDisposable – zwalnianie zasobów

cs/IDisposablePrzyklad.cs C#
public class PolaczenieBazy : IDisposable
{
    private bool polaczony = false;
    
    public void Polacz()
    {
        polaczony = true;
        Console.WriteLine("Połączono z bazą danych");
    }
    
    public void WykonajZapytanie(string sql)
    {
        if (!polaczony) throw new Exception("Brak połączenia!");
        Console.WriteLine($"Wykonuję: {sql}");
    }
    
    // Implementacja IDisposable – sprząta po sobie
    public void Dispose()
    {
        if (polaczony)
        {
            polaczony = false;
            Console.WriteLine("Rozłączono z bazą danych");
        }
    }
}

// Użycie z using – automatyczne wywołanie Dispose()!
using (PolaczenieBazy db = new PolaczenieBazy())
{
    db.Polacz();
    db.WykonajZapytanie("SELECT * FROM Users");
}  // ← Tutaj automatycznie wywołane Dispose()!

// Wyświetli:
// Połączono z bazą danych
// Wykonuję: SELECT * FROM Users
// Rozłączono z bazą danych  ← automatycznie!
using = automatyczny Dispose()

Gdy używasz using z obiektem implementującym IDisposable, C# automatycznie wywoła Dispose() na końcu bloku – nawet jeśli wystąpi wyjątek!

09

Właściwości w interfejsach

Interfejsy mogą deklarować właściwości (properties), nie tylko metody. Klasa implementująca musi je dostarczyć.

cs/WlasciwosciWInterfejsie.cs C#
public interface IIdentyfikowalny
{
    // Właściwość tylko do odczytu
    int Id { get; }
    
    // Właściwość do odczytu i zapisu
    string Nazwa { get; set; }
}

public interface ICenowany
{
    decimal Cena { get; set; }
    decimal ObliczCeneZVAT(decimal stawkaVAT);
}

// Klasa implementuje oba interfejsy
public class Produkt : IIdentyfikowalny, ICenowany
{
    // Implementacja IIdentyfikowalny
    public int Id { get; }  // Tylko get – bo interfejs tak wymaga
    public string Nazwa { get; set; }
    
    // Implementacja ICenowany
    public decimal Cena { get; set; }
    
    public decimal ObliczCeneZVAT(decimal stawkaVAT)
    {
        return Cena * (1 + stawkaVAT);
    }
    
    // Konstruktor ustawia Id (bo jest tylko get)
    public Produkt(int id)
    {
        Id = id;
    }
}

// Użycie:
Produkt p = new Produkt(1) { Nazwa = "Laptop", Cena = 3000 };
Console.WriteLine($"{p.Nazwa} z VAT: {p.ObliczCeneZVAT(0.23m):C}");
// Laptop z VAT: 3 690,00 zł
Interfejs a pola

Interfejs może mieć właściwości (z get/set), ale nie może mieć pól (zmiennych):

public interface IBledny
{
    int Pole;                    // ❌ BŁĄD – pola niedozwolone!
    int Wlasciwosc { get; set; } // ✅ OK – właściwość
}
10

interface vs abstract class – pełne porównanie

Cechainterfaceabstract class
Wielokrotne dziedziczenie ✅ Wiele interfejsów ❌ Tylko jedna klasa
Implementacja metod ❌ Tylko deklaracje* ✅ Może mieć implementację
Pola ❌ Nie ✅ Tak
Konstruktor ❌ Nie ✅ Tak
Modyfikatory dostępu ❌ Wszystko public ✅ Dowolne
Relacja „Umie” (can-do) „Jest rodzajem” (is-a)
Kiedy używać? Definiujesz umiejętności Masz wspólny kod

* Od C# 8.0 interfejsy mogą mieć domyślne implementacje.

Schemat decyzyjny

Pytanie 1
Czy masz wspólny kod
do współdzielenia?
TAK
→ abstract class
NIE
→ interface
Pytanie 2
Czy klasa musi „dziedziczyć”
po wielu źródłach?
TAK
→ interface
NIE
→ oba mogą działać

Praktyczny przykład – łączenie obu

cs/LaczenieObu.cs C#
// Interfejsy definiują UMIEJĘTNOŚCI
public interface IPlywa { void Plyn(); }
public interface ILata { void Lec(); }
public interface IJadalny { int Kalorie(); }

// Klasa abstrakcyjna definiuje WSPÓLNY KOD
public abstract class Zwierze
{
    public string Nazwa { get; set; }
    protected int wiek;
    
    public void Spij()
    {
        Console.WriteLine($"{Nazwa} śpi... Zzz");
    }
    
    public abstract void WydajDzwiek();
}

// Kaczka: JEST zwierzęciem + UMIE pływać, latać, jest jadalna
public class Kaczka : Zwierze, IPlywa, ILata, IJadalny
{
    public override void WydajDzwiek() => Console.WriteLine("Kwa!");
    public void Plyn() => Console.WriteLine($"{Nazwa} płynie");
    public void Lec() => Console.WriteLine($"{Nazwa} leci");
    public int Kalorie() => 250;
}

// Pies: JEST zwierzęciem + UMIE pływać (ale nie lata, nie jest jadalny)
public class Pies : Zwierze, IPlywa
{
    public override void WydajDzwiek() => Console.WriteLine("Hau!");
    public void Plyn() => Console.WriteLine($"{Nazwa} pływa pieskiem");
}
Najlepsza praktyka

Często najlepsze rozwiązanie to kombinacja:

  • abstract class – dla wspólnego kodu i relacji „jest rodzajem”
  • interface – dla umiejętności i „wielokrotnego dziedziczenia”

Kaczka JEST zwierzęciem (dziedziczy) i UMIE pływać, latać (implementuje interfejsy).

11

Kiedy używać interfejsów?

✅ Używaj interface gdy:

SytuacjaPrzykład
Definiujesz „umiejętność”, nie „rodzaj”IPlywa – kaczka i łódka pływają, ale to różne rzeczy
Potrzebujesz wielokrotnej implementacjiKlasa : IA, IB, IC
Nie masz wspólnego koduTylko deklaracje metod
Chcesz luźne powiązanie (loose coupling)Dependency Injection
Różne klasy mają wspólne zachowanieIComparable – sortowanie różnych typów

❌ NIE używaj interface gdy:

SytuacjaCo zamiast?
Masz wspólny kod do współdzieleniaabstract class
Potrzebujesz pólabstract class
Relacja „jest rodzajem”Dziedziczenie klas
Tylko jedna klasa będzie implementowaćMoże niepotrzebny?

✅ Dobre interfejsy

  • IPlywa, ILata, IChodzi
  • IComparable, IDisposable
  • ISerializable, ICloneable
  • ILogger, IEmailSender
  • IRepository, IService

❌ Złe interfejsy

  • IPies (to klasa, nie umiejętność)
  • ISamochod (to rzecz, nie zachowanie)
  • IData (za ogólne)
  • Interfejs z jedną metodą dla jednej klasy
12

Częste błędy

❌ Błąd 1: Próba utworzenia instancji interfejsu

❌ Źle

IPlywa p = new IPlywa();
// BŁĄD: Cannot create an instance
// of the abstract type or
// interface 'IPlywa'

✅ Dobrze

IPlywa p = new Kaczka();
// OK! Zmienna typu interfejsowego,
// obiekt klasy implementującej

❌ Błąd 2: Użycie override przy interfejsie

❌ Źle

public class Kaczka : IPlywa
{
    public override void Plyn()
    { }
}
// BŁĄD: 'Kaczka.Plyn()' is marked
// as override but no suitable
// method found

✅ Dobrze

public class Kaczka : IPlywa
{
    public void Plyn()
    { }
}
// OK! Bez override dla interfejsu

❌ Błąd 3: Pola w interfejsie

❌ Źle

public interface IPlywa
{
    int predkosc;  // POLE
}
// BŁĄD: Interfaces cannot
// contain instance fields

✅ Dobrze

public interface IPlywa
{
    int Predkosc { get; }  // WŁAŚCIWOŚĆ
}
// OK!

❌ Błąd 4: Modyfikatory dostępu w interfejsie

❌ Źle

public interface IPlywa
{
    public void Plyn();
    private void PomocniczaMetoda();
}
// Ostrzeżenie/Błąd – modyfikatory
// nie są potrzebne/dozwolone

✅ Dobrze

public interface IPlywa
{
    void Plyn();  // Bez modyfikatora
    void PomocniczaMetoda();
}
// OK! Wszystko domyślnie public

❌ Błąd 5: Brakująca implementacja

❌ Źle

public interface IPlywa
{
    void Plyn();
    double PobierzPredkosc();
}

public class Kaczka : IPlywa
{
    public void Plyn() { }
    // Brak PobierzPredkosc()!
}
// BŁĄD: 'Kaczka' does not
// implement 'IPlywa.PobierzPredkosc'

✅ Dobrze

public class Kaczka : IPlywa
{
    public void Plyn() { }
    
    public double PobierzPredkosc()
    {
        return 5.0;
    }
}
// OK! Wszystko zaimplementowane
13

Podsumowanie

Kluczowe pojęcia
  • interface = kontrakt definiujący „co obiekt umie”
  • Implementacja = klasa dostarcza kod dla metod interfejsu
  • Wielokrotna implementacja = klasa może implementować wiele interfejsów
  • Brak instancji = nie można zrobić new IInterface()
  • Brak override = przy interfejsach nie używamy override
  • Konwencja nazewnictwa = nazwa zaczyna się od I (IComparable)

Porównanie trzech podejść

Zwykła klasaabstract classinterface
Instancja✅ new Klasa()❌ Nie❌ Nie
Implementacja✅ Tak✅ Częściowa❌ Nie*
Pola✅ Tak✅ Tak❌ Nie
Wielokrotne❌ 1 bazowa❌ 1 bazowa✅ Wiele
Relacjais-ais-acan-do

Schemat składni

cs/Schemat.cs C#
// Definicja interfejsu
public interface INazwaInterfejsu
{
    // Metody (bez ciała)
    void Metoda();
    int MetodaZwracajaca();
    
    // Właściwości
    string Wlasciwosc { get; set; }
}

// Implementacja – sama klasa
public class Klasa : INazwaInterfejsu
{
    public void Metoda() { /* implementacja */ }
    public int MetodaZwracajaca() { return 0; }
    public string Wlasciwosc { get; set; }
}

// Implementacja – klasa bazowa + interfejsy
public class Klasa : KlasaBazowa, IInterfejs1, IInterfejs2
{
    // Implementacja wszystkich metod ze wszystkich interfejsów
}
Zadania

Zadania praktyczne

📝 Zadanie 1: System płatności

Utwórz interfejs i implementacje:

  • IMetodaPlatnosci: bool Zaplac(decimal kwota), string PobierzNazwe()
  • KartaKredytowa: NumerKarty, Limit
  • PayPal: Email, Saldo
  • Blik: NumerTelefonu
  • Gotowka: KwotaWPortfelu

Napisz metodę void RealizujZamowienie(IMetodaPlatnosci platnosc, decimal kwota) działającą z dowolną metodą płatności.

💡 Podpowiedź: Zaplac() zwraca true jeśli płatność się powiodła

📝 Zadanie 2: System eksportu danych

Utwórz:

  • IEksporter: void Eksportuj(string dane), string PobierzRozszerzenie()
  • EksportCSV: eksportuje do .csv
  • EksportJSON: eksportuje do .json
  • EksportXML: eksportuje do .xml
  • EksportPDF: eksportuje do .pdf

Utwórz listę List<IEksporter> i wyeksportuj dane do wszystkich formatów.

💡 Podpowiedź: Każdy eksporter może mieć własny sposób formatowania

📝 Zadanie 3: Urządzenia wielofunkcyjne

Utwórz interfejsy i klasy:

  • IDrukuje: void Drukuj(string dokument)
  • ISkanuje: string Skanuj()
  • IKopiuje: void Kopiuj(int ileKopii)
  • IFaksuje: void WyslijFaks(string numer, string dokument)
  • Drukarka: tylko drukuje
  • Skaner: tylko skanuje
  • Ksero: skanuje i drukuje (więc też kopiuje!)
  • UrządzenieWielofunkcyjne: wszystko!

💡 Podpowiedź: IKopiuje może wykorzystywać ISkanuje i IDrukuje wewnętrznie

⭐ Zadanie 4: System powiadomień

Utwórz:

  • IPowiadomienie: void Wyslij(string odbiorca, string tresc), bool CzyWyslano { get; }
  • Email: AdresNadawcy, Temat
  • SMS: NumerNadawcy (max 160 znaków!)
  • Push: Priorytet (Low/Normal/High)
  • Slack: Kanal, MozeBycEmoji

Utwórz klasę MenadzerPowiadomien z metodą void WyslijDoWszystkich(List<IPowiadomienie>, string odbiorca, string tresc).

💡 Podpowiedź: SMS może skracać treść automatycznie

⭐⭐ Zadanie 5: Gra RPG – pełny system

Połącz dziedziczenie, klasy abstrakcyjne i interfejsy:

  • abstract Postac: Imie, HP, abstract void Atakuj(Postac cel)
  • IBroniSieWalce: int Blokuj(), int PobierzPancerz()
  • IUzywaMagii: int Mana { get; }, void RzucCzar(Postac cel, string zaklecie)
  • ILeczy: void Lecz(Postac cel, int ilosc)
  • IKradnie: bool Okradnij(Postac cel)
  • Wojownik : Postac, IBroniSieWalce
  • Mag : Postac, IUzywaMagii
  • Paladyn : Postac, IBroniSieWalce, IUzywaMagii, ILeczy
  • Lotrzyk : Postac, IKradnie
  • Bard : Postac, IUzywaMagii, ILeczy

Utwórz symulację walki, gdzie różne postacie używają swoich unikalnych umiejętności.

💡 Podpowiedź: Użyj sprawdzania if (postac is ILeczy leczacy) do dynamicznego wywoływania umiejętności