Oop

Стовпи ООП

[object Object]

Стовпи ООП

Вступ: Чому ООП?

Уявіть, що ви пишете програму для управління автопарком. У структурному програмуванні ви б створили безліч змінних та функцій:

// Структурний підхід - важко підтримувати
string car1Brand = "Toyota";
int car1Year = 2020;
decimal car1Price = 25000;

string car2Brand = "Honda";
int car2Year = 2021;
decimal car2Price = 28000;

void PrintCarInfo(string brand, int year, decimal price)
{
    Console.WriteLine($"{brand} ({year}): ${price}");
}

Проблеми очевидні: дублювання коду, важко масштабувати, відсутність зв'язку між даними та поведінкою. Об'єктно-орієнтоване Програмування (ООП) вирішує ці проблеми через чотири фундаментальні принципи.

Об'єктно-орієнтоване Програмування (Object-Oriented Programming, OOP) — це парадигма програмування, яка організує код навколо об'єктів (комбінація даних та поведінки) замість функцій та логіки.

Чотири Стовпи ООП

Loading diagram...
graph TD
    OOP[Об'єктно-Орієнтоване<br/>Програмування]

    OOP --> E[Encapsulation<br/>Інкапсуляція]
    OOP --> I[Inheritance<br/>Успадкування]
    OOP --> P[Polymorphism<br/>Поліморфізм]
    OOP --> A[Abstraction<br/>Абстракція]

    E --> E1[Приховування<br/>деталей реалізації]
    I --> I1[Повторне<br/>використання коду]
    P --> P1[Гнучкість<br/>поведінки]
    A --> A1[Спрощення<br/>складності]

    style OOP fill:#4CAF50
    style E fill:#2196F3
    style I fill:#FF9800
    style P fill:#9C27B0
    style A fill:#F44336
ПринципАнглійськаМетаКлючові слова
ІнкапсуляціяEncapsulationПриховування внутрішньої реалізаціїprivate, public, protected
УспадкуванняInheritanceСтворення нових класів на основі існуючих:, base
ПоліморфізмPolymorphismРізна поведінка для схожих об'єктівvirtual, override, new
АбстракціяAbstractionВизначення контракту без деталейabstract, interface

1. Encapsulation (Інкапсуляція)

Визначення

Encapsulation (Інкапсуляція) — це принцип приховування внутрішньої реалізації об'єкта та надання контрольованого доступу до його стану через публічний інтерфейс.

Аналогія: Думайте про банкомат. Ви не бачите внутрішню механіку (як він рахує гроші, перевіряє баланс), але маєте зручний інтерфейс (кнопки, екран) для взаємодії.

Модифікатори Доступу

C# надає різні рівні доступу для контролю видимості членів класу:

МодифікаторДоступ всередині класуУ похідних класахУ тій самій збірціУ інших збірках
public
private
protected
internal
protected internal✅ (тільки похідні)
private protected✅ (у тій самій збірці)
file (C# 11+)✅ (тільки в файлі)

Приклад: Інкапсуляція в BankAccount

public class BankAccount
{
    public decimal Balance; // Публічне поле - небезпечно!
}

var account = new BankAccount();
account.Balance = -1000; // Ніхто не заважає зламати логіку!
account.Balance = 999999999; // Шахрайство легко

Best Practices Інкапсуляції

Поля завжди приватні: Використовуйте private для полів
Властивості для доступу: Надавайте контрольований доступ через properties
Валідація: Перевіряйте дані перед зміною стану
Мінімальний публічний інтерфейс: Робіть публічним тільки те, що необхідно
Tell, Don't Ask: Об'єкт сам керує своїм станом

Інкапсуляція є ключовим механізмом для підтримки інваріантів класу — умов, які завжди мають бути істинними для об'єкта. Наприклад, для банківського рахунку інваріантом може бути Balance >= 0. Завдяки приховуванню поля _balance та наданню контрольованих методів Deposit та Withdraw, ми гарантуємо, що жоден зовнішній код не зможе порушити цей інваріант, встановивши від'ємний баланс напряму.

🎓 Академічний Погляд: Інкапсуляція vs Приховування Даних

В академічній літературі часто розрізняють два поняття, які в C# часто зливаються:

  1. Інкапсуляція (Encapsulation): Це механізм об'єднання даних (полів) та методів, що працюють з цими даними, в єдиний компонент (клас).
  2. Приховування інформації (Information Hiding): Це принцип проектування, за яким внутрішні деталі реалізації компонента недоступні для інших компонентів.

"Інкапсуляція — це техніка для реалізації приховування інформації."

Інваріанти Класу

Важливим аспектом інкапсуляції є підтримка інваріантів — умов, які завжди мають бути істинними для об'єкта протягом його життя. Наприклад, для BankAccount інваріантом є Balance >= 0 (якщо ми не дозволяємо овердрафт). Інкапсуляція гарантує, що жоден зовнішній код не зможе порушити цей інваріант, встановивши баланс напряму.

2. Inheritance (Успадкування)

Визначення

Inheritance (Успадкування) — це механізм створення нових класів (похідних, derived) на основі існуючих (базових, base), дозволяючи повторно використовувати код та створювати ієрархії типів.

Успадкування моделює відношення "IS-A" (є). Наприклад, Dog є Animal. Це означає, що похідний клас має успадковувати всі характеристики та поведінку базового класу, а не просто повторно використовувати його код. Правильно побудована ієрархія успадкування є запорукою дотримання Принципу Підстановки Лісков (LSP), який стверджує, що об'єкти похідного класу повинні мати можливість заміщувати об'єкти базового класу без зміни коректності програми.

Синтаксис Успадкування

// Базовий клас (Base class, Parent class, Superclass)
public class Animal
{
    public string Name { get; set; }

    public void Eat()
    {
        Console.WriteLine($"{Name} їсть");
    }
}

// Похідний клас (Derived class, Child class, Subclass)
public class Dog : Animal  // Наслідує Animal
{
    public void Bark()
    {
        Console.WriteLine($"{Name} гавкає: Гав-гав!");
    }
}

// Використання
var dog = new Dog { Name = "Рекс" };
dog.Eat();  // Успадкований метод
dog.Bark(); // Власний метод

Ключове Слово base

Ключове слово base використовується для:

  1. Виклику конструкторів базового класу
  2. Доступу до членів базового класу (методів, властивостей)

1. Виклик Конструкторів

public class Animal
{
    public string Name { get; }
    public int Age { get; }

    public Animal(string name, int age)
    {
        Name = name;
        Age = age;
        Console.WriteLine($"Animal конструктор: {name}");
    }
}

public class Dog : Animal
{
    public string Breed { get; }

    // Конструктор похідного класу викликає базовий через :base()
    public Dog(string name, int age, string breed) : base(name, age)
    {
        Breed = breed;
        Console.WriteLine($"Dog конструктор: {breed}");
    }
}

// Порядок виклику:
var dog = new Dog("Рекс", 3, "Німецька вівчарка");
// Вивід:
// Animal конструктор: Рекс
// Dog конструктор: Німецька вівчарка
Порядок ініціалізації: Завжди спочатку виконується конструктор базового класу, потім похідного. Це гарантує, що базова частина об'єкта ініціалізована перед похідною.

2. Доступ до Членів Базового Класу

public class Animal
{
    public virtual void MakeSound()
    {
        Console.WriteLine("Тварина видає звук");
    }
}

public class Dog : Animal
{
    public override void MakeSound()
    {
        base.MakeSound(); // Виклик методу базового класу
        Console.WriteLine("Гав-гав!");
    }
}

var dog = new Dog();
dog.MakeSound();
// Вивід:
// Тварина видає звук
// Гав-гав!

Ієрархія Успадкування

Loading diagram...
classDiagram
    class Animal {
        +string Name
        +int Age
        +Eat()
        +Sleep()
    }

    class Mammal {
        +int NumberOfLegs
        +GiveBirth()
    }

    class Dog {
        +string Breed
        +Bark()
    }

    class Cat {
        +bool IsIndoor
        +Meow()
    }

    class Bulldog {
        +bool IsAggressive
        +Guard()
    }

    Animal <|-- Mammal
    Mammal <|-- Dog
    Mammal <|-- Cat
    Dog <|-- Bulldog

    note for Animal "Базовий клас<br/>Найзагальніші характеристики"
    note for Dog "Проміжний клас<br/>Спеціалізація для собак"
    note for Bulldog "Конкретна реалізація<br/>Специфічна порода"

Обмеження Успадкування

Важливо: C# підтримує тільки одиночне успадкування класів (клас може мати лише один базовий клас), але множинну реалізацію інтерфейсів.
// ❌ Це НЕ працює - множинне успадкування класів заборонено
public class FlyingFish : Fish, Bird  // ПОМИЛКА КОМПІЛЯЦІЇ
{
}

// ✅ Це працює - один базовий клас + множинні інтерфейси
public class FlyingFish : Fish, IFlyable, ISwimmable
{
}

🎓 Принцип Підстановки Лісков (LSP)

З точки зору теорії типів, успадкування створює відношення IS-A. Це формалізується Принципом Підстановки Барбари Лісков (Liskov Substitution Principle):

"Об'єкти в програмі повинні бути замінними на екземпляри їх підтипів без зміни правильності виконання програми."

Проблема Квадрата і Прямокутника

Класичний приклад порушення LSP при неправильному використанні успадкування:

// Якщо Square успадковує Rectangle
public class Rectangle { public virtual int Width { get; set; } public virtual int Height { get; set; } }
public class Square : Rectangle
{
    public override int Width { set { base.Width = value; base.Height = value; } }
    public override int Height { set { base.Width = value; base.Height = value; } }
}

// Це ламає логіку для коду, який очікує Rectangle:
Rectangle r = new Square();
r.Width = 5;
r.Height = 10;
// Для прямокутника площа має бути 50, але для квадрата вона стане 100 (бо Height=10 встановить і Width=10).

Це демонструє, чому математичне відношення "квадрат є прямокутником" не завжди працює в ООП.

Композиція замість Успадкування

В сучасній розробці часто надають перевагу композиції ("HAS-A") над успадкуванням ("IS-A") для повторного використання коду, щоб уникнути крихкості базового класу та проблем з LSP.

3. Polymorphism (Поліморфізм)

Визначення

Polymorphism (Поліморфізм) — це здатність об'єктів різних типів реагувати на ті самі повідомлення (виклики методів) по-різному. Буквально означає "багато форм" (poly = багато, morph = форма).

Цей принцип дозволяє писати більш гнучкий та узагальнений код. Замість того, щоб писати окремий код для кожного конкретного типу, ви можете працювати з об'єктами через їхній базовий тип або інтерфейс, покладаючись на те, що кожен об'єкт "знає", як правильно виконати ту чи іншу дію. Це досягається за допомогою механізмів статичної (ранньої) та динамічної (пізньої) диспетчеризації.

Аналогія: Команда "Видай звук" для різних тварин дасть різні результати: собака гавкає, кіт нявкає, корова мукає — це і є поліморфізм.

Види Поліморфізму

ВидЧас визначенняМеханізмПриклад
Compile-timeПід час компіляціїMethod overloadingРізні параметри методу
RuntimeПід час виконанняMethod overridingvirtual / override

🎓 Класифікація Поліморфізму

В теорії мов програмування (Cardelli & Wegner) виділяють кілька видів поліморфізму:

  1. Ad-hoc Polymorphism: Функція поводиться по-різному для обмеженого набору типів.
    • В C#: Method Overloading (перевантаження методів), Operator Overloading.
  2. Parametric Polymorphism: Код виконується однаково для будь-якого типу.
    • В C#: Generics (List<T>).
  3. Subtype Polymorphism (Inclusion Polymorphism): Код працює з об'єктом через інтерфейс його базового типу.
    • В C#: Inheritance + Virtual Methods.

Static vs Dynamic Dispatch

  • Static Dispatch (Early Binding): Рішення про те, який метод викликати, приймається під час компіляції (Overloading).
  • Dynamic Dispatch (Late Binding): Рішення приймається під час виконання на основі реального типу об'єкта (Overriding).

Runtime Polymorphism: Virtual та Override

Ключові Слова

  • virtual: Позначає метод у базовому класі, який може бути перевизначений
  • override: Перевизначає віртуальний метод у похідному класі
public class Animal
{
    // Virtual метод - може бути перевизначений
    public virtual void MakeSound()
    {
        Console.WriteLine("Тварина видає звук");
    }
}

public class Dog : Animal
{
    // Override - перевизначення віртуального методу
    public override void MakeSound()
    {
        Console.WriteLine("Гав-гав!");
    }
}

public class Cat : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("Мяу!");
    }
}

