TypeScript: Припиніть використовувати гармату для вбивства комара 🚫

Уявіть, що ви проектуєте флоту космічних кораблів для міжгалактичної місії. Кожен корабель має унікальні характеристики, такі як розмір, колір і тип двигуна, але всі вони мають спільні риси, такі як здатність запускатись, навігувати і докувати.

У програмуванні Об'єктно-орієнтоване програмування (OOP) дозволяє організовувати код подібним чином — групуючи пов'язані властивості та поведінки в об'єкти, точно так, як і проектуючи кожен космічний корабель.

Чотири основні принципи OOP для космічних кораблів

Чотири основні принципи об'єктно-орієнтованого програмування (OOP) це Інкапсуляція, Наслідування, Поліморфізм та Абстракція. Ці принципи працюють разом, щоб сприяти створенню модульного, багаторазового та підтримуваного коду. Ось розбір кожного з них:

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

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

Інкапсуляція схожа на об'єднання систем управління космічного корабля і його двигунів в один самодостатній блок. Усі важливі деталі залишаються всередині корабля, і ви взаємодієте з ним через панель управління.

class SpaceShip {  
  // Приватна властивість (доступна тільки в межах класу)  
  #fuelLevel;  

  constructor(name, fuelLevel) {  
    this.name = name; // Публічна властивість  
    this.#fuelLevel = fuelLevel; // Приватна властивість  
  }  

  // Публічний метод для взаємодії з приватною властивістю  
  launch() {  
    if (this.#fuelLevel > 0) {  
      console.log(`${this.name} is launching!`);  
      this.#fuelLevel -= 10; // Зменшуємо рівень пального  
    } else {  
      console.log(`${this.name} doesn't have enough fuel to launch.`);  
    }  
  }  

  // Публічний метод для перевірки рівня пального  
  getFuelLevel() {  
    return `${this.name} has ${this.#fuelLevel} units of fuel left.`;  
  }  
}  

// Приклад використання:  
const falcon = new SpaceShip("Falcon", 100);  
falcon.launch(); // Falcon is launching!  
console.log(falcon.getFuelLevel()); // Falcon has 90 units of fuel left.

2. Наслідування

Наслідування дозволяє створювати новий клас, який успадковує властивості і поведінки від існуючого класу.

Наслідування дозволяє нам використовувати шаблони для різних типів космічних кораблів. Наприклад, CargoShip і PassengerShip можуть успадковувати спільні риси від класу SpaceShip, але мати свої унікальні характеристики.

// Батьківський клас  
class SpaceShip {  
  constructor(name, speed) {  
    this.name = name;  
    this.speed = speed;  
  }  

  navigate() {  
    console.log(`${this.name} is navigating at ${this.speed} km/s.`);  
  }  
}  

// Дочірній клас: CargoShip  
class CargoShip extends SpaceShip {  
  constructor(name, speed, currentCargo) {  
    super(name, speed); // Викликаємо конструктор батьківського класу  
    this.currentCargo = currentCargo; // Поточна вага вантажу  
  }  

  loadCargo(weight) {  
    this.currentCargo += weight  
    console.log(`${this.name} loaded ${weight} tons of cargo. Total cargo: ${this.currentCargo} tons.`);  
  }  
}  

// Дочірній клас: PassengerShip  
class PassengerShip extends SpaceShip {  
  constructor(name, speed, passengerCount) {  
    super(name, speed); // Викликаємо конструктор батьківського класу  
    this.passengerCount = passengerCount;  
  }  

  boardPassengers(count) {  
    this.passengerCount += count;  
    console.log(  
      `${this.name} is boarding ${count} passengers. Total passengers: ${this.passengerCount}.`  
    );  
  }  
}  

// Приклад використання:  
const cargoShip = new CargoShip("Galactic Hauler", 10, 500);  
cargoShip.navigate(); // Galactic Hauler is navigating at 10 km/s.  
cargoShip.loadCargo(200); // Galactic Hauler loaded 200 tons of cargo. Total cargo: 700 tons.  
cargoShip.loadCargo(350); // Galactic Hauler loaded 350 tons of cargo. Total cargo: 1050 tons.  

const passengerShip = new PassengerShip("Stellar Voyager", 15, 50);  
passengerShip.navigate(); // Stellar Voyager is navigating at 15 km/s.  
passengerShip.boardPassengers(20); // Stellar Voyager is boarding 20 passengers.

3. Поліморфізм

Поліморфізм дозволяє об'єктам приймати різні форми, даючи можливість одному інтерфейсу представляти різні підлягаючі поведінки.

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

