2Brute2Best
πŸ”₯0
A7

Cash-on-Delivery Refunds & Fat Partner Interfaces

Pro OnlyLiskov Substitution Principle (LSP)Interface Segregation Principle (ISP) std::expected (C++23)
⚑ 60-Second Architecture SummaryA7 · Liskov Substitution Principle (LSP) + Interface Segregation Principle (ISP)
Naive Bottleneck→Liskov Substitution Principle (LSP) + Interface Segregation Principle (ISP) Cure
The Naive Bottleneck: Batch refund processor crashes on Cash-on-Delivery orders

Our monolithic `RestaurantPartner` interface forces cloud kitchens to implement `reserveDineInTable()` and `printValetTicket()` by throwing dummy runtime exceptions (**ISP violation**). Similarly, a generic `PaymentMethod` interface forces `CashOnDeliveryPayment` to throw on `refundOnline()`, causing automated refund batch jobs to crash in production (**LSP violation**).

CashOnDeliveryPayment inherits refundOnline() and throws std::runtime_error. Callers iterating PaymentMethod* expect every instance to honour the interface contract β€” violating the Liskov Substitution Principle (LSP).

The Pattern Cure: Liskov Substitution Principle (LSP) + Interface Segregation Principle (ISP)

Subtypes must be substitutable for their base types without throwing unsupported exceptions (LSP); interfaces must be small and client-specific rather than fat and general-purpose (ISP).

We decompose fat interfaces into granular role interfaces: Payable and OnlineRefundable for payments; DeliveryPartner, DineInReservable, and ValetService for restaurants. CardPayment and UpiPayment implement both Payable and OnlineRefundable. CashOnDeliveryPayment implements only Payable. The batch refund processor accepts OnlineRefundable&, turning invalid refund attempts into compile-time errors.

Sprint 7 of 9 Β· Fixing Broken Subtypes & Fat Partner Interfaces
Story A β€” Food Delivery System β€” Progressive System Build
1. Where our codebase left off
← A6

In Sprint 6 (A6), we inverted dependencies in OrderService using constructor injection so high-level checkout logic depends only on abstract repository and gateway interfaces.

2. What we are building now

We are expanding our catalog to support diverse restaurant business models (delivery-only cloud kitchens alongside full-service dine-in restaurants) and multiple payment settlement types (including Cash on Delivery).

3. The pain point we hit

Our monolithic RestaurantPartner interface forces cloud kitchens to implement reserveDineInTable() and printValetTicket() by throwing dummy runtime exceptions (ISP violation). Similarly, a generic PaymentMethod interface forces CashOnDeliveryPayment to throw on refundOnline(), causing automated refund batch jobs to crash in production (LSP violation).

4. What this unlocks next
Next: A8 β†’

Decomposing fat interfaces into lean, focused role interfaces (DeliveryPartner, DineInReservable, Payable, OnlineRefundable) turns invalid operations into compile-time errors. Next in Sprint 8 (A8 Β· Adapter), we onboard Bella Pizza and 3rd-party providers whose legacy C-style SDKs (expecting raw C-strings and paise integers like LegacyPizzaSdk / StripeIndiaSDK) fail to match our clean domain interfaces.

01

The product ticket

Ticket Β· A7FOOD-107 Β· Cash-on-Delivery Online Refunds and Fat Partner Interfaces

Two critical production defects were reported this week: (1) An automated refund cron job looped over orders calling paymentMethod->refundOnline(), but CashOnDelivery orders threw std::runtime_error("COD cannot refund online"), crashing the batch processor. (2) A new delivery-only cloud kitchen brand onboarded, but was forced to write dummy stubs throwing exceptions for reserveDineInTable() and printValetTicket() because RestaurantPartner was a bloated fat interface.

Requirements
  • β€’

    Ensure all subtypes honour base contracts without throwing 'not supported' exceptions, fulfilling the Liskov Substitution Principle (LSP).

  • β€’

    Segregate bloated interfaces into focused, cohesive role interfaces so implementers never depend on methods they do not use (Interface Segregation Principle (ISP)).

  • β€’

    Catch invalid operations (like attempting to refund Cash-on-Delivery online) at compile time rather than during production runtime crashes.

The next requirement that breaks naive code

Product wants online refunds to report domain failure reasons (e.g. 'refund window expired', 'exceeds transaction amount') without using expensive C++ exceptions for standard control flow.

02

Step 1 β€” The naive code & why it breaks

A monolithic PaymentMethod interface forces CashOnDeliveryPayment to throw on refundOnline(), while a bloated RestaurantPartner forces CloudKitchen to implement dine-in and valet methods.

Naive design (what not to ship) Β· naive.cpp
#include <iostream>
#include <memory>
#include <print>
#include <stdexcept>
#include <string>
#include <vector>

// LSP Violation: PaymentMethod assumes all payment methods can refund online.
class PaymentMethod {
public:
    virtual ~PaymentMethod() = default;
    virtual void pay(int amountRupees) = 0;
    virtual void refundOnline(int amountRupees) = 0;
};

class CardPayment : public PaymentMethod {
    std::string cardMasked_{"*4242"};
public:
    void pay(int amountRupees) override {
        std::println("[CARD] Paid Rs.{} using card {}", amountRupees, cardMasked_);
    }
    void refundOnline(int amountRupees) override {
        std::println("[CARD] Refunded Rs.{} back to card {}", amountRupees, cardMasked_);
    }
};

