newbie: understanding Interfaces

D

deko

From what I've read about Interfaces, they simply define a contract - that
is, they say: "this class will implement all the methods I say it will".
That's a nice "note to developer" but the question I have is this: what can
I do with an Interface that I can't do with regular method calls?

Perhaps it could be said "interfaces enable plug-and-play architectures
since all classes that implement the same interface can be used in the same
way". But if all the developers on my team agreed that any class name
ending in "03abc" implemented all 3 methods in class "abc", then we'd have
the same thing (except the compiler would not enforce our agreement).

Aside from the compiler enforcing an agreement among developers, what do
Interfaces do that I could not do with method calls?
 
M

Michael C

deko said:
From what I've read about Interfaces, they simply define a contract - that
is, they say: "this class will implement all the methods I say it will".
That's a nice "note to developer" but the question I have is this: what
can I do with an Interface that I can't do with regular method calls?

Regular method calls are really an interface also, it's just the interface
is stuck to your class and cannot be used elsewhere.
Perhaps it could be said "interfaces enable plug-and-play architectures
since all classes that implement the same interface can be used in the
same way". But if all the developers on my team agreed that any class
name ending in "03abc" implemented all 3 methods in class "abc", then we'd
have the same thing (except the compiler would not enforce our agreement).

You wouldn't be able to use the same variable to access any of the classes,
eg

abc myInstance;
if(SomeCondition) myInstance = new abc01()
else myInstance = new abc02();
myInstance.DoSomething();

Michael
 
G

Guest

Here's a simplified an example on how interfaces can be useful. Perhaps I
want to writer a program plays different types of media player. Perhaps I
want one program (or class) that will play a VHS player, a DVD player, or an
MP3 player. The code can be as follows:

public interface IPlayer
{
void Play();
void Stop();
void Pause();
void FastForward();
void Rewind();
}

public class VHSPlayer : IPlayer
{
#region IPlayer Members

public void Play()
{
// TODO: Add VHSPlayer.Play implementation
}

public void Stop()
{
// TODO: Add VHSPlayer.Stop implementation
}

public void Pause()
{
// TODO: Add VHSPlayer.Pause implementation
}

public void FastForward()
{
// TODO: Add VHSPlayer.FastForward implementation
}

public void Rewind()
{
// TODO: Add VHSPlayer.Rewind implementation
}

#endregion

}

public class DVDPlayer : IPlayer
{
#region IPlayer Members

public void Play()
{
// TODO: Add DVDPlayer.Play implementation
}

public void Stop()
{
// TODO: Add DVDPlayer.Stop implementation
}

public void Pause()
{
// TODO: Add DVDPlayer.Pause implementation
}

public void FastForward()
{
// TODO: Add DVDPlayer.FastForward implementation
}

public void Rewind()
{
// TODO: Add DVDPlayer.Rewind implementation
}

#endregion

}

public class MP3Player : IPlayer
{
#region IPlayer Members

public void Play()
{
// TODO: Add MP3Player.Play implementation
}

public void Stop()
{
// TODO: Add MP3Player.Stop implementation
}

public void Pause()
{
// TODO: Add MP3Player.Pause implementation
}

public void FastForward()
{
// TODO: Add MP3Player.FastForward implementation
}

public void Rewind()
{
// TODO: Add MP3Player.Rewind implementation
}

#endregion

}

public class TestPlayer
{
private IPlayer m_myMediaPlayer;

public void ExecuteMediaPlayer()
{
this.m_myMediaPlayer = new VHSPlayer();
this.m_myMediaPlayer.Play();
this.m_myMediaPlayer.Stop();

this.m_myMediaPlayer = new DVDPlayer();
this.m_myMediaPlayer.Play();
this.m_myMediaPlayer.FastForward();
this.m_myMediaPlayer.Stop();

//etc.
}
}

In the code above, the private member m_mediaPlayer can hold an instance of
any class that implements the IPlayer interface. So I can play any kind of
using an interface contract.

In addition, I can take this a step further and use Reflection to determine
what type of media player I should play. For example I could pass in a
parameter in ExecuteMediaPlayer(string typeToPlay) (or I could read it from a
file, etc). and use reflection to assign m_mediaPlayer the proper media
player to play.

this.ExecuteMediaPlayer("VHSPlayer");
this.ExecuteMediaPlayer("DVDPlayer");
//etc.

Hope this helps.
 
D

deko

Thanks for the nice example. That's a good illustration.
In the code above, the private member m_mediaPlayer can hold an instance
of
any class that implements the IPlayer interface. So I can play any kind
of
using an interface contract.

