The most important point to address here is that you're not quite sure
of the purpose of interfaces. Forget about the casting thing... it was
just a bad example in the book.
The value of interfaces doesn't come up immediately after you create
the object in question. After all, as you pointed out, you have the
object and you know what class it is, so why do you care about
interfaces at that point in the code? The answer is that you don't: you
have a reference to the object and that reference is declared as the
true type of the object, so you can get at all of the functionality of
the object. (For the pendantic types, yes, I know that that isn't
entirely true: there are circumstances in which you have to cast to an
interface in order to "see" methods and properties defined on that
interface, but this is an introduction, not a comprehensive guide.
Interfaces come into their own when you start storing objects in
composite structures and/or passing them around. Think of it this way.
Let's say that you have a bit of functionality that you want to
implement in a bunch of objects that have no other logical realtionship
between them. IStorable isn't such a bad example. Let's say that you're
writing business software, so you have lots of classes defined like
Supplier, Customer, PurchaseOrder, Invoice, StockItem, and things like
that. All of these classes are stored in database tables somewhere. As
well, you have other classes that are just kind of housekeeping things
that you create, use, and destroy while your programs are running but
don't form part of your company's permanent data store.
OK, now what you want to do is write some sort of central software for
managing writes back to your database, caching, and refreshing the
cache from time to time. However, there's no common ancestor between
all of your "stored" classes: a StockItem and a Customer have nothing
to do with each other, other than the fact that they can be stored in
the database. Here, you have a few choices:
1. Make a common base class like StorableObject and inherit every
storable thing from it. That works great when you have only one
quality, "storable," and that's it. However, what happens when some of
these things, like PurchaseOrder and Invoice, should be "printable"?
Well, you could give them a common ancestor, too, which works until you
end up with something that's "printable" and not "storable". Do you see
how, when you have only single inheritance to work with, embedding all
of this functionality in the class hierarchy starts to create fake base
classes with no purpose other than to offer some tidbit of
functionality, and how that in turn leads to a mess?
2. Teach your central data-managing classes about each "storable"
class. Most likely, each method in the data-managing classes would take
a type of "object" (so, no compile-time type checking here) and would
check the object at run time against all possible storable types of
things to see what it is, then cast it, then call its "Store" method.
So, every time you create a new storable class, you have to go an
modify all of the methods in your data-managing classes. Maintenance
nightmare.
3. Create an interface, and have all of those classes that otherwise
have nothing in common, implement IStorable. Now your data-managing
classes can deal only with objects that implement IStorable. *This* is
the big payoff. It's the ability to write code like this:
public class DataManager
{
WriteCollectionToDatabase(IEnumerable collection)
{
foreach (object o in collection)
{
IStorable storable = o as IStorable;
if (storable != null)
{
storable.Store();
}
}
}
}
Notice that the method _doesn't care_ what kind of object is in the
collection, only that the object implements IStorable and so has a
Store() method. This is the big payoff: the ability to write generic
code that can deal with any object in your class hierarchy that
implements the specific behaviour it needs.
Here are some examples of interfaces I've created for my projects:
IKeyed: objects that implement IKeyed know their own primary keys. This
capability is implemented by many classes across my class hierarchy,
but whether an object knows its own primary key or not has nothing to
do with its place in the inheritance tree. Thus, the behaviour is
defined in an interface.
IHasListViewModel: I have various controls that inherit from
Windows.Forms classes. As such, if I want a class that inherits from a
Panel and a class that inherits from a ListView to have some
functionality in common, I have no choice but to use an interface,
because the class hierarchy is already established by Microsoft. I
can't add functionality to Windows.Forms.Control, for example, so I
have to "add in" functionality to selected of my custom controls using
an interface.
ISelfUpdatingCollection: I have many different kinds of collections
that automatically keep themselves in synch with the database. Each one
of these inherits (logically) from the simple collection class of the
same type, so the self-updating collections can't have any common
ancestor that distinguishes them from collections that don't
self-update. To give you an idea of the class hierarchy, here are some
examples:
SelfKeyedCollection (base class)
StockItemCollection (derived from SelfKeyedCollection)
SelfUpdatingStockItemCollection (derived from
StockItemCollection)
SupplierCollection (derived from SelfKeyedCollection)
SelfUpdatingSupplierCollection (derived from
SupplierCollection)
etc. Now, if I want to tell the world that
SelfUpdatingStockItemCollection and SelfUpdatingSupplierCollection have
some functionality in common, and I want to write methods that can deal
with a SelfUpdating...Collection without caring what type it is, I have
to use an interface, because there's nowhere in the class hierarchy
that I can insert the appropriate common behaviour.