Easy- general coding question

eramgarden

Contributor
Joined
Mar 8, 2004
Messages
579
I've been reading this website about OOP. I need to wrap my head around OOP and have general questions. For example:
http://www.dotnetjunkies.com/Tutorial/B1E4CE82-962D-4220-956E-1E4775ACCD46.dcik

1. Why should I use "overloading" when I can have 2 functions with 2 different names?? (Look at "Method Overloading")

2. When should I use "Interfaces"? why should i use them when I can have regualr classes to inherit from?

3. When and why should I use "abstract" classes when I can just have regualr classes to inherit from?

4. Why should I use Polymorphism when I can just have separate functions doing whatever?? (look at "Polymorphism" section)

is there a rule of thump to use what type of ...classes, polymorphism, sturcts, etc ...where?
 
You're asking some important questions - not always easy to answer. First, I'd suggest getting a good OOP book, not just an article. A book can go into more depth on things like interfaces, abstract classes, etc. These are common things to use, but knowing when to use which takes a bit of practice to learn.

Here are my versions of answers to your questions.
1. Why use operator overloading? In some cases, it makes the code more readable. You could have Int32.ParseFromString, Int32.ParseFromStringWithFormatProvider, Int32.ParseFromStringWithFormatProviderAndNumberStyles, etc., but that would be tedious.

More often than not, overloads are used when some parameters aren't strictly necessary and there's a common default or when you need multiple versions of a method that each take a specific type and you don't want to use "object" and do type checking.

2. Interfaces are a big subject. An interface tells you what behavior your class must have, but not how to do it. Also, you can implement many interfaces, but you can only inherit from one class (regular or abstract).

3. An abstract class is useful when you have core stuff you want to expose to multiple subclasses, but don't want anyone to create that base class. The common example is a Shape class, with methods to Draw(). You don't want anyone to create a Shape() class, but you want to use that for a Rectangle or Square shape. This is different from an interface in that the abstract class can have some code where the interface cannot.

4. Polymorphism allows you to pass different types to a function that takes a higher type. For example, you could have an ArrayList of Shapes or List<Shape> as an argument to a function. That list could really be some Rectangles, some Triangles, some Squares, etc. The function would only know how to work with a Shape, which is common to all.

Rule of thumb? Some books have checklists that help you narrow it down. Sometimes experience is the best key. Luckily, it's sometimes easy to change your mind later if you want. So if you started with an interface and find yourself putting the same code everywhere, maybe you want an abstract class.

Pick up the "Refactoring" book by Martin Fowler. I guarantee it will help out, once you start coding with OOP (or not!).

-ner
 
Just thought I'd add that theres a small series of articles in the tutors corner that covers some of these features, and I'm pretty sure it also brushes on why and when to use them (I could be wrong its awhile since I read it).
 
Sometimes it just takes the right kind of project to provide the opportunity to use one of these techniques and then the technique will start to make sense. Just sitting down and trying to come up with uses for interfaces is really hard. It wasn't until I started dealing with mock objects (a whole other ball of wax) that I could really understand the point of interfaces. Now I get it, and I've figured out when to use them and when I don't need them.

Polymorphism and abstract classes are pretty straight forward, but again, you aren't always going to have a need for these tools. And if you've never had a project where they might be used it's really difficult to fathom why you would ever need them. The best thing to do is constantly be aware of the tools you can use, learn about them, and not be afraid to try them out when you think you are in a situation that can benefit. It will come easier as you gain more experience.

Also, I find it helpful to study great designs (or at least designs that are better than mine ;)). The .Net Framework uses all of the tools within it to create a pretty darn good architecture. Pay close attention to the use of interfaces, inheritance, abstract classes, and so on for accessible examples.
 
You make a very important point, mskeel. It is something that never occured to me until recently; it is very difficult to understand why a tool exists until you have used it in a practical situation yourself. To that end, how about some examples of when you would each said feature. It might not be as education as certain books and tutorials, but for anyone who is merely interested in a quick read, here goes:

1. Overloading
Nerseus gave an example of this. This is very nice any time you want to have a function, but there are different possiblilites of parameters that could be applicable. Instead of choosing one possible set of parameters, or writing a dozen different functions that do the same exact thing with different input, you make a dozen overloads for the same function. This is ideal for constructors, for example. Look at the System.Version class. When you create a System.Version object, you may want to pass a string (new Version("1.2")). You may want to pass integers (new Version(1, 2)). It might seem useless to be able to type "1.2" instead of 1, 2, but it is anything but useless when the data could either come from a textbox or a function that returns an integer. You may or may not want to specify revision and build. So you create overloads for each possibility. And, unlike normal functions, you don't have the option of creating a number of differently named constructors, so overloading makes life much, much easier.

