
Liskov Substitution Principle (2)
The fourth article in the SOLID Principles series describes the Liskov Substitution Principle (LSP). The LSP specifies that functions that use pointers of references to base classes must be able to use objects of derived classes without knowing it.
Example Code
To demonstrate the application of the LSP, we can consider code that violates it and explain how the classes can be refactored to comply with the principle. The following code shows the outline of several classes:
public class Project
{
public Collection<ProjectFile> ProjectFiles { get; set; }
public void LoadAllFiles()
{
foreach (ProjectFile file in ProjectFiles)
{
file.LoadFileData();
}
}
public void SaveAllFiles()
{
foreach (ProjectFile file in ProjectFiles)
{
if (file as ReadOnlyFile == null)
file.SaveFileData();
}
}
}
public class ProjectFile
{
public string FilePath { get; set; }
public byte[] FileData { get; set; }
public void LoadFileData()
{
// Retrieve FileData from disk
}
public virtual void SaveFileData()
{
// Write FileData to disk
}
}
public class ReadOnlyFile : ProjectFile
{
public override void SaveFileData()
{
throw new InvalidOperationException();
}
}
The first class represents a project that contains a number of project files. Two methods are included that load the file data for every project file and save all of the files to disk. The second class describes a project file. This has a property for the file name and a byte array that contains file data once loaded. Two methods allow the file data to be loaded or saved.
The third class may have been added to the solution at a later time to the other two classes, perhaps when a new requirement was created that some project files would be read-only. The ReadOnlyFile class inherits its functionality from ProjectFile. However, as read-only files cannot be saved, the SaveFileData method has been overridden so that an invalid operation exception is thrown.
The ReadOnlyFile class violates the LSP in several ways. Although all of the members of the base class are implemented, clients cannot substitute ReadOnlyFile objects for ProjectFile objects. This is clear in the SaveFileData method, which introduces an exception that cannot be thrown by the base class. Next, a postcondition of the SaveFileData method in the base class is that the file has been updated on disk. This is not the case with the subclass. The final problem can be seen in the SaveAllFiles method of the Project class. Here the programmer has added an if statement to ensure that the file is not read-only before attempting to save it. This violates the LSP and the OCP as the Project class must be modified to allow new ProjectFile subclasses to be detected.
11 January 2011