Попробуйте вместо попыток натянуть сову на глобус, а задачу на паттерн, действовать от простого. Пусть заказ бывает бесплатным. У заказа есть флаг "является ли он бесплатным". Мы даже можем воспользоваться возможностями языка и прикрутить какой-то геттер для надежности, чтобы флаг менялся в зависимости от устанавливаемой цены (в примерах условно JS):
class Order {
get isFree() { return this.price === 0; }
}
Мы можем прям сюда присобачить и оплату, но допустим мы не хотим, чтобы заказ что-то знал об урлах для оплаты. Ему это не надо. Заказ - это список товаров, какой-то статус завершенности, адрес доставки и.т.д. Он про данные, не про процессы. Путь будет обработчик, который может провести заказ как кассир в пятерочке:
class Order { /* --- */ }
class Сashier {
proceed(order) {
const url = order.isFree ? "free url" : "paid url";
// ...редиректим, проводим оплату, предлагаем пакетик и кофе по акции
}
}
Допустим у нас есть много платежных систем, там все сложно. Инфраструктура - не кассира дело. Ок, дадим ему доступ к адаптеру, который скрывает все за собой и проводит непосредственно оплату.
class Order { /* --- */ }
class PaymentHandler {
charge(price) {
// ...редиректим куда надо, в зависимости от цены и привязанной карты
}
}
class Cashier {
proceed(order) {
const isSuccess = PaymentHandler.charge(order.price);
if (isSuccess) {
// заказ прошел
} else {
throw new Error('ГААААААЛЯЯЯ ОТМЕЕЕЕНА!!!!');
}
}
}
Получается четкое разделение ролей. Заказ - про данные, кассир - про процесс, обработчик оплаты - про муть с платежными системами. Возможно, что даже флаг isFree не особо нужен у заказа. И, возможно, что к паттерну "состояние" мы не придем, потому что он в первую очередь решает проблему бесконечного количества комбинаций флагов, но если нет флагов, то нет и проблемы. И.т.д. Развивайте конструкции по мере их естественного появления. Тогда проблем с именованием будет на порядок меньше. Потому что каждое слово в коде отражает какую-то заранее сформированную сущность в алгоритмах в голове.