Programming Microsoft ASP.NET 4 - Dino Esposito [297]
}
else
{
...
}
}
The real problem you have here, instead, is an incorrect definition of inheritance rules. Square can’t be derived from Rectangle because it is not expected to be able to do at least all the things that the base class does.
Substitution Principle Real-World Considerations
Liskov’s principle can be difficult to grasp for many developers. An easier way to explain it is the following: each derived class should expect no more than the parent and provide no less than the parent.
This means, for example, that you break the principle if a virtual member ends up using a private member of the class. Likewise, you break the principle if a derived class adds more preconditions to a virtual method.
Liskov’s principle isn’t meant to portray inheritance—a pillar of OOP—in a bad light. Quite the reverse, it calls your attention to a safe use of virtual members. If you derive and just add new features, you’re absolutely safe. If you don’t have virtual members, you’re absolutely safe. If you have virtuals and actually override them in derived class, you should pay additional attention.
Note
In .NET 4, you have the Code Contracts API to express preconditions, postconditions, and invariants around your classes. A precondition is simply an IF in a method that executes at the very beginning of the code. If you use this API to express preconditions for the methods of a class, and happen to add preconditions to an overridden method of a class, you get a warning (not an error) from the C# compiler.
The Interface Segregation Principle
There’s an aspect of coding that I find particularly annoying—being forced to write code that I don’t really need. You might say that you should not write any code that you don’t need. There are situations, however, in which this is necessary. When? For sure, when the code ignores the Interface Segregation principle.
The principle is so simple that it seems like merely common sense. It says that client components should not be forced to depend upon interfaces that they do not use. More precisely, components should not be forced to implement members of interfaces (or base classes) that they don’t plan to use.
There’s nothing bad in a client that provides a void implementation of a particular interface method or that just throws if invoked. Sometimes this happens simply because the client deliberately intends to provide a partial implementation; sometimes, however, it happens because the interface is poorly designed. Fat interfaces are a bad thing. The Single Responsibility principle should hold true for interfaces also.
Interface Segregation Canonical Example
The classic scenario to examine to start thinking about interface segregation is the definition of a door. If you’re asked to simply define a door, you would probably come up with an interface with just a couple of Lock and Unlock methods and perhaps a Boolean property IsDoorOpen. However, if you know that you also need to deal with timed doors that sound an alarm if left open for too long, you might arrange something like this:
public interface IDoor
{
void Lock();
void Unlock();
Boolean IsDoorOpen { get; }
Int32 OpenTimeout { get; set; }
event EventHandler DoorOpenForTooLong;
}
The apparent plus of this design is that it gives you just one interface that can serve up both scenarios: timed and regular doors. This is an apparent benefit because it forces you to have two extra members on any class that implements the interface: the event DoorOpenForTooLong and the timeout property. Why on earth should you have these members where they aren’t needed?
Interface Segregation Real-World Considerations
Note also that code that is not strictly needed can’t be ignored after it is compiled. In other words, any code that gets compiled does count, regardless of whether or not it was necessary when you designed your classes. Because it’s there, it can influence the application, it must be tested, it must be debugged, and it must be maintained. Quite paradoxically, because it’s there it can