SOLID PrinciplesOpen-Closed PrincipleMedium⏱️ ~3 min

Applying OCP: Parking Lot Discount System

Scenario: Extensible Discount Calculation

A parking lot needs to calculate fees with various discount strategies: member discounts, off-peak hour discounts, promotional codes, and loyalty points. Without OCP, you would modify the fee calculator every time a new discount type is introduced, risking bugs in existing discount logic.

Design with OCP Violation

Before (Violates OCP)
class FeeCalculator:
  calculateFee(ticket, discountType):
    if discountType == "MEMBER":
      // member logic
    else if discountType == "OFFPEAK":
      // off-peak logic
    else if discountType == "PROMO":
      // promo logic
    // Add new if-else for each discount!
After (Follows OCP)
Each discount is a separate class implementing DiscountStrategy. New discounts are added by creating new classes, not modifying FeeCalculator.

OCP-Compliant Design

«interface»
DiscountStrategy
+ applyDiscount(baseFee): Money
MemberDiscount
+ applyDiscount()
OffPeakDiscount
+ applyDiscount()
PromoDiscount
+ applyDiscount()
LoyaltyDiscount
+ applyDiscount()
FeeCalculator
- strategies: List<DiscountStrategy>
+ calculateFee(ticket): Money
+ addDiscount(strategy): void

How It Works

Step 1: FeeCalculator maintains a list of DiscountStrategy objects. These are injected at runtime based on ticket properties (customer type, time of day, promo code presence).

Step 2: When calculating fees, FeeCalculator iterates through strategies and applies each discount sequentially or uses a composition rule (best discount, stacked discounts, etc.).

Step 3: To add a new discount (for example, corporate partnership discount), create CorporateDiscount implementing DiscountStrategy. No changes to FeeCalculator, MemberDiscount, or any other existing class.

Interview Tip: Explain how discounts are selected and applied. For example, "The ticket metadata determines which strategies to instantiate, and the calculator applies them in priority order." This shows you think about runtime behavior, not just static design.

Edge Cases and Variations

If discounts cannot be combined, use a single strategy selected by priority. If discounts stack, apply each sequentially. If there are complex eligibility rules, consider adding a canApply(ticket): boolean method to the interface, though this adds complexity. For simple cases, the factory or client code handles eligibility before passing strategies to the calculator.

💡 Key Takeaways
Discount strategies are separate classes, not if-else chains
FeeCalculator depends on DiscountStrategy abstraction, not concrete discounts
New discount types require zero changes to existing classes
Strategies can be composed, sequenced, or selected based on priority
Handle eligibility logic either in factory code or by adding canApply() to interface
📌 Examples
1Adding LoyaltyDiscount without modifying FeeCalculator or other discounts
2Composing multiple discounts for a single parking ticket
3Switching discount algorithms based on customer tier
← Back to Open-Closed Principle Overview
Applying OCP: Parking Lot Discount System | Open-Closed Principle - System Overflow