Professor Mustard Programming
 

Programming 101 - Unit 04 - OOP

04a) Object Basics
 
I won't kid you... the first time you try to learn the ways of Object Oriented Programming (OOP for short), you're going to wonder what in blazes is going on, and why we're bothering to write all this code. That's how it was for me, that's how it is for most programming students, and that's probably how it's going to be for you, as well. It's not that OOP is incredibly complex, or horribly complicated... it's just that it usually takes awhile before you can see any point to it! But look at it this way: after learning about methods, can you even imagine writing a large program without using them? Of course not! Well, trust me, once you've become comfortable with OOP, you'll feel that way about objects, too.

A big reason that OOP is so hard to grasp (at first) is that it uses a totally different approach to programming, compared to what you've seen so far. You're used to thinking of programming in terms of verbs. For example, if you were programming a track-and-field run, you might think of it this way: "RUN down the path, then CRAWL through the log. If you're tall enough the ledge, then REACH for it, otherwise JUMP." This is called "procedural programming".

OOP, on the other hand, approaches programming in terms of nouns. If you were writing a program about a pizzeria, you would think of it this way: "Use the OVEN to bake the PIZZA. Divide the PIZZA into SLICES, and provide each GUEST with a SLICE."

What's the big difference? Procedural programming is more concerned with writing a series of instructions for performing a specific task, whereas OOP focuses on the nouns involved, the information they contain, and the tasks they can perform. That being said, however, OOP is a lot like the Matrix: nobody can be "told" what it is; you have to see it for yourself.

namespace A_BasicClass
{
  // The class our program runs from
  class Class1
  {
    [STAThread]
    static void Main(string[] args)
    {
    
    }

  }

  // A class for simulating pizzas!
  class Pizza
  {
    // Members go here
    // Constructors go here
    // Methods go here
  }
}



Ignore the "members/constructors/methods go here" part just for a moment, and look at the program itself. As you can see, the syntax for declaring a new class looks quite a bit like declaring a new method, except that we don't want any brackets in the class signature. Also, you'll notice that we haven't declared the Pizza class within our old Class1. For the first time, we've added something outside of our original class, so that only the namespace contains it.

What is a class? Let's start by thinking of them as custom, programmable datatypes. With the exception of your program's "main" class, Class1, most classes exist to define a "blueprint", from which an infinite number of objects can be created. Unfortunately, that only leads to another question, "what is an object"? We can't get any closer to that answer without writing some more code, so let's just take the plunge and expand on the Pizza class a little more:

class Pizza
{
  // Members go here
  public string Topping;
  public double Diameter;

  // Constructors go here
  public Pizza()
  {
    Topping = "";
    Diameter = 0.0;
  }

  // Methods go here
  public double GetRadius()
  {
    return Diameter / 2.0;
  }
}



Okay, that's starting to look a little more like something. What I've done is created a very simple class. It contains two variables ("Topping" and "Diameter"), a "constructor" for setting the variables to default (blank) values, and a method that calculates the pizza's radius, based on its diameter. Still, at this point, you're probably scratching your head, wondering what all this stuff actually does. And more importantly, why. Stop fretting. I'm going to add one last piece to this puzzle, and write some code in Main() that actually uses the Pizza class. After that, I'll (attempt to) explain everything.

[STAThread]
static void Main(string[] args)
{
  // Create a "default" pizza, and print its information
  Pizza pizza1 = new Pizza();
  Console.WriteLine("PIZZA1 after construction:");
  Console.WriteLine(" Topping = " + pizza1.Topping);
  Console.WriteLine(" Diameter = " + pizza1.Diameter);
  Console.WriteLine(" Radius = " + pizza1.GetRadius());

  // Change the pizza's information
  pizza1.Topping = "Pineapple";
  pizza1.Diameter = 9.0;

  // Re-print its information
  Console.WriteLine(Environment.NewLine + "PIZZA1 after changing its information:");
  Console.WriteLine(" Topping = " + pizza1.Topping);
  Console.WriteLine(" Diameter = " + pizza1.Diameter);
  Console.WriteLine(" Radius = " + pizza1.GetRadius());
}



Okay, now we finally take a step back and start making some sense out of this OOP thing. Let's start with the code we just wrote in main, on the first line (the one with the "new" keyword in it). In effect, this line has created a variable called "pizza1". This variable's datatype is not int or string, however; it's Pizza... a kind of "super-datatype" that we created ourselves! However, a variable whose datatype is based on a class differs from a simple datatype in two main ways.

The first difference is that we don't call it a "variable"; we call it an "object". It's really just a terminology difference... we get "variables" from simple datatypes, and "objects" from classes (you might also see objects referred to as "instances"; it's just another name for them, so don't get too hung up on it).

The second difference is how we give the object its initial value. With a simple variable, we just assign it simple value, like 4, "penguin", 3.92, or whatever. With objects, however, we can't do that. It just doesn't make sense to assign a simple value like 3 to a pizza, because the Pizza class contains many different pieces of information. So we use that "new Pizza()" thingy to initialize its data for us. Take a look inside the Pizza class we defined above, and you'll see that it actually contains a method called "Pizza()". All it does is set Topping to "", and Diameter to 0.0, but that's required to give the Pizza object its initial values. This is a very specialized kind of method, which is referred to as a constructor. You can tell that a method is a constructor, because it has the same name as the class it's in, and has no return type, not even "void".

