Розділ[16]: Передача об’єктів класів як параметрів у Java: приклад платіжної системи

pic

Зображення від Freepik

Отже, давайте застосуємо все, що ми навчилися, на практиці! Як щодо створення онлайн-системи платежів, щоб дійсно зрозуміти абстракцію? Це допоможе нам побачити, як усе працює разом у реальному сценарії.

Приклад коду: Онлайн-система платежів

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

class PaymentProcessor {  
 void process(double amount) {  
 System.out.println("Підключення до банку...");  
 System.out.println("Шифрування платіжних даних...");  
 System.out.println("Обробка платежу на суму $" + amount);  
 System.out.println("Платіж успішно завершено!");  
 }  
}  

class PaymentService {  
 private PaymentProcessor processor = new PaymentProcessor(); // Приховуємо логіку  
 void makePayment(double amount) {  
 System.out.println("Ініціація платежу...");  
 processor.process(amount); // Приховуємо внутрішні деталі  
 }  
}  
public class Main {  
 public static void main(String[] args) {  
 // Користувач взаємодіє з PaymentService, а не з логікою бекенду  
 PaymentService service = new PaymentService();  
 service.makePayment(100.50); // Просто і чисто  
 }  
}

Що відбувається?

  • Клас 1 (PaymentProcessor): Містить усі складні деталі, такі як підключення до банку, шифрування даних і обробка платежів.
  • Клас 2 (PaymentService): Служить мостом між користувачем і бекенд-логікою. Користувач не взаємодіє безпосередньо з процесором; він просто викликає метод makePayment.

Чому це краще?

  1. Простота у використанні: Користувачі не повинні знати, як працює обробка платежів — вони просто викликають метод.
  2. Безпека: Чутливу логіку приховано і вона не може бути доступна безпосередньо.

Ключове слово abstract

Тепер давайте підвищимо рівень. Ключове слово abstract дозволяє визначити шаблон для класу. Подумайте про це як про таке: "Ось що кожен платіжний метод має робити, але я не скажу тобі, як це робити."

abstract class Payment {  
 // Абстрактний метод: Підкласи повинні реалізувати це  
 abstract void processPayment();  

// Звичайний метод: Спільна функціональність для всіх типів платежів  
 void paymentDetails() {  
 System.out.println("Обробка платежу безпечно...");  
 }  
}  


class CreditCardPayment extends Payment {  
 @Override  
 void processPayment(double amount) {  
 paymentDetails(); // Перевикористовуємо спільну логіку  
 System.out.println("Обробка платежу карткою на суму $" + amount);  
 }  
}  


class PayPalPayment extends Payment {  
 @Override  
 void processPayment(double amount) {  
 paymentDetails(); // Перевикористовуємо спільну логіку  
 System.out.println("Обробка PayPal платежу на суму $" + amount);  
 }  
}  


class PaymentService {  
 private Payment payment; // Триматиме посилання на абстрактний клас Payment  
 PaymentService(Payment payment) {  
 this.payment = payment; // Користувач вибирає тип платежу  
 }  
 void makePayment(double amount) {  
 payment.processPayment(amount); // Делегує обробку конкретному типу платежу  
 }  
}  


public class Main {  
 public static void main(String[] args) {  
 PaymentService service;  
 // Використовуємо оплату карткою  
 service = new PaymentService(new CreditCardPayment());  
 service.makePayment(150.75);  
 // Перехід на оплату через PayPal  
 service = new PaymentService(new PayPalPayment());  
 service.makePayment(200.50);  
 }  
}

Що відбувається?

  • Клас 1 (Payment): Це абстрактний клас. Він визначає шаблон, якому має відповідати кожен тип платежу (через метод processPayment()). Також надає спільну функціональність, таку як paymentDetails().
  • Клас 2 і Клас 3 (CreditCardPayment і PayPalPayment): Ці підкласи реалізують специфічну логіку для обробки платежів, використовуючи спільну функціональність.
  • Клас 4 (PaymentService): Цей клас взаємодіє з користувачем. Він використовує посилання на Payment, щоб викликати відповідну реалізацію залежно від того, що вибирає користувач.

Чому використовувати абстрактні класи?

  1. Перевикористання коду: Спільна логіка (така як paymentDetails()) визначається один раз в абстрактному класі і перевикористовується підкласами.
  2. Гнучкість: Ви можете додавати нові методи оплати (наприклад, Google Pay), створюючи ще один підклас без зміни існуючого коду.

Обмеження абстрактних класів

  • Тільки одиночне успадкування: Клас може наслідувати лише один абстрактний клас. Якщо вам потрібно успадковувати кілька поведінок, вам потрібні інтерфейси (це ми розглянемо в частині 3).
  • Часткова абстракція: Абстрактні класи можуть мати як абстрактні, так і не абстрактні методи, тому вони можуть не повністю забезпечувати абстракцію.

Підсумок

  • Абстрактний клас (Payment): Надає спільну структуру для всіх типів платежів.
  • Успадкування: CreditCardPayment і PayPalPayment успадковують від Payment і надають специфічні реалізації для методу processPayment().
  • Поліморфізм: PaymentService працює з типом Payment, тому ви можете передавати йому будь-який підклас Payment (наприклад, CreditCardPayment або PayPalPayment), що робить його гнучким і зручним для повторного використання.

Це повинно дати вам чітке уявлення про те, як працює абстракція в реальних прикладах! Повідомте мене, якщо у вас є питання або вам потрібні подальші спрощення. 😊

Ви можете знайти та завантажити код з тут

.

.

.