// Поліморфна поведінка
Animal animal1 = new Dog();
Animal animal2 = new Cat();

animal1.MakeSound(); // Гав-гав! (викликається Dog.MakeSound)
animal2.MakeSound(); // Мяу! (викликається Cat.MakeSound)

Як працює Virtual Method Table (VTable)

Loading diagram...
sequenceDiagram
    participant Client as Клієнт
    participant Ref as Animal ref
    participant VTable as Virtual Table
    participant Impl as Dog.MakeSound()

    Client->>Ref: animal.MakeSound()
    Note over Ref: Тип рантайму: Dog
    Ref->>VTable: Пошук методу MakeSound<br/>у VTable для Dog
    VTable->>Impl: Знайдено override
    Impl-->>Client: "Гав-гав!"

    Note over VTable: VTable зберігає покажчики<br/>на фактичні реалізації<br/>віртуальних методів

Method Hiding з new

Ключове слово new приховує метод базового класу, а не перевизначає його.

public class Animal
{
    public virtual void MakeSound()
    {
        Console.WriteLine("Animal звук");
    }
}

public class DogOverride : Animal
{
    public override void MakeSound()  // Перевизначення
    {
        Console.WriteLine("Гав-гав!");
    }
}

public class DogNew : Animal
{
    public new void MakeSound()  // Приховування
    {
        Console.WriteLine("Гав-гав!");
    }
}
Важливо: Використовуйте override для поліморфної поведінки, а new тільки коли свідомо хочете приховати метод базового класу та запобігти поліморфізму.