The object is able to provide access to the variables and methods that it contains. To access the Topping variable in the pizza1 object, for example, you would type "pizza1.Topping". To access the GetRadius() method, you would type "pizza1.GetRadius()". It works exactly the same as when you call "ToString()" on one of your variables. You may have noticed, by the way, that we haven't used the word "static" anywhere in the Pizza class. We'll look at that in some more detail in a few lessons, but for now we'll just pretend that static is only used in your program's "main" class, Class1, and nowhere else.

Having looked at the constructor and members, let's also take a quick look at the Pizza class's only method, GetRadius(). More importantly, let's take a quick look at why it's so awesome. We could have stored the pizza's radius as a separate variable in the pizza class, but then it might disagree with the value stored in Diameter. Point in fact, a pizza's radius is completely dependant on the value of its diameter, and thus the radius doesn't need its own variable at all! Since the radius is always going to be half of the diameter, we simply create a method called GetRadius(), which returns half of the value of Diameter... and this way, the value of "radius" will always be correct (based on the Diameter), every time we ask for it! We could also create methods like GetArea() and GetPerimeter(), which would work exactly the same way (just with a different formula, of course).

Incidentally, you can declare a class's members, constructors and methods in any order you want, and you can name a class whatever you want, as well. Traditionally, though, members and constructors are listed first, and classes are given noun names that start with capital letters (they're supposed to represent nouns, after all).

So, having discussed all that, let's go back to our original question. What is a class, and what is an object? A class is a programming structure that lets us define our own custom datatypes. A class can contain methods, constructors, and members. An object is an "instance" of a class. Every object that you create will contain its own separate set of all of the methods and members that were in the original class you wrote. You could create a pizza2 object, and it would have its own set of members and methods; completely independent of pizza1. Heck, you could create a whole array of Pizza if you wanted to. Without objects, the only way to emulate this would be to have any array of strings called "toppings", and an array of doubles called "diameters". And even then, there still wouldn't be any way to prove which topping matched up to which diameter, nor would there be any way to associate a "GetRadius" method which each one (well, okay, you could use a relational database... but let's not even go there today!).

Let's talk about the benefits of OOP, just based on what we've learned so far. First off, there's code reuse. We don't have to write a Pizza class for every Pizza object we want. We write the Pizza class once, and then we can create as many Pizza objects as we want. Secondly, there's structure. Classes provide a logical way to group related information and methods into one tidy package. They enforce the idea that many verbs (methods) and adjectives (variables) can belong to the same noun (object). In turn, the benefit of structure is that bugs are less likely, and when they do occur, they're easier to find and fix. If your "Company Management" program isn't processing employee data the way you're expecting it to, then a good place to start looking for bugs is in that Employee class you wrote - not the other three million lines of code that deal with accounting, shipping, research, etc. But we haven't even considered one of the biggest advantages of classes... encapsulation.

We'll cover encapsulation, and improve on our understanding of classes in the next lesson. In the meantime, study the code sample below, which summarizes everything we've learned. I'd like to close this lesson with three final points:

1) You can overload constructors just like you would overload a normal method. It's handy if you want to create an object that contains useful values right away. Just like any method, a constructor can have as many or as few parameters as you want.
2) Just for the record, an object's members don't have to be simple datatypes. You can store other objects in an object, as well!
3) This is your first step into a gigantic new area. If you're feeling lost at first, or still can't really see the point to this whole OOP thing, don't worry. As you learn more about how classes work and what they can do, things will start to look up.



using System;

namespace A_BasicClass
{
  // The class our program runs from
  class Class1
  {
    static readonly string ln = Environment.NewLine;

    [STAThread]
    static void Main(string[] args)
    {
      // Create a "default" pizza, and print its information
      Pizza pizza1 = new Pizza();
      Console.WriteLine("PIZZA1 after construction:");
      Console.WriteLine(" Topping = " + pizza1.Topping);
      Console.WriteLine(" Diameter = " + pizza1.Diameter);
      Console.WriteLine(" Radius = " + pizza1.GetRadius());

      // Change the pizza's information
      pizza1.Topping = "Pineapple";
      pizza1.Diameter = 9.0;

      // Re-print its information
      Console.WriteLine(ln + "PIZZA1 after changing its information:");
      Console.WriteLine(" Topping = " + pizza1.Topping);
      Console.WriteLine(" Diameter = " + pizza1.Diameter);
      Console.WriteLine(" Radius = " + pizza1.GetRadius());

      // Create a "custom" pizza, and print its information
      Pizza pizza2 = new Pizza("Pepperoni", 6.0);
      Console.WriteLine(ln + "PIZZA2 after construction:");
      Console.WriteLine(" Topping = " + pizza2.Topping);
      Console.WriteLine(" Diameter = " + pizza2.Diameter);
      Console.WriteLine(" Radius = " + pizza2.GetRadius());


      PromptForExit();
    }

    static void PromptForExit()
    {
      Console.Write(ln + "Program complete! Hit enter to exit...");
      Console.ReadLine();
    }
  }

  // A class for simulating pizzas!
  class Pizza
  {
    // Members that every pizza will have
    public string Topping;
    public double Diameter;

    // Two different "constructors" - ways to create a Pizza object
    public Pizza()
    {
      Topping = "";
      Diameter = 0.0;
    }

    public Pizza(string pizzaTopping, double pizzaDiameter)
    {
      Topping = pizzaTopping;
      Diameter = pizzaDiameter;
    }

    // Method to calculate pizza's radius, based on its diameter
    public double GetRadius()
    {
      return Diameter / 2.0;
    }
  }
}