My Thoughts

By replacing a messy switch statement with polymorphism, we were able to future-proof the Log() method. The result is much cleaner.
The Open/Closed Principle (OCP) is similar to the Interface Segregation Principle (ISP) and Dependency Inversion Principle (DIP) in that it encourages operating on interfaces instead of classes. This puts less restriction on how we choose to modify our model class hierarchies and allows our functional classes to become more useful.

Liskov Substitution Principle (LSP)

Objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program.

In other words, functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.

You cannot substitute Square for Rectangle, even though it seems like it would be. This is because the dimensions of a square cannot (or, at least, should not) be modified independently.

Rules:

Contravariance of method arguments in subtype.

Covariance of return types in subtype.

Preconditions cannot be strengthened in a subtype.

Postconditions cannot be weakened in a subtype.

Cannot introduce new exception types.

You cannot make an immutable class mutable.

Bad

ReadOnlyFile inherits from ProjectFile, but throws a new Exception type. If it were truly replaceable, the conditional check in SaveAllFiles() wouldn’t be necessary. It also violates the post-condition that the file was actually saved to disk.

My Thoughts

The Emailer and Dialler classes are now more useful to us, as they can operate on classes other than Contact. This type of refactoring could present us with a number of opportunities to prevent duplicated effort.
The Interface Segregation Principle (ISP) is similar to the Open/Closed Principle (OCP) and Dependency Inversion Principle (DIP) in that it encourages operating on interfaces instead of classes. This puts less restriction on how we choose to modify our model class hierarchies and allows our functional classes to become more useful.

Dependency Inversion Principal (DIP)

High-level modules should not depend on low-level modules. Both should depend on abstractions.

Abstractions should not depend upon details. Details should depend upon abstractions.

Write against an interface instead of a concrete class.

Bad

The TransferManager can operate only on BankAccounts. If other Account types are added later, they will have to be subclasses of BankAccount.

My Thoughts

The Dependency Inversion Principle (DIP) is similar to the Open/Closed Principle (OCP) and Interface Segregation Principle (ISP) in that it encourages operating on interfaces instead of classes. This puts less restriction on how we choose to modify our model class hierarchies and allows our functional classes to become more useful.

Conclusion

The five SOLID principles build on each other in interesting ways.

The SRP encourages us to have functional classes that operate on object classes within our model hierarchy.

The OCP, ISP, and DIP then encourage us to have our functional classes operate on interfaces rather than concrete classes, thus freeing us to structure our model hierarchies as we see fit.

The LSP encourages us to write our classes in a way that functionality in classes is not broken in subclasses.