general question regarding when to abstract vs. interface

  • Thread starter Thread starter Trevor Braun
  • Start date Start date
T

Trevor Braun

This is just a thought I wanted to put out there to get some opinions...

I've been programming for a number of years, but until a couple of years
ago, always with VB6, so everything I did to "fake" inheritance was done
with encapsulation. About two years ago, I began working with .NET and C#.
This question has bothered me for a while, but never enough to cause any
great concern in my work.

I just started designing a new project, and the question has popped up
again....

Can you think of a time where you would intentionally choose to use an
interface instead of an abstract, other than as a workaround for the lack
multiple inheritance support? Iss there any general rule or rule of thumb
or best practice that has been formulated that dictates when to use
interfaces vs. when to use abstract classes.

Thanks for any comments.
Trevor
 
Trevor Braun said:
This is just a thought I wanted to put out there to get some opinions...

I've been programming for a number of years, but until a couple of years
ago, always with VB6, so everything I did to "fake" inheritance was done
with encapsulation. About two years ago, I began working with .NET and
C#. This question has bothered me for a while, but never enough to cause
any great concern in my work.

I just started designing a new project, and the question has popped up
again....

Can you think of a time where you would intentionally choose to use an
interface instead of an abstract, other than as a workaround for the lack
multiple inheritance support? Iss there any general rule or rule of thumb
or best practice that has been formulated that dictates when to use
interfaces vs. when to use abstract classes.

You have a set of related methods with no implementation, and a number of
types which will share these methods. The question is wether to use a
interface or an abstract class.

IMO the answer is: use an interface. Using an abstract class restricts the
other types for no good reason. You are forclosing the option to have one
of those types extend some other base type.

The corollary to this is: Never use a 100% abstract class. Only use an
abstract class when you are going to implement some of the methods or some
of the data in the base class.

David
 
Ok, that makes some sense. The situation I'm facing now is that I'm using a
utility library as a way to sneak around the circular reference problem.
That is, one project (Proj A) that is referenced by two other projects (Proj
B and C) so and contains a bunch of classes (interfaces or abstract) so that
the other two other projects to reference one another's classes.

I see two options:
1) Build some hefty abstract classes with all the implementation code in
them in Project A and inherit the abstracts into Projects B,C
2) Build interfaces in Project A and all the implementation code in
Projects B,C.

Since the derived classes will only ever reasonably be inheriting from the
classes in Project A, do you think it's best to put the implementation into
Project A so there is less work involved when I want to extend the classes?

Trevor
 
Hi,

One approach I use with my students that are very new to the concepts of OO
is
to explain the difference class relationships that can be represented using
various
constructs provided by there OO language of choice. This is by no means
original, but I do
find that it helps clarify the topic to some extent.

Inheritance represents an "IS A" relationship, for example a Car IS A
Vehicle,
an Employee IS A Person. The related classes have both interface and
functionality
in common i.e.. Car type extends on the basic functionality and interface
provided by that
of a Vehicle type, and so would a Boat type while different to a Car it does
have commonalities
with the Vehicle type. The same goes for the Employee class that shares the
interface and functionality
of a Person reusing what the Person type has to offer and adding those
aspects unique to an Employee.

Interfaces on the other hand represent a "CAN DO" relationship, for example
a class of type String CAN DO comparisons, so can a class of type Int32
however
the how of the comparison is very specific to the class. Strings have to
take culture and characterset
into account while Int32 can do binary comparisons between instances. For
these classes
all they have in common is there ability to compare. Since they share this
common ability (CAN DO)
it would be advantageous if we could ensure that all types with similar
abilities provide the same
interface to that functionality even though the implementation is totally
unrelated.

Abstract classes are classes that are intended to be base classes therefore
part of an IS A
relationship. However abstract classes are incomplete and instances of these
types would be senseless.
So if I took my example of the person and employee further, one might
consider a living being to be
the abstract base class of a Person. A LivingBeing provides some base
functionality and even interface,
it is not a complete enough definition to be instantiated, to prevent a
caller from misusing you LivingBeing class and creating instances, it is
marked as abstract. However the class Person can be derived from a
LivingBeing, benefiting from
the functionality and interfaces provided by the LivingBeing class and
extending that into a complete type
that can be instantiated. And later we can define a Dog class which while
not related to the Person the Dog
is a LivingBeing.

Additionally an abstract class can have abstract methods. In short the
purpose of an abstract method is
to ensure that derived classes provide an implementation of the required
method.

If you made it this far I hope it was worth the read :)
 
Trevor Braun said:
Ok, that makes some sense. The situation I'm facing now is that I'm using
a utility library as a way to sneak around the circular reference problem.
That is, one project (Proj A) that is referenced by two other projects
(Proj B and C) so and contains a bunch of classes (interfaces or abstract)
so that the other two other projects to reference one another's classes.

This class structure sounds a little fishy. Are you sure that there need to
be circular references?

One exercise would be to isolate the parts of each class which need to refer
to the other class. Once you've done that, you may find that the isolated
sections of each class should be yanked out into a third class which the
first two should reference.

John Saunders
 
That's exactly what I'm doing. I guess I wasn't very clear on this, but:

The "third class" you mention is what I call Project A.
the two other classes that have had their parts isolated are Projects B and
C. The both reference Project A.

(the circular reference is exactly why the third class must be created, as
you said) I'm not sure what you found fishy, but hopefully I've explained
it more clearly now with the silly sample at the end of my post. (it's been
a long day, and I'm tired, but hopefully it successfully illustrates my
question)

My direct question is since the "shared" classes in Projects B and C won't
be inheriting from anything other classes, should the implementation code be
in Project A, or should Project A only contain interfaces. This isn't a
technical question it is a "Best Practice" question.

Also, I guess where I said "abstract" classes isn't exactly right. For this
example (and my project) it doesn't really matter if they're abstract or
not.

And this really is just a best practice thing. Either way will work fine, I
just wondered if there was any suggestions out there. The interface thing
sounds good, and I like the reasoning, but it creates a lot of extra coding,
since the objects have to be created by another object, and extending
classes means extending the interface and then extending the class(es) that
inherit from them. As for using a base class/abstract class, it means
there's code split between projects and inheritance is "used up".

If anyone has any other suggestions or opinions please let me know.

Thanks,
Trevor

// Option 1 starts here
ProjectA
public interface ImNeededByProjectC
{
int MyMethod(int x);
}

public interface ImNeededByProjectB
{
int OtherMethod(int y, int z);
}

ProjectB
public class NeededByProjectC : ImNeededByProjectC
{
public int MyMethod(int x)
{
return x+5;
}
}

ProjectC
public class NeededByProjectB
{
public int OtherMethod(int y, int z)
{
return y+z;
}
}
// Option 1 ends here


// Option 2 starts here
ProjectA
public class BaseNeededByProjectC
{
public int MyMethod(int x)
{
return x+5;
}
}

public class BaseNeededByProjectB
{
public int OtherMethod(int y, int z)
{
return y+z;
}
}

ProjectB
public class NeededByProjectC : BaseNeededByProjectC
{
}

ProjectC
public class NeededByProjectB : BaseNeededByProjectB
{
}

// Option 2 ends here
 
Back
Top