Щасливого кодування!
CreditCardPayment є підтипом Payment, тому його можна зберігати в змінній типу Payment.

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

Передача об'єктів як параметрів

Давайте передамо ці об'єкти в метод:

public class Main {  
 public static void main(String[] args) {  
 Payment payment1 = new CreditCardPayment();  
 Payment payment2 = new PayPalPayment();  

 processTransaction(payment1); // Передаємо об'єкт `CreditCardPayment`  
 processTransaction(payment2); // Передаємо об'єкт `PayPalPayment`  
 }  
 static void processTransaction(Payment payment) {  
 payment.processPayment(); // Динамічний виклик методу  
}

Що відбувається тут?

1: Створення об'єктів:

  • payment1 — це об'єкт CreditCardPayment, збережений у змінній типу Payment.
  • payment2 — це об'єкт PayPalPayment, збережений у змінній типу Payment.

2: Передача об'єктів у метод processTransaction:

  • Коли викликається processTransaction(payment1), передається об'єкт CreditCardPayment.
  • Коли викликається processTransaction(payment2), передається об'єкт PayPalPayment.

3: Динамічний виклик методу: В середині processTransaction викликається метод processPayment() для об'єкта типу Payment. На етапі виконання Java визначає, яку реалізацію викликати, залежно від реального типу об'єкта.

Обробка платежу карткою...  
Обробка платежу через PayPal...

Пропуск змінної: Передача об'єктів безпосередньо

Замість того щоб зберігати об'єкт у змінній, можна передати його безпосередньо в метод:

processTransaction(new CreditCardPayment());  
processTransaction(new PayPalPayment());

Як це працює:

  1. Створення об'єкта: new CreditCardPayment() створює новий екземпляр класу CreditCardPayment.
  2. Пряма передача: Цей об'єкт безпосередньо передається в метод processTransaction, пропускаючи оголошення проміжної змінної.
// З використанням змінної  
Payment payment1 = new CreditCardPayment();  
validatePayment(payment1);  
processTransaction(payment1);  
// Пряма передача  
processTransaction(new CreditCardPayment());

Основні моменти:

  1. Посилання батьківського класу можуть утримувати об'єкти підкласів: Змінна типу Payment може зберігати об'єкт типу CreditCardPayment або PayPalPayment завдяки успадкуванню.
  2. Динамічний виклик методу: Java визначає на етапі виконання, яку реалізацію методу processPayment викликати, залежно від реального типу об'єкта.
  3. Гнучкість: Використання посилань на батьківський клас робить ваш код більш гнучким і масштабованим. Ви можете додавати нові типи платежів (наприклад, UPIPayment), не змінюючи існуючі методи.
  4. Пряма передача: Пряма передача об'єктів зручна для одноразових випадків, але може знизити читаність коду, якщо використовувати занадто часто.

Опанувавши ці концепції, ви зможете створювати масштабовані, багаторазово використовувані та гнучкі Java-додатки — як потужну платіжну систему! 🚀

Оволодіння композицією

Як ефективно використовувати відносини IS-A (успадкування) та HAS-A (композиція) в Java разом з абстракцією.
В кінці ви отримаєте чітке розуміння через практичні приклади, поступово переходячи від основ до більш складних реалізацій (у наступному розділі).

Основи відносин IS-A та HAS-A

Перед тим як заглиблюватися в абстракцію, давайте прояснимо два основоположні концепти:

1: Відношення IS-A (успадкування):

  • Означає, що один клас є типом іншого класу.
  • Досягається через extends в Java.

2: Відношення HAS-A (композиція):

  • Показує, що один клас містить інший клас як частину своєї структури.
  • Досягається шляхом включення класу як змінної-члена.

Ось простий приклад з Car та Engine:

==============================================================================  
// Відношення IS-A: Car є типом Vehicle  
class Vehicle {  
 void start() {  
 System.out.println("Транспортний засіб запускається...");  
 }  
}  
class Car extends Vehicle {  
 void drive() {  
 System.out.println("Автомобіль рухається...");  
 }  
}  

==============================================================================  
// Відношення HAS-A: Car має Engine  
class Engine {  
 void run() {  
 System.out.println("Двигун працює...");  
 }  
}  
class CarWithEngine {  
 private Engine engine; // Автомобіль має двигун  
 public CarWithEngine(Engine engine) {  
 this.engine = engine;  
 }  
 void startCar() {  
 System.out.println("Автомобіль запускається...");  
 engine.run(); // Делегуємо завдання двигуну  
 }  
}  

==============================================================================  
// Основний клас для тестування відносин  
public class CarEngineExample {  
 public static void main(String[] args) {  

 // Демонстрація відношення IS-A  
 Car myCar = new Car();  
 myCar.start(); // Спадковано від Vehicle  
 myCar.drive(); // Специфічно для Car  

 System.out.println(); // Роздільник  

 // Демонстрація відношення HAS-A  
 Engine carEngine = new Engine();  
 CarWithEngine carWithEngine = new CarWithEngine(carEngine);  
 carWithEngine.startCar(); // Використовуємо функціональність двигуна  

 }  
}  
==============================================================================

Вихід:

Транспортний засіб запускається...  
Автомобіль рухається...  

Автомобіль запускається...  
Двигун працює...

Комбінуючи ці відносини з абстракцією, ви можете створювати гнучкі, масштабовані та підтримувані Java додатки. Дайте знати, якщо хочете заглибитися в це більше!

Ви можете знайти та завантажити код з тут

.

.

.

Успіхів у програмуванні!

Перекладено з: Chapter[16]: Passing Class Objects as Parameters in Java: Payment System Example

Leave a Reply

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