Sealed Classes та Methods

Sealed Class

sealed class — клас, який не може бути успадкований.

public sealed class FinalClass
{
    public void DoSomething() { }
}

// ❌ ПОМИЛКА КОМПІЛЯЦІЇ
public class Derived : FinalClass  // CS0509: cannot derive from sealed type
{
}

Коли використовувати sealed class:

  • Коли успадкування може порушити логіку класу
  • Для оптимізації (компілятор може виконати devirtualization)
  • Для безпеки (запобігання небажаному розширенню)

Sealed Override

sealed override — метод, який перевизначає віртуальний метод, але не може бути перевизначений далі.

public class A
{
    public virtual void DoWork()
    {
        Console.WriteLine("A.DoWork");
    }
}

public class B : A
{
    public override void DoWork()
    {
        Console.WriteLine("B.DoWork");
    }
}

public class C : B
{
    // Sealed override - зупиняє ланцюжок перевизначення
    public sealed override void DoWork()
    {
        Console.WriteLine("C.DoWork");
    }
}

public class D : C
{
    // ❌ ПОМИЛКА - не можна override sealed метод
    // public override void DoWork() { }
}

Порівняльна Таблиця

KeywordПризначенняДе використовуєтьсяПоліморфізм
virtualДозволяє перевизначенняБазовий клас
overrideПеревизначає віртуальний методПохідний клас
newПриховує метод базового класуПохідний клас
sealed overrideПеревизначає і зупиняє подальше overrideПохідний клас✅ (до цього рівня)
sealed classЗабороняє успадкуванняОголошення класуN/A