Yes, but why couldn't we assign similar objects to the m_mediaPlayer without
an Interface? I am not trying to insult the Interface construct here, just
trying to make my point, however sophomoric it may be.
In addition, I can take this a step further and use Reflection to
determine
what type of media player I should play. For example I could pass in a
parameter in ExecuteMediaPlayer(string typeToPlay) (or I could read it
from a
file, etc). and use reflection to assign m_mediaPlayer the proper media
player to play.

this.ExecuteMediaPlayer("VHSPlayer");
this.ExecuteMediaPlayer("DVDPlayer");

Now that sounds interesting. I could have a DataService class and use
Reflection to have the DataService connect to one of many different
databases depending on some criteria or availability or something like
that...
 
M

Michael C

deko said:
Yes, but why couldn't we assign similar objects to the m_mediaPlayer
without an Interface?

You'd need to define m_mediaPlayer as an object and then use late binding on
it. This is possible but not a neat. You'd also have to trap errors in case
something that wasn't a media player got in there.
Now that sounds interesting. I could have a DataService class and use
Reflection to have the DataService connect to one of many different
databases depending on some criteria or availability or something like
that...

That is a good use for interfaces.

Michael
 
G

Guest

deko said:
Thanks for the nice example. That's a good illustration.


Yes, but why couldn't we assign similar objects to the m_mediaPlayer without
an Interface? I am not trying to insult the Interface construct here, just
trying to make my point, however sophomoric it may be.

How would you define m_mediaPlayer and assign an instance to it? Can you
write a short example to show your point?
Now that sounds interesting. I could have a DataService class and use
Reflection to have the DataService connect to one of many different
databases depending on some criteria or availability or something like
that...

Correct, in your case all your DataService class will have the same method
names, but each method is implement differently. Just like the way a Play()
method would be totally different from a DVD player versus a VHSPlayer, etc.

Also, the contract is not necessarily between coders, it can be between
other classes, or even other assemlies. If I have my IPlayer interface
public, another assembly can have a class that implements my interface to
create a CassettePlayer or a BetaMaxPlayer or whatever.
 
G

Guest

Michael has a good point deko. Here's a rough example using interfaces and
reflection to create a Media Player what plays different types of audio and
video players.

[STAThread]
static void Main(string[] args)
{
IPlayer myMediaPlayer;
string playerToPlay;

//Lets pretend we have code here in which a user
//can select what type of player to play and
//we assign it to playerToPlay. We do not know
//what type of player to create, until the program
//executes and the user selects.
playerToPlay = "DVDPlayer";
myMediaPlayer = CreateMediaPlayer(playerToPlay);
myMediaPlayer.Play();
myMediaPlayer.Pause();
myMediaPlayer.Stop();

//Since we don't know at runtime which player to create,
//we will read it from a file, which will read in what
//type of player to create, it could be anything!
playerToPlay = "MP3Player";
myMediaPlayer = CreateMediaPlayer(playerToPlay);
myMediaPlayer.Play();
myMediaPlayer.Rewind();
myMediaPlayer.Stop();

}

private static IPlayer CreateMediaPlayer(string mediaPlayerName)
{
Assembly asm = Assembly.GetExecutingAssembly();
Type[] asmTypes;

asmTypes = asm.GetTypes();

string typeNamespace;
string typeToLookFor;
foreach(Type currentType in asmTypes)
{
typeNamespace = currentType.Namespace;
typeToLookFor = typeNamespace + "." + mediaPlayerName;
if(String.Compare(typeToLookFor, currentType.FullName) == 0)
{
//we have found our type, so return an instance of it
return (IPlayer) Activator.CreateInstance(currentType);
}
}

//if we get this far, then the type was not found.
throw new Exception("The type " + mediaPlayerName + " was not found.");
}
 
D

deko

Yes, but why couldn't we assign similar objects to the m_mediaPlayer
How would you define m_mediaPlayer and assign an instance to it? Can you
write a short example to show your point?

I'll try...

public class TestPlayer
{
private object m_myMediaPlayer;

public void ExecuteMediaPlayer()
{
if (m_myMediaPlayer is VHSPlayer)
{
m_myMediaPlayer.Play(VHSPlayer);
m_myMediaPlayer.Stop(VHSPlayer);
}
else if (m_myMediaPlayer is DVDPlayer)
{
m_myMediaPlayer.Play(DVDPlayer);
m_myMediaPlayer.Stop(DVDPlayer);
}
}
}
Correct, in your case all your DataService class will have the same method
names, but each method is implement differently. Just like the way a
Play()
method would be totally different from a DVD player versus a VHSPlayer,
etc.

