Слишком много абстрактных функций - ООП Лучшие практики

Сколько абстрактных функций объявление абстрактного класса слишком много?

Например, для платежной системы на основе членства:

Поддерживаются несколько режимов оплаты:

  • Кредитная карта
  • Токен (оплата кредитной картой, но с использованием токена)
  • Редирект (т.е. Paypal)
  • Ручной (админ заряжает вручную пользователя)

У меня есть абстрактный класс PaymentMode, и различные режимы выше распространяются на этот класс.

Каждый режим имеет свою собственную уникальную логику методов ниже, и я должен объявить абстрактные методы в классе PaymentMode для этих

// each mode has own way of validating the customer data
validate();

// own logic of cleaning customer data (e.g removing/adding/updating)
preparePaymentData();

// returns a string for saving in database, subclass must implement so developers plan to extend the PaymentMode abstract will be forced to return the correct value
getModeOfPayment();

// each mode has its own logic when determining payment gateways to attempt
getGatewaysToAttempt();

// before sending the payment to gateway, each mode has its own logic when adding specific data
addCustomDataSpecificForGateway();

// check if transaction has failed, different payment modes has different logic of determining a failed transaction
isTransactionFailed()

Есть 6 уникальных логик для каждого режима, мне уже удалось обобщить общие коды и поместить их в класс PaymentMode.

Это число может возрасти, поскольку мы реализуем новые функции, уникальные для каждого режима.

На мой взгляд, я обеспокоен тем, что если какой-либо будущий разработчик расширит мой класс PaymentMode, он должен реализовать все объявления абстрактных функций.

Значит ли большое количество объявлений абстрактных функций является признаком ПЛОХОГО ДИЗАЙНА? Сколько это слишком много?

Если это плохой дизайн, можете ли вы порекомендовать какие-либо методы или шаблоны проектирования, которые решат эту проблему?

Спасибо

2 ответа

Трудно ответить без подробностей, но:

Очевидно, что нет никаких жестких ограничений на абстрактные методы (методы в интерфейсах или абстрактные классы), хотя меньше всегда яснее и проще для понимания.

Однако субоптимальный дизайн указывает на то, что вам необходимо изменять абстракцию метода оплаты с каждым новым методом оплаты. Это для меня указывает на неудачную абстракцию. ООП - это не только извлечение общего кода, избегая дублирования, но и абстракции.

Я хотел бы как-то перевести контроль (реальный контроль) в способ оплаты. Доверьтесь методу оплаты, передайте ему задачу оплаты.

Под этим я подразумеваю, что вы сохраняете контроль где-то, где вы просите, чтобы способ оплаты выполнял определенные части своей работы (причем эти части отличаются для разных конкретных методов). Шаги как validate(), prepare...(), Кроме того, вы ожидаете, что он предоставит вам "шлюз", поэтому теперь код вне метода оплаты (даже если это суперкласс) должен знать, что это такое или как с ним обращаться.

Вместо того, чтобы делать все это, попробуйте придумать дизайн, который передает полный контроль над методом оплаты, чтобы он мог выполнять свою работу без использования внешнего кода, выполняющего какой-либо конкретный набор шагов.

Например:

public interface PaymentMethod {
    Receipt payFor(Bill bill);
}

PaymentMethod Здесь все делают сами. Перенаправление пользователя, сохранение квитанции в базе данных, все что нужно. Как только вы почувствуете себя комфортно с этой "основной" абстракцией (она охватывает все варианты использования), вы можете работать над созданием меньших абстракций, которые охватывают такие детали, как сохранение в базе данных, если оно одинаково для всех методов.

В связи с этим: не используйте абстрактные родительские классы как способ обмена кодом между классами, это не совсем то, для чего предназначено наследование. Создайте правильные абстракции для разных "кусочков кода", и пусть они будут использоваться "большими" абстракциями (то есть композицией).

Не существует такого количества объявлений абстрактных функций, как BAD, хотя огромное количество может означать, что дизайн имеет недостатки. Просто обратите внимание на принцип Единой ответственности.

Вы уже определили, что у вас есть 4 режима - поэтому я думаю, что вы должны сделать 4 интерфейса для каждого режима в вашем случае. После этого вы можете увидеть, что является общим для всех 4 из них, и извлечь базовый интерфейс. Вы можете извлечь 6 уникальных логик для всех из них также в качестве интерфейсов...

Другие вопросы по тегам