class CashOnDeliveryPayment : public PaymentMethod {
public:
    void pay(int amountRupees) override {
        std::println("[COD] Paid Rs.{} in physical cash to delivery driver", amountRupees);
    }
    // LSP Violation: violates base class contract by throwing an exception!
    void refundOnline(int) override {
        throw std::runtime_error("COD orders cannot be refunded online! Cash refund required.");
    }
};

// ISP Violation: Bloated fat interface forces all restaurants to support dine-in and valet!
class RestaurantPartner {
public:
    virtual ~RestaurantPartner() = default;
    virtual void acceptDeliveryOrder(const std::string& orderId) = 0;
    virtual void reserveDineInTable(int partySize, const std::string& time) = 0;
    virtual void printValetTicket(const std::string& vehicleNumber) = 0;
};

class CloudKitchen : public RestaurantPartner {
public:
    void acceptDeliveryOrder(const std::string& orderId) override {
        std::println("[CloudKitchen] Cooking delivery order {}", orderId);
    }
    // ISP Violation: dummy stubs forced upon delivery-only cloud kitchen!
    void reserveDineInTable(int, const std::string&) override {
        throw std::logic_error("Cloud kitchen has no dine-in seating!");
    }
    void printValetTicket(const std::string&) override {
        throw std::logic_error("Cloud kitchen has no valet parking!");
    }
};

int main() {
    std::println("--- Naive Demo: Demonstrating LSP & ISP Fragility ---");
    CardPayment card;
    card.pay(500);
    card.refundOnline(500);

    CashOnDeliveryPayment cod;
    cod.pay(300);
    try {
        cod.refundOnline(300); // Throws!
    } catch (const std::exception& e) {
        std::println("Caught expected LSP crash: {}", e.what());
    }

    CloudKitchen kitchen;
    kitchen.acceptDeliveryOrder("ORD-901");
    try {
        kitchen.reserveDineInTable(4, "19:00"); // Throws!
    } catch (const std::exception& e) {
        std::println("Caught expected ISP crash: {}", e.what());
    }
    return 0;
}
Batch refund processor crashes on Cash-on-Delivery orders

CashOnDeliveryPayment inherits refundOnline() and throws std::runtime_error. Callers iterating PaymentMethod* expect every instance to honour the interface contract β€” violating the Liskov Substitution Principle (LSP).

Delivery-only cloud kitchens forced to write dummy stubs

RestaurantPartner bundles delivery, dine-in table reservations, and valet ticketing into one fat interface. CloudKitchen is forced to implement irrelevant methods that throw std::logic_error β€” violating the Interface Segregation Principle (ISP).

Type errors caught at runtime instead of compile time

Because CashOnDeliveryPayment is a PaymentMethod, the compiler cannot stop callers from passing it to online refund routines. The bug is only discovered when the production server crashes.

03

Step 2 β€” The fix: Liskov Substitution Principle (LSP) + Interface Segregation Principle (ISP)

Core idea in one sentence

Subtypes must be substitutable for their base types without throwing unsupported exceptions (LSP); interfaces must be small and client-specific rather than fat and general-purpose (ISP).

We decompose fat interfaces into granular role interfaces: Payable and OnlineRefundable for payments; DeliveryPartner, DineInReservable, and ValetService for restaurants. CardPayment and UpiPayment implement both Payable and OnlineRefundable. CashOnDeliveryPayment implements only Payable. The batch refund processor accepts OnlineRefundable&, turning invalid refund attempts into compile-time errors.

Pattern roles in our story
Role Interface (Payable)

Payable β€” declares virtual void pay(int amountRupees) = 0; for methods that can accept payments.

Role Interface (OnlineRefundable)

OnlineRefundable β€” declares virtual bool refundOnline(int amountRupees) = 0; for methods capable of reverse electronic transfer.

Role Interface (DeliveryPartner)

DeliveryPartner β€” declares virtual void acceptDeliveryOrder(string_view) = 0; for food preparation and packing.

Role Interface (DineInReservable)

DineInReservable β€” declares virtual void reserveDineInTable(int, string_view) = 0; for on-premise dining rooms.

Full Capability Implementer

DineInRestaurant β€” implements DeliveryPartner, DineInReservable, and ValetService without dummy methods.

Focused Implementer

CloudKitchen β€” implements only DeliveryPartner cleanly.

Brute2Best Pro Content

Unlock Full UML Diagram & C++23 Solution

The interactive UML architecture diagrams, refactored C++23 code, real-world extensions, and interview Q&A are part of Brute2Best Pro. All 7 foundation chapters are 100% free.

All 440 visual dry runs & algorithms
Interactive step-by-step playback with pointer & array state transitions
52 comprehensive LLD chapters
Interactive UML diagrams, real-world stories (Food Delivery, Trading, RPG), and practice solutions
40 C++ basics labs & hardware memory models
Vtables, byte layouts, assembly insights, and multi-threading race conditions
Production-ready C++23 source code
Modern idioms, clean contracts, time/space complexity derivations, and interview takeaways

First anchor question per pattern, Part 1 LLD & Module 1 C++ basics are 100% Free forever.

Systems Integration

Related C++ Systems & Algorithmic Foundations

Deep dive into underlying layers