Also, the contract is not necessarily between coders, it can be between
other classes, or even other assemlies. If I have my IPlayer interface
public, another assembly can have a class that implements my interface to
create a CassettePlayer or a BetaMaxPlayer or whatever.

So it appears that Interfaces don't really have any functionality(?) but
rather allow developers to categorize classes and use those categories for
different purposes?
 
M

Michael C

deko said:
if (m_myMediaPlayer is VHSPlayer)
{
m_myMediaPlayer.Play(VHSPlayer);
m_myMediaPlayer.Stop(VHSPlayer);
}
else if (m_myMediaPlayer is DVDPlayer)
{
m_myMediaPlayer.Play(DVDPlayer);
m_myMediaPlayer.Stop(DVDPlayer);
}

Now imagine you've got this "if" statement in 100 different places in your
code but slightly different each time and someone comes along an invents an
MP6 player...
So it appears that Interfaces don't really have any functionality(?)
Correct.

but rather allow developers to categorize classes and use those categories
for different purposes?

Correct.

Michael
 
G

Guest

That will work, but makes your code sloppy and harder to maintain. What
happens when we have more MediaPlayers (or power supplies, or DataSources, or
whatever)? You will have a very big if statement, or a huge switch/case
statement. Then you can have this code in multiple places in your program.
And then later down the road, another developer needs to create another
player, you will create your new class, and then have to update the code is
many different places, making your if statements bigger and the code uglier.

I rewrote a program about a year ago. It had what you just wrote. It had
switch statements all over the place and the code was hard to read and
maintain. I used the method described in my other post to make the code more
readable and maintainable.

Also, when you declare m_mediaPlayer of type object, you will have to cast
it to the specific media player and then use it. So you code will almost
work.

if(m_myMediaPlayer is VHSPlayer)
{
VHSPlayer typedMediaPlayer = m_mediaPlayer as VHSPlayer;
typedMediaPlayer.Play();
//etc.
}

You will get a compile error because System.Object does not implement a
method called "Play()", "Stop()", etc. This is because C# is strongly typed.
 
D

deko

Michael has a good point deko. Here's a rough example using interfaces
and
reflection to create a Media Player what plays different types of audio
and
video players.

[STAThread]
static void Main(string[] args)
{
IPlayer myMediaPlayer;
string playerToPlay;

//Lets pretend we have code here in which a user
//can select what type of player to play and
//we assign it to playerToPlay. We do not know
//what type of player to create, until the program
//executes and the user selects.
playerToPlay = "DVDPlayer";
myMediaPlayer = CreateMediaPlayer(playerToPlay);
myMediaPlayer.Play();
myMediaPlayer.Pause();
myMediaPlayer.Stop();

//Since we don't know at runtime which player to create,
//we will read it from a file, which will read in what
//type of player to create, it could be anything!
playerToPlay = "MP3Player";
myMediaPlayer = CreateMediaPlayer(playerToPlay);
myMediaPlayer.Play();
myMediaPlayer.Rewind();
myMediaPlayer.Stop();

}

private static IPlayer CreateMediaPlayer(string mediaPlayerName)
{
Assembly asm = Assembly.GetExecutingAssembly();
Type[] asmTypes;

asmTypes = asm.GetTypes();

string typeNamespace;
string typeToLookFor;
foreach(Type currentType in asmTypes)
{
typeNamespace = currentType.Namespace;
typeToLookFor = typeNamespace + "." + mediaPlayerName;
if(String.Compare(typeToLookFor, currentType.FullName) == 0)
{
//we have found our type, so return an instance of it
return (IPlayer) Activator.CreateInstance(currentType);
}
}

//if we get this far, then the type was not found.
throw new Exception("The type " + mediaPlayerName + " was not found.");
}

Yes, that helps. In this scenario the solution is such that you have
multiple classes that can implement the same interface - without which there
is no good reason to use an Interface (?)
 
D

deko

That will work, but makes your code sloppy and harder to maintain. What
happens when we have more MediaPlayers (or power supplies, or DataSources,
or
whatever)? You will have a very big if statement, or a huge switch/case
statement. Then you can have this code in multiple places in your
program.
And then later down the road, another developer needs to create another
player, you will create your new class, and then have to update the code
is
many different places, making your if statements bigger and the code
uglier.

I rewrote a program about a year ago. It had what you just wrote. It had
switch statements all over the place and the code was hard to read and
maintain. I used the method described in my other post to make the code
more
readable and maintainable.

Also, when you declare m_mediaPlayer of type object, you will have to cast
it to the specific media player and then use it. So you code will almost
work.