class SpaceShip {  
  constructor(name) {  
    this.name = name;  
  }  

  navigate() {  
    console.log(`${this.name} is navigating through space.`);  
  }  
}  

class AutopilotShip extends SpaceShip {  
  navigate() {  
    console.log(`${this.name} is navigating using autopilot.`);  
  }  
}  

class ManualControlShip extends SpaceShip {  
  navigate() {  
    console.log(`${this.name} requires manual controls to navigate.`);  
  }  
}  

class AIControlledShip extends SpaceShip {  
  navigate() {  
    console.log(`${this.name}'s AI is calculating the safest route.`);  
  }  
}  

const genericShip = new SpaceShip("Generic Cruiser");  
const autopilotShip = new AutopilotShip("Auto Pilot One");  
const manualShip = new ManualControlShip("Manual Voyager");  
const aiShip = new AIControlledShip("AI Explorer");  

const ships = [genericShip, autopilotShip, manualShip, aiShip];  
ships.forEach((ship) => ship.navigate());  

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

Абстракція приховує складні деталі реалізації і надає тільки необхідні функції.

Абстракція приховує складні механізми роботи двигунів космічного корабля. Вам не потрібно знати кожну деталь — ви просто натискаєте кнопку для запуску. Подібно до цього, OOP приховує внутрішні роботи об'єкта, дозволяючи вам взаємодіяти з ним простим способом.

class SpaceShip {  
  constructor(name) {  
    this.name = name;  
  }  

  #calculateThrust() {  
    return "Calculating optimal thrust...";  
  }  

  launch() {  
    console.log(`${this.name} is launching.`);  
    console.log(this.#calculateThrust()); // Приховуємо складність  
  }  
}  

const voyager = new SpaceShip("Voyager");  
voyager.launch();   

Висновок

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

Інкапсуляція захищає цілісність даних, наслідування сприяє повторному використанню коду та логічним ієрархіям, поліморфізм дозволяє гнучким і динамічним поведінкам, а абстракція спрощує складність, зосереджуючи увагу на основних функціях. Всі ці принципи дозволяють розробникам інтуїтивно моделювати складні системи, розбиваючи їх на керовані, самодостатні об'єкти, які співпрацюють для досягнення функціональності.

Зрештою, освоєння OOP — це не тільки вивчення синтаксису, але й прийняття менталітету, який ставить на перше місце чисту архітектуру, співпрацю та підготовку до майбутнього. Використовуючи його принципи, розробники можуть створювати надійне, адаптоване програмне забезпечення, яке еволюціонує з розвитком вимог, забезпечуючи довговічність і ефективність у змінному технологічному середовищі.
🖥️👕
Результат: Добре структурований, багаторазовий та легкий у підтримці код.

Приклад 4: Впровадження залежностей (Dependency Injection) 🛠️

Інтерфейси також є чудовим інструментом для впровадження залежностей (Dependency Injection), що робить ваше програмне забезпечення більш гнучким та масштабованим.

interface Logger {  
 log(message: string): void;  
}
class ConsoleLogger implements Logger {  
 log(message: string): void {  
 console.log(`Console: ${message}`);  
 }  
}  
class FileLogger implements Logger {  
 log(message: string): void {  
 console.log(`File: ${message}`); // Тут ви б записали в файл замість цього  
 }  
}  
class App {  
 private logger: Logger;  
 constructor(logger: Logger) {  
 this.logger = logger;  
 }  
 run() {  
 this.logger.log("Application is running!");  
 }  
}  
const appWithConsoleLogger = new App(new ConsoleLogger());  
appWithConsoleLogger.run();  
// Вивід: Console: Application is running!  
const appWithFileLogger = new App(new FileLogger());  
appWithFileLogger.run();  
// Вивід: File: Application is running!

Тут інтерфейс Logger забезпечує гнучкість, дозволяючи використовувати різні стратегії логування (консоль vs файл) без зміни основної логіки App.

Інтерфейс vs. Тип: Коли використовувати що

pic

Остаточний вердикт 🎯

Минають ті часи, коли інтерфейси використовувалися для всього в TypeScript.

  • Використовуйте інтерфейси тільки для задач, орієнтованих на OOP (Object-Oriented Programming).
  • Використовуйте типи (type) для всього іншого.

Наступного разу, коли ви збираєтеся використовувати інтерфейс, зупиніться і подумайте:
"Чи не використовую я гармату для вбивства комара?" 😄

Перекладено з: TypeScript: Stop Using a Cannon to Kill a Mosquito 🚫

Leave a Reply

Your email address will not be published. Required fields are marked *