Most of the developers on the SnupNow team do not have much background in OOP. Most cut their teeth on structured programming techniques. Some, like me, started programming before they even heard of “structured” techniques and learned first hand the cost of maintaining spaghetti code.
Switching completely to OO thinking from structured programming thinking is non-trivial, but that doesn’t mean that understanding enough about OO to start coding or read the code of others is difficult. This short introduction is geared to seasoned software developers who just don’t happen to have OOP in their bag of tricks to date. It uses InterSystems Cache’ examples, since that is our platform of choice for this project.
I’ll add a quick disclaimer that the average theorist and perhaps even practitioner might really dislike me putting OOP into old terminology in a way that is not precise. I am painting a picture that might add light to some aspects of learning OOP while potentially obscuring some other areas that are not the focus. For example, I will not talk about the heap and the stack. I won’t even mention references. This introduction will focus on a high level way a developer can think about their coding efforts, rather than on the details of how the machine works. In spite of this disclaimer, I just know some of you are going to shudder when you read the next sentence, but at least it should irk both OO and relational purists alike.
Description of Objects
Objects are named records in memory. They could be persisted, of course, to some secondary storage device, but even if they have been, they are in memory once the programmer is working with them. They have properties, which might be called members or attributes, but let’s just call them fields, OK?
Once you catch on to objects being records with fields, there is a bit more to add to it, but before we do that here is some motivation. Why would we want records that might not be persisted? Stored records are at the “back end” of the application (I lost some DBMS folks there, right?), but there are lots of nouns at the front-end too. When working with a browser user interface, for example, we have web pages. These pages have elements, some of which might be combined together and maybe even mapped to virtual records (objects) to form a component, as we do with the Cache’ Zen framework. Working with records throughout your software means that you can model nouns everywhere: front-end, back-end, and everywhere between.
Thinking procedurally about software with a user interface, we have a user invoking some software, which starts to run and then shows the user something, collecting information from them, which it manipulates and then interacts with a DBMS to store data, for example. Thinking in objects, the user still invokes something, we still show the user something, collect information, manipulate it and persist it. The verbs don’t go away, but the nouns get more visibility. The user arrives at a page on our site and fills out a textarea on a form. They trigger events, such as changing values on the page, moving the mouse, or clicking a button. Do you see how some verbs got rolled into the noun “event”? We can work with virtual records of data that back a page, and can think in terms of such objects as the pages and their components as well as the database files, records, and fields (tables, rows, and columns, if you prefer).
On to a Class
Let’s toss some of the other OO terms into the mix now. An object is an instance of a class. A class is–oh man if I were closer you would slap me (and you know who you are)–a program. It is source code, then perhaps bytecode or object code once compiled. It is a program that defines a type of object. It is the definition for our records in memory, whether a virtual or stored file definition. One instance of a class is an object just like one instance of a file is a record. The class is the definition of the object, the metadata, the spec for one type of record.
In case it helps to redeem myself after offending just about every purist reading (I’m hoping they simply are not reading this), I will say that I do not explain OOP this way to youngsters, just seasoned pros. With those starting out, I talk about modeling objects in the real world with objects in the computer and blah blah blah, the usual sort of introductory material. But I’ll continue with the previous approach by indicating that typically at the top of this program, called a class, is the data dictionary, the list of properties. There can be zero properties, unlike typical persisted records. So, there must be something else to it.
Yup, code. An amazing amount of the code in a class is just structured programming code, sometimes with more complex objects being manipulated in addition to variables of string or integer types, for example. The variables used in our code can be strings, collections, such as lists or records, or objects, those records in memory already mentioned.
Still Procedural, In Spite of Itself
When I was first teaching myself OOP, I had a question that took me a bit to figure out. I didn’t understand how a software application got started when all you had were these classes, these nouns. I found out that you could make one class that would be the application, the big kahuna, with all other class code coming into play from that one. That application was still a thing, a noun, however. How did you get it to do anything? I figured you had to start somewhere and progress to somewhere else, procedurally, and yet we seemed stuck with nouns.
Here is the secret that I’ll share in Java, only because I have not needed to learn the Cache’ syntax for this. In Java, a class can have a “main() method” declared by the cryptic public static void main(String args) declaration, which we might think of as a MAIN: label in the program. If you launch this class (not all classes can be launched), and it finds this main method, it runs it. It runs it like you would run a BASIC program, starting at the top and exiting at the end of this method. There you have it. All OO applications are written procedurally, start to finish. Object-oriented applications start somewhere and end somewhere, just like any other software applications. They have if statements and the usual cadre of control structures. They have variables and assignment statements. It is just code, not magic.
A variable in a method can be of any type. Types are defined by classes. So, if I define a class called P.Person (we can talk about the dot in that name later), I can have an object identified with a variable name, such as myPerson, that is an instance of this class, a P.Person record, so to speak.
Our classes have code along with fields. We can run this code from our programs (other classes), much as we might run subroutines. This code is available to be run once one has one of these objects in memory. It is written in the form of functions, sometimes called methods. These functions are like subroutines where you can pass in values and get values back in return, adhering to the specific syntax for declaring them and running them. They are typically shaped in the form of a function such as myFunction(thisInput,parameterList) returns a String. In Cache’ a method declaration might be:
Method getAge() As %Integer
If this were a typical subroutine or a function from structured programming, we would pass in a parameter, which could be done in OOP and which we would do in this example if we were using a class method, a method that does not assume you have an instance of this class in memory. That could be declared as
ClassMethod calculateAge(BirthDate) As %Integer
Instead of passing in the BirthDate, if that birthdate is part of our object (a property or attribute of the object, aka a member of the object), then we need not pass it in, we simply have to indicate what object to use and it has the birthdate. If our class is called P.Person, we might create a new instance of it (a new record) with
myNewPerson = “P.Person”->%New()
in Cache’ using MVBASIC enhanced for OOP. We could then continue with something like
myNewPerson->BirthDate = “04/06/1956”
and then (for the sake of this example) decide to show the user the age of this person by executing the code from the getAge() method as
print “The age of this person is ” : myNewPerson->getAge()
If using a class method from any class instead, you could write
print “The age of this person is ” : “AnyClass”->getAge(“04/06/1956”)
See how that works? You can call class methods as functions without needing to have an instance (object) of the class they are in, while you call regular or instance methods by using the name of the object that is an instance of that class and then calling (with an arrow in this notation, a dot in Java) the method “on” that object and from the class that defines the type of object this is.
When you read class documentation, you are reading program documentation telling you the properties (fields) for an object of this type and what functions you can call, whether with an object (instance methods) or without (class methods).
myForm = zenPage.getComponentById(‘personForm’);
myForm = zen(‘personForm’);
The object named myForm is in memory in the browser now, having executed the getComponentById() method from the page object named zenPage. Using Zen we can do this same thing in another language of our choice on the server when appropriate (like when the page has been generated on the server, before we show it to the user), using MVBASIC, for example, with the statement:
myForm = %page->%GetComponentById(‘personForm’);
MVBASIC enhanced for objects makes it easier to work with the more complex (than a string) nouns on the front-end of our application, such as a web page and its components.