2. Interfaces
In programming we see the term "class" so often that we seem forget what it really means. A class defines a "class," or category, or type, of object. Stream defines a type of object. FileStream is a Stream. MemoryStream is a stream. They are all objects of the same "class" (or category, or type). A Bitmap is an Image and a Metafile is an Image. Interfaces define common functionality between objects that serve different purposes. Streams are a means of communicating data. Images are a means of representing visual data. But both of these different categories of objects have a certain common functionality: they both have a function to free unmanaged memory: Dispose(). This is the most common .Net Interface because it is a very commonly implemented functionality, but is not the fundamental function of the class.

A TreeNode collection and an integer array are two very different things, and there is no reason why they should inherit the same base class, but they both represent some type of collection that can be enumerated, so they both implement IEnumerable. You can operate on an IEnumerable interface and not care about what class the object actually is.

3. Abstract Classes
Abstract classes exist to define some, but not all, of a classes function. It is similar to an interface, but unlike an interface, it can implement some functionality. For example, the Control class handles keyboard and mouse input, but does not do any drawing. That is left up to the deriving class. The control class calls the OnPaint method when something needs to be drawn, but the Button class, or the CheckBox class, or whatever class, actually defines the OnPaint method. This can't be done with interfaces, and unlike regular inheritable classes, abstract classes don't have to implement everything.

4. Polymorphism
Suppose you write a function to take data for input, encrypt it, and then output it in its original form. Suppose that you write this function to operate over an array of bytes. Then you want to use it for and array of integers. And then for a text file. And then for downloaded web content. And you end up writing four different functions. Suppose you wanted to be able to output in any of the four forms and output in any of the four forms. Now you need sixteen functions, or at least the very least four read functions, four write functions, and one encrpyt function.

Now what if you write a function that accepts a stream as input, encrypts the data, and outputs the data to a stream. You can now use a MemoryStream object to encrypt a byte array, and a FileStream object to encrypt a file, and you don't need to write extra code to put the encrypted data in a different place from where it came. And then, if someone wants to encrypt data coming in through a serial port, they can write a SerialStream class. And not only can they encrypt the serail port data, but they can use that serial port data for any function that operates on streams.

With polymorphism, you can write one function and that one function is incredibly flexible because it operates on polymorphic objects. Not only that, but you can expand what the function can do without any modification to the function by exercising polymorphism. Create your own stream class, and like magic, the EncryptStream function is capable of doing more.
 
I don't have much to add but I thought some examples might help. I'll try to put up some links to some great designs (which you have the best chance of seeing in a book like the one nerseus suggested).

1. Overloading
There are two basic cases where overloading comes into play in my experience. When you have optional parameters (StreamWriter Constructor is a decent example), and when you need to cover multiple types (Console.WriteLine(). is an ok example of that). Overall this is mostly for convenience (And it's great!) but you do need to take care when implementing to adhere to the DRY principle. A quick example:
C#:
void WriteData(string data, int stars)
{
   WriteRowOfStars(stars);  //Writes a row of stars
   Console.WriteLine(data);
   WriteRowOfStars(stars);
}

void WriteData(string data) //stars are optional
{
   this.WriteData(data, 0); //So I make it easier for the user by passing 0 for him.
}
2. Interfaces
I'm sorry marble, but I'm going to get a little nit-picky on your wording (though I may have misunderstood what you meant). Interfaces don't so much define common functionality between objects as they define the common way to use something (all right...not that my wording is much better :D). You actually can't guarantee functionality for a class that implements and interface. By defining an interface, what you are saying is that "The methods in this interface are guaranteed to be here but what they do will depend on the class." So it's a little like a contract that a method will be available. The best example to me is the IList. Another example to show this in action:
C#:
class Sample
{
   private ArrayList _list = null;  //internal arraylist for storing stuff

   // ...
  
   public IList List  //you expose your internal list as an IList instead of an ArrayList
   {
      get { return this._list; }  //Now if you need to change your ArrayList to something else, 
                                       //So long as it implements IList, you won't have to change whatever uses this property.
   }
  
   //...
}
3. Abstract Classes
I don't really have anything to add. Abstract classes can be very useful.

4. Polymorphism
Nothing to add here either, except both abstract classes and interfaces can play a part in creating good class hierarchies.
 
Good point about the interfaces - how different objects behave over the same interface can vary. Passing certain objects to one ICollection can raise exceptions while passing the same objects to other ICollections will succeed. Common functionality, though, is the underlying concept. IEnumerators enumerate, ICollections expose a set of objects, IDisposable frees resources, etc.

In an ideal world, every interface would have appropriate functions to allow us to use every instance that interface without making any assumptions. An ICollection interface would have a CanBeModified or CanBeAddedTo property, and an IsAcceptableObject function so that we would not have to worry about whether we are dealing with a TreeNodeCollection or a Byte array, but instead simply query whether a given object can be augmented with a collection. But we have to stop the abstraction somewhere.
 
Back
Top