The other two answers, and the "possible duplicate question" link adequately address the problems of using base classes, rather than interfaces. But I'd like to address the code in the question itself.
The first thing to note is the return customer.IsBirthday() ? 0.10m : 0;
line.
This code reveals the fact that the IsBirthday()
method is likely tightly coupled to DateTime.Now
. This makes testing harder than it need be, eg without changing the system time (or only running the test once every four years), there's no easy way to test that it correctly handles a customer born on 29th February. The current date should be passed to IsBirthday()
.
Secondly, if the current date is passed to it, CalculateCustomerDiscount
becomes a pure function, thus can be made static:
public static decimal CalculateBirthdayDiscount(Customer customer, DateTime date) =>
customer.IsBirthday(date) ? 0.10m : 0;
And it can be referenced via a Func<Customer, DateTime, decimal>
delegate throughout the code. This removes the need to define the IDiscountRule
interface.
Not only do we simplify the code by removing an unused interface, we remove the need to have to create mocks of that interface in test code. A simple mock method can be used instead, once again simplifying things, potentially even removing the need to use a mocking framework.