Interfaces vs Classes

R

RichS

Further to this. If you want to create a common Plugin environment it
is nearly impossible to use Classes. Take the following example:


In the Framework model you create a common Plugin base class, then in
each Plugin project you want to use that class. Because of the C#
compiler, even though it is the same class [ from the same file etc...
], because it was compiled in two different projects, the CLR treats
the classes as different types. Hence you cannot cast between the 2
different projects. The only way to do this is to create a common
interface, and that is the same across the 2 different projects so you
can cast.

This is especially important if you want to do something like this:

iPluginFile = Activator.CreateInstance( _assemblyType ) as IPluginFile;




Rich.
 
H

Helge Jensen

Frank said:
I get that. But, my rule has been what I learned in Intro to OOP 10
years ago. Interface is a "can-be", while Class is a "is-a". In my
case, one developer want to define an interface called, IDoThis and then
derive the class called CDoThis that implements IDoThis. To me, it
makes no sense whatsoever.

I'm curious as to what the difference between a "can-be" and "is-a"
relation is?

I would expect a "can-be" relation to be somekind of possibility for
polymophism, which might or might not succeed. This is relevant in some
cases, for example conversion and coercion, but i haven't really seen
interfaces used to indicate possibilities, but rather certanties.

I keep feeling that maybe "can/must-do" is a better word for what you
are talking about?
IDisposable makes sense because the class that implements it "can-be"
IDisposable. It is not "is-a" IDisposable. In fact, if you look at the
.NET framework, you won't find a single case of straight down
(IDoSomething, that's implemented by CDoSomething) encapsulation.

A special kind of "is-a" relation is the "can/must-do", which is what
IDisposable is. It effectively says: "I can (and should be) disposed
using the protocol for that".

I cannot really understand why implementations of IDisposable aren't in
an is-a relationship with IDisposable, they exhibit the behaviour
specified in the IDisposable "contract".

The .NET-fw has very few hotspots, where you can plug in your own
behaviour. This (largely) removes the need for IFoo, DefaultFoo, since
you are simply required to use DefaultFoo. The one place where .NET
almost have this behaviour is IComparator, which has a system-default
(but it also has a non-casesensitive one for strings, so it's not quite
there).

The IFoo, DefaultFoo is useful where you anticipate multiple
implementations but wish to provide a default one. Generic algorithms
and data-structures are prime examples.

For example I have the following in a project:

interface ISpanningTree {
ICollection Nodes { get; }
object Parent(object node);
ICollection Children(object node);
}

With only one implementation for now:

public delegate double Measure(object nodeA, object nodeB);
public class DijkstraSpanningTree : ISpanningTree {
DijktraSpanningTree(IGraph graph, object root, Measure measure) {...};
...
}

But I don't want the code that just needs some spanning-tree to know
that it's actually a Dijkstra-generated tree.

The extra code introduced amounts to the declararion of the interface,
which is a small price to pay for the clarity of dependence.
 
N

Nick Malik [Microsoft]

Hi Helge,

I agree with most of what you said. I have one nit-picky objection though:
The .NET-fw has very few hotspots, where you can plug in your own
behaviour. This (largely) removes the need for IFoo, DefaultFoo, since you
are simply required to use DefaultFoo. The one place where .NET almost
have this behaviour is IComparator, which has a system-default (but it
also has a non-casesensitive one for strings, so it's not quite there).

The 1.1 Framework has dozens of places where you can plug in your own
behavior. There fact that it is a fairly complete system means that there's
not a lot of need to do that, but there are MANY opportunities to do so.
The 2.0 Framework increased this number substantially.

--
--- Nick Malik [Microsoft]
MCSD, CFPS, Certified Scrummaster
http://blogs.msdn.com/nickmalik

Disclaimer: Opinions expressed in this forum are my own, and not
representative of my employer.
I do not answer questions on behalf of my employer. I'm just a
programmer helping programmers.
--
 
M

Michael S

Intresting thread.
I have little to contribute with that haven't already been said.
So I'll post a joke on interfaces instead.

I need a WebServer and so I'll build it like this:

interface IWebServer
{
void Start();
void Stop();
}

Done! All that's missing is the implementation.... =)

- Michael S
 
N

Nick Malik [Microsoft]

Hello Frank,
Thank you. One additional tid bit I'd like to know is relative speed. In
Java, abstract methods and classes are very fast. Interfaces require
extra indirection to find the corresponding method in the actual class. Is
this the case in .NET?

I haven't tested this. Since Abstract methods are implicitly Virtual
methods, and since the runtime will check the data type of the object
reference at run time when a virtual method is called, then it is safe to
assume that methods that are marked as abstract will incur a small run-time
performance hit by comparison to simply overriding a method in a class.
However, I'm speaking from conjecture. I have no evidence that there is any
performance hit at all, and it is quite possible that this part of the code
has been the focus of optimization efforts, thus reducing the chance you
will would be able to measure much difference at all.

That said, I strongly agree with the other responders: there are concerns
that are far more likely to slow you down than an in-memory type comparison.
Optimize where you spend the time. I would be very surprised if your
optimization of the inheritance tree would have ANY effect on the
performance of your application, especially if you make a single network,
hard disk, or database call in the inner loops of your logic.

To be honest, Frank, it sounds like you have convinced yourself, and no
amount of discussion on the merits of Interfaces will have any effect on you
one way or the other. I do not believe that your mind is open on this
topic.

If you would like to truly explore modern thinking in OO design, and not be
limited to what you learned 10 years ago, I encourage you to pick up a slim
volume called "Design Patterns Explained, 2nd Edition" by Shalloway and
Trott. This is an excellent examination of how to go from "is-a" and
"has-a" thinking to a richer and more meaningful understanding of what
objects can do for you.

--
--- Nick Malik [Microsoft]
MCSD, CFPS, Certified Scrummaster
http://blogs.msdn.com/nickmalik

Disclaimer: Opinions expressed in this forum are my own, and not
representative of my employer.
I do not answer questions on behalf of my employer. I'm just a
programmer helping programmers.
--
 
F

Frank Rizzo

Jeff said:
Well... Unfortunately I disagree with this rule. Realize that an
interface in C# is
conceptually equivalent to a pure virtual class in C++. Therefore an
interface
simply represents a abstract IS_A relationship. It "can be" or "looks
like", but it
still represents an IS_A relationship.

Well, there is a bit of difference. This of it in terms of nouns. What
is IDisposable? It is a Disposer (for the terms of our discussion).
For instance, BaseClass.ChildClass.GrandChildClass inherits from
BaseClass and Object, because it is that. However, just because it also
implements IDisposable, doesn't mean that GrandChildClass is a Disposer.
It isn't. It *CAN* Dispose, but it isn't a Disposer in itself.
 
F

Frank Rizzo

Jon said:
Yes, it requires an extra level of indirection. The time taken for that
indirection is almost certainly going to be insignificant for anything
other than the tightest loops.

Architectural performance (how many round-trips you need to make,
database locking etc) is going to be a *far* bigger factor in
performance than micro-optimisation like this.

I totally agree with that. Just wanted to get that piece of doubt out
of my hands. Btw, I did run some really simple (really simple) tests and
it seems that the speed is fairly comparable even in tight loops.
Ah, there we disagree. I believe that coding to an interface makes it
very clear what any one caller actually needs. A single class may
implement multiple interfaces to be used by multiple callers in
different ways - making each caller use just the interface makes it
easy to swap implementations, and to see what's using what.

I guess, will just have to agree to disagree.
 
F

Frank Rizzo

Nick said:
Hello Frank,



That said, I strongly agree with the other responders: there are concerns
that are far more likely to slow you down than an in-memory type comparison.
Optimize where you spend the time. I would be very surprised if your
optimization of the inheritance tree would have ANY effect on the
performance of your application, especially if you make a single network,
hard disk, or database call in the inner loops of your logic.

That is correct. I ran a couple of tests and they confirmed what other
people here were saying: the difference is very, very insignificant.
The reason I brought this up is that it was an issue in Java (at least
in the early years, haven't been keeping up).
To be honest, Frank, it sounds like you have convinced yourself, and no
amount of discussion on the merits of Interfaces will have any effect on you
one way or the other. I do not believe that your mind is open on this
topic.

No, actually, I do appreciate all the comments and do understand the
merits of interfaces. I'll have to evaluate them as it pertains to my
project.

If you would like to truly explore modern thinking in OO design, and not be
limited to what you learned 10 years ago, I encourage you to pick up a slim
volume called "Design Patterns Explained, 2nd Edition" by Shalloway and
Trott. This is an excellent examination of how to go from "is-a" and
"has-a" thinking to a richer and more meaningful understanding of what
objects can do for you.

Thanks, I'll check it out.
 
J

Jon Skeet [C# MVP]

Frank Rizzo said:
That is correct. I ran a couple of tests and they confirmed what other
people here were saying: the difference is very, very insignificant.
The reason I brought this up is that it was an issue in Java (at least
in the early years, haven't been keeping up).

It's not an issue in Java either any more, as far as I know. In fact,
Hotspot may even inline interface implementations if only one class
implements the interface (as it can undo the optimisation when another
class implementing the interface is loaded). I don't know whether or
not it does this (it definitely does for virtual methods in classes) -
either way, I wouldn't consider it a reason for just using concrete
classes unless I got really desperate :)
 
J

Jeff Louie

Frank...

If a class derives from a base class that implements IDisposable or if
the class
implements IDisposable itself then the class IS_A Disposer. The
GrandChildClass _must not_ restrict the behavior of the base class. If
the
GrandChildClass is not a disposer then it violates the Liskov
Substitution
Principle.

Again, IMHO an interface in C# is the equivalent to a pure virtual
_class_ in
C++. A class in C++ can inherit from one or more pure virtual classes
(interfaces). If you believe that class inheritance represents an IS_A
relationship then you must agree that an interface represents a subset
of IS_A
relationships.

You must decide if you believe that class inheritance represents an IS_A
relationship. You must decide if you believe that interfaces are
equivalent to
pure virtual classes in C++. But if you believe in both statements, then
logically you can only come to the conclusion that interfaces represent
an
IS_A relationship.

CAN_BE, LIKE_A is a useful construct for designing frameworks using
multiple
inheritance.

IS_A inheritance
HAS_A containment
LIKE_A multiple inheritance

Regards,
Jeff
Well, there is a bit of difference. This of it in terms of nouns. What
is IDisposable? It is a Disposer (for the terms of our discussion).
For instance, BaseClass.ChildClass.GrandChildClass inherits from
BaseClass and Object, because it is that. However, just because it also
implements IDisposable, doesn't mean that GrandChildClass is a Disposer.
It isn't. It *CAN* Dispose, but it isn't a Disposer in itself.
 
D

David Levine

RichS said:
Further to this. If you want to create a common Plugin environment it
is nearly impossible to use Classes. Take the following example:


In the Framework model you create a common Plugin base class, then in
each Plugin project you want to use that class. Because of the C#
compiler, even though it is the same class [ from the same file etc...
], because it was compiled in two different projects, the CLR treats
the classes as different types. Hence you cannot cast between the 2
different projects. The only way to do this is to create a common
interface, and that is the same across the 2 different projects so you
can cast.

That's not true. You can redirect all assembly references to use the current
version so that all references to a class refer to the current
implementation of the class, regardless of the version against whch the
original assembly was compiled.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top