Null Object Pattern: Cleaner Business Logic in C#
In business applications, we often deal with "optional" rules. For example, a customer might have a discount code, or they might have a loyalty membership.
A common mistake is treating the absence of a discount as a null value. This forces your pricing engine to constantly check if (discount != null) before calculating the total.
The Null Object Pattern solves this by treating "No Discount" as a valid, existing strategy that simply does nothing (returns the price unchanged).
The Scenario: Pricing Engine
We are building a checkout system. We have an interface IDiscountStrategy that calculates the final price.
❌ The "Bad" Example: Pollution with Null Checks
In this approach, if a customer is a "Guest" (no account), the DiscountStrategy property is null.
Why this is bad for business logic:
- Inconsistent Rules: The definition of "what happens when there is no discount" is hidden inside the
if/elseblock of theOrderProcessor. - Scalability: If you add tax calculation, shipping rules, and referral bonuses, your
CalculateFinalmethod will become a mess of nestedif (!= null)checks.
✅ The "Good" Example: The Null Object Strategy
Instead of passing null, we create a NoDiscount class. It implements the interface but applies neutral behavior (it returns the price exactly as it is).
Usage in the Real World
Now, your checkout flow is uniform for all customers.
Summary
| Feature | Null Checks (Bad) | Null Object Pattern (Good) |
| Logic | "If discount exists, apply it. Else, return price." | "Apply the discount." |
| Responsibility | The Processor decides the default behavior. | The NoDiscount class owns the default behavior. |
| Crash Risk | High (NullReferenceException) | None (Object always exists) |
| Maintenance | Messy if/else chains. | clean, polymorphic method calls. |
By using the Null Object Pattern, you remove the concept of "missing data" from your business logic. A customer with no discount isn't a "null case"; they are simply a customer with a "0% off strategy."