if(m_myMediaPlayer is VHSPlayer)
{
VHSPlayer typedMediaPlayer = m_mediaPlayer as VHSPlayer;
typedMediaPlayer.Play();
//etc.
}

You will get a compile error because System.Object does not implement a
method called "Play()", "Stop()", etc. This is because C# is strongly
typed.

10-4. Thanks for your comments. This has been a great dialogue.
 
M

Mark Harris

There is, if for instance you are exporting your class as a COM library,
you need to make your interface. Another reason why you would implement an
interface is if later on other developers may add additional classes that
would use it. So if you were making a media player, and all it did was
play AVI's then you would have an AVIPlayer - but what if you thought that
later someone might add a MP4 player, MPG player, or some other format? In
this case you wouldnt *need* the interface but it would future proof it.

Using interfaces also lets you do some nice things with casting.

The interface example below with reflection can be extended for 3rd party
plugins when done right too - which is very cool.

Michael has a good point deko. Here's a rough example using interfaces
and
reflection to create a Media Player what plays different types of audio
and
video players.

[STAThread]
static void Main(string[] args)
{
IPlayer myMediaPlayer;
string playerToPlay;

//Lets pretend we have code here in which a user
//can select what type of player to play and
//we assign it to playerToPlay. We do not know
//what type of player to create, until the program
//executes and the user selects.
playerToPlay = "DVDPlayer";
myMediaPlayer = CreateMediaPlayer(playerToPlay);
myMediaPlayer.Play();
myMediaPlayer.Pause();
myMediaPlayer.Stop();

//Since we don't know at runtime which player to create,
//we will read it from a file, which will read in what
//type of player to create, it could be anything!
playerToPlay = "MP3Player";
myMediaPlayer = CreateMediaPlayer(playerToPlay);
myMediaPlayer.Play();
myMediaPlayer.Rewind();
myMediaPlayer.Stop();

}

private static IPlayer CreateMediaPlayer(string mediaPlayerName)
{
Assembly asm = Assembly.GetExecutingAssembly();
Type[] asmTypes;

asmTypes = asm.GetTypes();

string typeNamespace;
string typeToLookFor;
foreach(Type currentType in asmTypes)
{
typeNamespace = currentType.Namespace;
typeToLookFor = typeNamespace + "." + mediaPlayerName;
if(String.Compare(typeToLookFor, currentType.FullName) == 0)
{
//we have found our type, so return an instance of it
return (IPlayer) Activator.CreateInstance(currentType);
}
}

//if we get this far, then the type was not found.
throw new Exception("The type " + mediaPlayerName + " was not found.");
}

Yes, that helps. In this scenario the solution is such that you have
multiple classes that can implement the same interface - without which
there is no good reason to use an Interface (?)
 
M

Michael C

deko said:
Yes, that helps. In this scenario the solution is such that you have
multiple classes that can implement the same interface - without which
there is no good reason to use an Interface (?)

Pretty much true but not always. You might create an interface and use it in
only one class because you expect in the future you might create a second
class. Or you could do it just because you want the method seperated out
into an interface for some reason. For example you might create an interface
with, say, 4 methods that one of your forms uses. This makes finding the
functions that you've added to the form easier. You could also use it to get
around dotnet not having multiple inheritance.
 
G

Guest

Your missing the point :) That is the perfect reason to implement interfaces
:) If I want to add another type of player, I just create another class that
implements my IPlayer interface and I'm done. I don't have to touch my Main
Program (with if statements) to take into account my new class.

An interface does not implement methods, but DEFINES methods. The variable

IPlayer m_myMediaPlayer

will Guarentee that I will have the methods Play(), Stop(), etc. defined by
what ever class that gets returned from CreateMediaPlayer().

Declaring m_myMediaPlayer of type object, does not guarentee that it
contains an instance of a class that implements Play(), Stop(), etc. In your
example VHSPlayer is not guarenteed to contain the Play() method if you do
not use an interface. You just have the "word of the programmer", which is
not good enough in my book;

public class VHSPlayer
{
public void PlayerVHSCassette();
}

private object m_myMediaPlayer;
f(m_myMediaPlayer is VHSPlayer)
{
//will get error because play is not defined.
//There's no guarentee Play is ever defined in this class.
m_myMediaPlayer.Play();
}

I hope this clears some things up :)

deko said:
Michael has a good point deko. Here's a rough example using interfaces
and
reflection to create a Media Player what plays different types of audio
and
video players.