4. Abstraction (Абстракція)

Визначення

Abstraction (Абстракція) — це принцип визначення контракту (що має робити об'єкт) без деталей реалізації (як саме він це робить). Абстракція дозволяє приховати складність та зосередитися на суттєвих характеристиках.

Абстракція є фундаментальним інструментом мислення, який ми використовуємо щодня. Коли ви керуєте автомобілем, ви взаємодієте з простою абстракцією (кермо, педалі), ігноруючи складну внутрішню роботу двигуна. В програмуванні абстракція працює так само: вона дозволяє нам створювати компоненти, які легко використовувати, не занурюючись у деталі їх реалізації. abstract class та interface є лише інструментами мови C# для втілення цього принципу.

Abstract Classes

Abstract class (Абстрактний клас) — клас, який не може бути інстанційований і може містити abstract методи (без реалізації).

public abstract class Shape
{
    // Звичайна властивість
    public string Color { get; set; }

    // Abstract метод - БЕЗ реалізації
    public abstract double CalculateArea();

    // Abstract властивість
    public abstract double Perimeter { get; }

    // Звичайний метод з реалізацією
    public void Display()
    {
        Console.WriteLine($"Фігура кольору {Color} з площею {CalculateArea()}");
    }
}

public class Circle : Shape
{
    public double Radius { get; set; }

    // ОБОВ'ЯЗКОВО override abstract методу
    public override double CalculateArea()
    {
        return Math.PI * Radius * Radius;
    }

    public override double Perimeter => 2 * Math.PI * Radius;
}

// Використання
Shape shape = new Circle { Color = "Червоний", Radius = 5 };
shape.Display(); // Викликається конкретна реалізація CalculateArea()
Важливо: Не можна створити екземпляр abstract класу напряму:
Shape shape = new Shape(); // ❌ ПОМИЛКА КОМПІЛЯЦІЇ

Interfaces

Interface (Інтерфейс) — це контракт, який визначає набір методів та властивостей, які має реалізувати клас.

// Інтерфейс - тільки оголошення
public interface IDrawable
{
    void Draw();
    void Resize(double scale);
}

public interface IColorable
{
    string Color { get; set; }
}

// Клас може реалізовувати МНОЖИННІ інтерфейси
public class Circle : IDrawable, IColorable
{
    public string Color { get; set; }

    // Реалізація IDrawable
    public void Draw()
    {
        Console.WriteLine($"Малюю коло кольору {Color}");
    }

    public void Resize(double scale)
    {
        Console.WriteLine($"Змінюю розмір на {scale}");
    }
}

Default Interface Methods (C# 8+)

public interface ILogger
{
    void Log(string message);

    // Default реалізація в інтерфейсі
    void LogError(string error)
    {
        Log($"ERROR: {error}");
    }
}

public class ConsoleLogger : ILogger
{
    public void Log(string message)
    {
        Console.WriteLine(message);
    }

    // LogError вже є - не обов'язково override
}

Abstract Classes vs Interfaces

// Абстрактний клас - "IS-A" відношення
public abstract class Vehicle
{
    // Може мати СТАН (поля)
    private string _brand;

    public string Brand => _brand;

    // Конструктор
    public Vehicle(string brand)
    {
        _brand = brand;
    }

    // Abstract метод
    public abstract void Start();

    // Звичайний метод
    public void Stop()
    {
        Console.WriteLine($"{Brand} зупинився");
    }
}

public class Car : Vehicle
{
    public Car(string brand) : base(brand) { }

    public override void Start()
    {
        Console.WriteLine($"{Brand} заводиться");
    }
}
КритерійAbstract ClassInterface
Інстанціювання❌ Не можна❌ Не можна
Множинне наслідування❌ Ні (тільки 1)✅ Так (багато)
Поля (стан)✅ Так❌ Ні
Конструктори✅ Так❌ Ні
Методи з реалізацією✅ Так✅ Так (C# 8+, default)
Access modifiers✅ Будь-які❌ Тільки public (за замовчуванням)
Коли використовувати"IS-A" (собака є тварина)"CAN-DO" (собака може бігти)
Loading diagram...
flowchart TD
    Start([Потрібен контракт<br/>для класів?]) --> Q1{Класи мають<br/>спільний код?}

    Q1 -->|Так| Q2{Потрібно<br/>множинне<br/>наслідування?}
    Q1 -->|Ні| Interface[Interface]

    Q2 -->|Так| Both[Abstract Class +<br/>Interfaces]
    Q2 -->|Ні| Abstract[Abstract Class]

    Q3{Відношення} --> ISA[IS-A?]
    Q3 --> CANDO[CAN-DO?]

    ISA --> Abstract
    CANDO --> Interface

    style Abstract fill:#FF9800
    style Interface fill:#2196F3
    style Both fill:#9C27B0

🎓 Абстрактні Типи Даних (ADT)

Абстракція в ООП тісно пов'язана з поняттям Абстрактних Типів Даних. ADT визначається своєю поведінкою (операціями) з точки зору користувача, а не своєю структурою. Клас в C# реалізує ADT, надаючи публічний інтерфейс (операції) та приховуючи представлення даних.

Проблема "Дірявих Абстракцій" (Leaky Abstractions)

Закон Джоела Спольскі стверджує:

"Усі нетривіальні абстракції певною мірою 'протікають'."

Це означає, що деталі реалізації іноді "вилазять" назовні. Наприклад, ітерація по List<T> (масив у пам'яті) швидша за LinkedList<T> (розкидані вузли) через кеш процесора, хоча абстракція IEnumerable<T> приховує цю різницю.

Приклад: Комплексна Ієрархія

Loading diagram...
classDiagram
    class IPayable {
        <<interface>>
        +ProcessPayment(amount)
    }

    class IPrintable {
        <<interface>>
        +Print()
    }

    class Document {
        <<abstract>>
        #string Title
        #DateTime CreatedDate
        +abstract Save()
        +Display()
    }

    class Invoice {
        +decimal TotalAmount
        +Save()
        +ProcessPayment(amount)
        +Print()
    }

    class Report {
        +string Author
        +Save()
        +Print()
    }

    Document <|-- Invoice
    Document <|-- Report
    IPayable <|.. Invoice
    IPrintable <|.. Invoice
    IPrintable <|.. Report

    note for Document "Abstract class<br/>Спільні характеристики<br/>документів"
    note for IPayable "Interface<br/>Можливість оплати"
    note for Invoice "Конкретний клас<br/>Рахунок-фактура"

Зв'язок між Стовпами ООП

Чотири принципи працюють разом для створення гнучких та підтримуваних систем:

// ENCAPSULATION: Приховування деталей
public abstract class BankAccount  // ABSTRACTION: Абстрактний контракт
{
    private decimal _balance;  // ENCAPSULATION: Приватне поле

    public string AccountNumber { get; }

    protected BankAccount(string accountNumber, decimal initialBalance)
    {
        AccountNumber = accountNumber;
        _balance = initialBalance;
    }

    public decimal Balance => _balance;

    // ABSTRACTION: Abstract метод для різних типів рахунків
    public abstract decimal CalculateInterest();

    // POLYMORPHISM: Virtual метод
    public virtual void Deposit(decimal amount)
    {
        if (amount <= 0)
            throw new ArgumentException("Сума має бути додатною");
        _balance += amount;
    }
}

// INHERITANCE: Успадкування від BankAccount
public class SavingsAccount : BankAccount
{
    private decimal _interestRate;

    public SavingsAccount(string accountNumber, decimal initialBalance, decimal interestRate)
        : base(accountNumber, initialBalance)
    {
        _interestRate = interestRate;
    }

    // ABSTRACTION: Реалізація абстрактного методу
    public override decimal CalculateInterest()
    {
        return Balance * _interestRate / 100;
    }

    // POLYMORPHISM: Override методу
    public override void Deposit(decimal amount)
    {
        base.Deposit(amount);  // INHERITANCE: Виклик базового методу
        Console.WriteLine($"Нарахований бонус: {amount * 0.01m:C}");
    }
}

public class CheckingAccount : BankAccount
{
    public CheckingAccount(string accountNumber, decimal initialBalance)
        : base(accountNumber, initialBalance) { }

    public override decimal CalculateInterest() => 0; // Немає відсотків
}

Troubleshooting: Типові Помилки

Помилка 1: Порушення Інкапсуляції

public class User
{
    public string Password; // Публічне поле!
}

var user = new User();
user.Password = "123"; // Прямий доступ - небезпечно

Помилка 2: Забутий override Keyword

public class Animal
{
    public virtual void MakeSound()
    {
        Console.WriteLine("Звук");
    }
}

public class Dog : Animal
{
    // ❌ УВАГА: Компілятор видасть попередження CS0114
    public void MakeSound()  // Забули override!
    {
        Console.WriteLine("Гав");
    }
}

Animal animal = new Dog();
animal.MakeSound(); // "Звук" - викликається Animal, а не Dog!
Рішення: Завжди використовуйте override для перевизначення віртуальних методів:
public override void MakeSound() { }

Помилка 3: Спроба Успадкувати Sealed Class

public sealed class FinalClass
{
    public void DoWork() { }
}

// ❌ ПОМИЛКА CS0509: cannot derive from sealed type 'FinalClass'
public class DerivedClass : FinalClass
{
}

Помилка 4: Неправильний Вибір Abstract vs Interface

// ❌ Неправильно - інтерфейс для IS-A відношення
public interface IVehicle  // Краще abstract class
{
    string Brand { get; }
    void Start();
}

// ✅ Правильно
public abstract class Vehicle  // IS-A: Car IS-A Vehicle
{
    public string Brand { get; protected set; }
    public abstract void Start();
}

public interface IDriveable  // CAN-DO: Car CAN-DO Drive
{
    void Drive();
}

Помилка 5: Abstract Method без Override

public abstract class Shape
{
    public abstract double Area { get; }
}

// ❌ ПОМИЛКА CS0534: 'Circle' does not implement inherited abstract member
public class Circle : Shape
{
    public double Radius { get; set; }
    // Забули override Area!
}

// ✅ Правильно
public class Circle : Shape
{
    public double Radius { get; set; }
    public override double Area => Math.PI * Radius * Radius;
}

Практичні Завдання

Рівень 1: Ієрархія Employee

Завдання 1.1

Створіть ієрархію працівників з використанням інкапсуляції та успадкування:

  1. Базовий abstract клас Employee:
- Приватні поля: `_name`, `_salary`
- Властивості: `Name` (get), `Salary` (get, protected set)
- Abstract метод: `CalculateBonus()`
- Звичайний метод: `DisplayInfo()`

2. Похідні класи: - Manager: бонус = 20% від зарплати - Developer: бонус = 15% від зарплати - Intern: бонус = 5% від зарплати

Рівень 2: Система Транспорту з Поліморфізмом

Завдання 2.1

Створіть систему транспорту, що демонструє всі аспекти поліморфізму:

  1. Abstract клас Vehicle:
- Virtual метод `Start()`
- Virtual метод `Stop()`
- Abstract метод `GetMaxSpeed()`

2. Похідні класи:

- `Car`: override `Start()`, sealed override `Stop()`, реалізує `GetMaxSpeed()`
- `Motorcycle`: override `Start()` і `GetMaxSpeed()`
- `ElectricCar` extends `Car`: спробуйте override `Stop()` (sealed!)

3. Демонструйте різницю між virtual, override, sealed override та new

Рівень 3: Система Платіжних Методів

Завдання 3.1

Створіть систему обробки платежів, що демонструє абстракцію:

  1. Інтерфейс IPaymentMethod:
- `bool ProcessPayment(decimal amount)`
- `string GetPaymentDetails()`

2. Інтерфейс IRefundable:

- `bool Refund(decimal amount)`

3. Abstract клас PaymentMethod:

- Поле `_transactionHistory` (List of transactions)
- Abstract метод `ValidatePayment(decimal amount)`
- Метод `LogTransaction(decimal amount, bool success)`

4. Конкретні класи: - CreditCardPayment: implements IPaymentMethod, IRefundable, extends PaymentMethod - CashPayment: implements IPaymentMethod, extends PaymentMethod (не refundable) - CryptoPayment: implements IPaymentMethod, extends PaymentMethod

Резюме

У цьому розділі ви вивчили чотири фундаментальні принципи ООП:

🔒 Encapsulation (Інкапсуляція): Приховування внутрішньої реалізації через модифікатори доступу (private, protected, public)🔗 Inheritance (Успадкування): Створення нових класів на основі існуючих через : та base keyword🎭 Polymorphism (Поліморфізм): Різна поведінка для схожих об'єктів через virtual, override, new, sealed📐 Abstraction (Абстракція): Визначення контракту без деталей через abstract class та interface

Коли використовувати що:

СитуаціяРішення
Приховати даніEncapsulation (private поля, public properties)
Повторне використання кодуInheritance (базовий клас)
Різна поведінка для схожих типівPolymorphism (virtual/override)
Визначити контракт IS-AAbstraction (abstract class)
Визначити можливість CAN-DOAbstraction (interface)
Запобігти успадкуваннюsealed class
Зупинити override ланцюжокsealed override

Наступні Кроки: У наступному розділі ви дізнаєтеся про Advanced Types: structs, enums, records, tuples, та nullable types, які розширюють можливості системи типів C#.