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
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!
DiscountStrategy. New discounts are added by creating new classes, not modifying FeeCalculator.OCP-Compliant Design
+ 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.
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.