[STAThread]
static void Main(string[] args)
{
IPlayer myMediaPlayer;
string playerToPlay;

//Lets pretend we have code here in which a user
//can select what type of player to play and
//we assign it to playerToPlay. We do not know
//what type of player to create, until the program
//executes and the user selects.
playerToPlay = "DVDPlayer";
myMediaPlayer = CreateMediaPlayer(playerToPlay);
myMediaPlayer.Play();
myMediaPlayer.Pause();
myMediaPlayer.Stop();

//Since we don't know at runtime which player to create,
//we will read it from a file, which will read in what
//type of player to create, it could be anything!
playerToPlay = "MP3Player";
myMediaPlayer = CreateMediaPlayer(playerToPlay);
myMediaPlayer.Play();
myMediaPlayer.Rewind();
myMediaPlayer.Stop();

}

private static IPlayer CreateMediaPlayer(string mediaPlayerName)
{
Assembly asm = Assembly.GetExecutingAssembly();
Type[] asmTypes;

asmTypes = asm.GetTypes();

string typeNamespace;
string typeToLookFor;
foreach(Type currentType in asmTypes)
{
typeNamespace = currentType.Namespace;
typeToLookFor = typeNamespace + "." + mediaPlayerName;
if(String.Compare(typeToLookFor, currentType.FullName) == 0)
{
//we have found our type, so return an instance of it
return (IPlayer) Activator.CreateInstance(currentType);
}
}

//if we get this far, then the type was not found.
throw new Exception("The type " + mediaPlayerName + " was not found.");
}

Yes, that helps. In this scenario the solution is such that you have
multiple classes that can implement the same interface - without which there
is no good reason to use an Interface (?)
 
G

Guest

Great conversation. This is just scratching the surface. This example is
only one implementation of how using interfaces can help.

If you really want to have fun, try playing with Abstract classes. They are
similiar to interfaces, except that you can actually implement methods in
abstract classes, and then just delcare other method names:
 
M

Michael C

Mark Harris said:
There is, if for instance you are exporting your class as a COM library,
you need to make your interface.

That's not really creating a single class that implements that interface
though because many other developers would also be implementing the same
interface.

Michael
 
D

deko

Your missing the point :) That is the perfect reason to implement
interfaces
:) If I want to add another type of player, I just create another class
that
implements my IPlayer interface and I'm done. I don't have to touch my
Main
Program (with if statements) to take into account my new class.

Yes - perhaps I should have said "without the prospect of additional
classes..."
An interface does not implement methods, but DEFINES methods. The
variable

IPlayer m_myMediaPlayer

will Guarentee that I will have the methods Play(), Stop(), etc. defined
by
what ever class that gets returned from CreateMediaPlayer().

Declaring m_myMediaPlayer of type object, does not guarentee that it
contains an instance of a class that implements Play(), Stop(), etc. In
your
example VHSPlayer is not guarenteed to contain the Play() method if you do
not use an interface. You just have the "word of the programmer", which
is
not good enough in my book;


public class VHSPlayer
{
public void PlayerVHSCassette();
}

private object m_myMediaPlayer;
f(m_myMediaPlayer is VHSPlayer)
{
//will get error because play is not defined.
//There's no guarentee Play is ever defined in this class.
m_myMediaPlayer.Play();
}

I hope this clears some things up :)

Yes, thanks. I'm working on a learning project now and will try to use an
interface for connecting to different databases - XML, Access, SQL Server -
based on user selection in a preferences form. The app could start off with
a DataSet serialized to XML; when that grows beyond a certain point, the app
could prompt the user to switch to Access; when that grows to a certain
point, prompt to switch to SQL Server. With all 3 DataService classes
implementing "IGetData" the switch will be seamless... :)
 
M

Michael C

deko said:
Yes, thanks. I'm working on a learning project now and will try to use an
interface for connecting to different databases - XML, Access, SQL
Server - based on user selection in a preferences form. The app could
start off with a DataSet serialized to XML; when that grows beyond a
certain point, the app could prompt the user to switch to Access; when
that grows to a certain point, prompt to switch to SQL Server. With all 3
DataService classes implementing "IGetData" the switch will be seamless...
:)

I wouldn't say seamless. You need to make sure the appropriate files are
installed, import the data and then deal with any differences between the
different backends. :)
 
G

Guest

Actually the true advantage is using the interface instead of the generic
object, it will provide type safety and allow you to execute any of the
IPlayer functions:
class MyPlayer
{
private IPlayer m_embeddedPlayer;

MyPlayer(IPlayer embeddedPlayer)
{
m_embeddedPlayer = embeddedPlayer;
}
internal void Play()
{
m_embeddedPlayer.Play();
}
}

as you can see, no testing to see what type of object m_embeddedPlayer is so
there is no 'overhead' trying to resolve the correct type, the player will
automatically call the correct play function.
 

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