The principal ways of doing this are:
Constant and liberal use of the above techniques is recommended.
An interesting quote from chapter one of Design Patterns, regarding the use of generic references:
"This so greatly reduces implementation dependencies between subsystems that it leads to the following principle of reusable object-oriented design :
Program to an interface, not an implementation.
Don't declare variables to be instances of particular concrete classes. Instead, commit only to an interface defined by an abstract class. You will find this to be a common theme of the design patterns in this book."
(They state a second general principle as well: "Favor object composition
over class inheritance.")