Зображення від 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
.
Чому це краще?
- Простота у використанні: Користувачі не повинні знати, як працює обробка платежів — вони просто викликають метод.
- Безпека: Чутливу логіку приховано і вона не може бути доступна безпосередньо.
Ключове слово 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
, щоб викликати відповідну реалізацію залежно від того, що вибирає користувач.
Чому використовувати абстрактні класи?
- Перевикористання коду: Спільна логіка (така як
paymentDetails()
) визначається один раз в абстрактному класі і перевикористовується підкласами. - Гнучкість: Ви можете додавати нові методи оплати (наприклад, 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());
Як це працює:
- Створення об'єкта:
new CreditCardPayment()
створює новий екземпляр класуCreditCardPayment
. - Пряма передача: Цей об'єкт безпосередньо передається в метод
processTransaction
, пропускаючи оголошення проміжної змінної.
// З використанням змінної
Payment payment1 = new CreditCardPayment();
validatePayment(payment1);
processTransaction(payment1);
// Пряма передача
processTransaction(new CreditCardPayment());
Основні моменти:
- Посилання батьківського класу можуть утримувати об'єкти підкласів: Змінна типу
Payment
може зберігати об'єкт типуCreditCardPayment
абоPayPalPayment
завдяки успадкуванню. - Динамічний виклик методу: Java визначає на етапі виконання, яку реалізацію методу
processPayment
викликати, залежно від реального типу об'єкта. - Гнучкість: Використання посилань на батьківський клас робить ваш код більш гнучким і масштабованим. Ви можете додавати нові типи платежів (наприклад,
UPIPayment
), не змінюючи існуючі методи. - Пряма передача: Пряма передача об'єктів зручна для одноразових випадків, але може знизити читаність коду, якщо використовувати занадто часто.
Опанувавши ці концепції, ви зможете створювати масштабовані, багаторазово використовувані та гнучкі 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