08
Interfejsy: definiowanie kontraktów funkcjonalnych
Cel zadania

Zrozumienie interfejsów jako kontraktów określających zachowanie klasy bez wymuszania konkretnej hierarchii dziedziczenia. Student nauczy się implementować wiele interfejsów w jednej klasie, rozróżniać implementację jawną od niejawnej oraz wykorzystywać systemowy interfejs IComparable<T> do automatycznego sortowania kolekcji.

Scenariusz problemowy

W Twoim komisie "Auto-Premium" pojawiają się nowe możliwości: sprzedaż krajowa, eksport zagraniczny oraz wynajem długoterminowy. Szybko zauważasz, że dziedziczenie to za mało. Na placu stoją przecież różne obiekty: samochody, motocykle, a czasem nawet drogie akcesoria. Nie wszystkie są na sprzedaż (niektóre to tylko auta demonstracyjne), a niektóre mogą być sprzedawane wyłącznie poza granice kraju. Rozwiązaniem są interfejsy – czyste kontrakty, które mówią "co" dany obiekt potrafi robić (np. być sprzedanym), a nie "czym" merytorycznie jest.

Postanawiasz wprowadzić interfejsy ISprzedawalny i IEksportowalny. Dzięki nim możesz potraktować zupełnie różne obiekty jako grupę elementów "sprzedawalnych" i wyliczyć dla nich łączną wartość, nie martwiąc się czy dany element ma koła, czy silnik. Dodatkowo, aby Twój system był bardziej przyjazny dla klienta, implementujesz standardowy interfejs IComparable. Dzięki niemu lista pojazdów na stronie internetowej komisu będzie mogła zostać automatycznie posortowana od najtańszego do najdroższego auta za pomocą jednego polecenia. To dowód na to, jak wielka moc drzemie w standaryzacji kodu za pomocą interfejsów.

Opis wykonania
  • Zdefiniuj interfejs ISprzedawalny z metodą WystawFakture() oraz właściwością Cena.
  • Zdefiniuj interfejs IEksportowalny z metodą SprawdzDokumentyCelne().
  • Stwórz klasę Samochod, która dziedziczy po Pojazd i implementuje oba interfejsy.
  • Stwórz klasę Motocykl, która implementuje tylko ISprzedawalny.
  • Zademonstruj implementację jawną (explicit) jednego z interfejsów (np. IEksportowalny), aby ukryć jego metody przed zwykłym użytkownikiem obiektu.
  • Zimplementuj interfejs IComparable<Pojazd> w klasie bazowej, aby umożliwić sortowanie po cenie.
  • W metodzie Main utwórz listę pojazdów o różnych cenach (np. Audi: 120k, Fiat: 10k, BMW: 60k).
  • Wywołaj lista.Sort() i wypisz wynik – sprawdź, czy kolejność jest poprawna.
  • Użyj rzutowania na interfejs: ISprzedawalny s = myAuto; aby wywołać metodę WystawFakture().
  • Zademonstruj sprawdzenie wspieranych interfejsów za pomocą operatora is.
Kod źródłowy
Program.cs
using System;
using System.Collections.Generic;

namespace LaboratoriumOOP
{
    // INTERFEJSY: Kontrakty zaczynające się od litery 'I'. 
    // Nie mogą zawierać pól (tylko właściwości i metody).
    public interface ISprzedawalny
    {
        double Cena { get; set; }
        void WystawFakture();
    }

    public interface IEksportowalny
    {
        void SprawdzDokumentyCelne();
    }

    // Dziedziczenie po JEDNEJ klasie i WIELU interfejsach
    public class Samochod : Pojazd, ISprzedawalny, IEksportowalny
    {
        public Samochod(string marka, double cena) : base(marka, cena) { }

        // Iplementacja niejawna (Implicit) - dostępna bezpośrednio
        public void WystawFakture()
        {
            Console.WriteLine($"[FINANSE] Wystawiono fakturę VAT na kwotę: {Cena} PLN za samochód {Marka}.");
        }

        // Implementacja jawna (Explicit) - dostępna tylko po rzutowaniu na interfejs
        void IEksportowalny.SprawdzDokumentyCelne()
        {
            Console.WriteLine($"[EKSPORT] {Marka}: Dokumenty celne zweryfikowane pozytywnie.");
        }
    }

    // Klasa bazowa z implementacją sortowania
    public abstract class Pojazd : IComparable<Pojazd>
    {
        public string Marka { get; set; }
        public double Cena { get; set; }

        protected Pojazd(string marka, double cena)
        {
            Marka = marka;
            Cena = cena;
        }

        // Porównywanie obiektów (potrzebne do Sort())
        public int CompareTo(Pojazd other)
        {
            if (other == null) return 1;
            return this.Cena.CompareTo(other.Cena);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("--- ZADANIE 08: Interfejsy i sortowanie ---\n");

            List<Pojazd> magazyn = new List<Pojazd>
            {
                new Samochod("Volvo", 95000),
                new Samochod("Trabant", 5000),
                new Samochod("Dacia", 45000)
            };

            Console.WriteLine("1. Lista przed sortowaniem:");
            magazyn.ForEach(p => Console.WriteLine($"- {p.Marka}: {p.Cena}"));

            // Automatyczne sortowanie dzięki IComparable
            magazyn.Sort();

            Console.WriteLine("\n2. Lista po sortowaniu (od najtańszego):");
            magazyn.ForEach(p => Console.WriteLine($"- {p.Marka}: {p.Cena}"));

            Console.WriteLine("\n3. Obsługa interfejsów:");
            foreach (var p in magazyn)
            {
                if (p is ISprzedawalny sprzedaz)
                {
                    sprzedaz.WystawFakture();
                }

                // Przykład wywołania implementacji jawnej
                if (p is IEksportowalny eksport)
                {
                    eksport.SprawdzDokumentyCelne();
                }
            }

            Console.WriteLine("\nNaciśnij dowolny klawisz...");
            Console.ReadKey();
        }
    }
}
                    
Wynik w konsoli
--- ZADANIE 08: Interfejsy i sortowanie --- 1. Lista przed sortowaniem: - Volvo: 95000 - Trabant: 5000 - Dacia: 45000 2. Lista po sortowaniu (od najtańszego): - Trabant: 5000 - Dacia: 45000 - Volvo: 95000 3. Obsługa interfejsów: [FINANSE] Wystawiono fakturę VAT na kwotę: 5000 PLN za samochód Trabant. [EKSPORT] Trabant: Dokumenty celne zweryfikowane pozytywnie. [FINANSE] Wystawiono fakturę VAT na kwotę: 45000 PLN za samochód Dacia. [EKSPORT] Dacia: Dokumenty celne zweryfikowane pozytywnie. [FINANSE] Wystawiono fakturę VAT na kwotę: 95000 PLN za samochód Volvo. [EKSPORT] Volvo: Dokumenty celne zweryfikowane pozytywnie. Naciśnij dowolny klawisz...