hello friends! new(ish)!
Object-oriented programming
Since the 90s, the concept of Object-Orientated Programming (OOP) has been very popular and you will certainly hear mention of it. OOP is basically a way of organizing code in types of variables. Dominant earlier languages like C often specified a small number of basic types, like integer, decimal, string, memory address and array (list of one of the previous types). Code was organized into routines, each routine could call other routines (there is often a single top-level routine called main()
which is what gets called when you run your program).
Since the basic types provided don't cover all that much, you represent complex data (for instance an email which has a sender, recipient, content, attachments, encoding, subject line, cc line, timestamp and so on) by putting each part in a separate variable. To help with this, there is something called a struct
, which is basically a bag of variables. A struct can contain other structs (and arrays of structs), so by nesting them together you can create a lot of complicated, hierarchical variables.
Object oriented programming takes this one step further. Let's use a classic example of a program that tracks a fleet of cars. Suppose we have decided that we must store the license no, color, speed and current destination of each car. We could have a construct like so:
sruct Car { string license_no; int color; int speed; Location destination; }
We could then perform operations on aspects of a given car:
taxi = new Car() taxi.license_no = "GAY4PAY" taxi.color = hFFCC00 taxi.speed = get_speed_from_gps_tracker(taxi.license_no) taxi.destination = "Mr. Smith's house" truck = new Car() truck.licence_no = "45A1CF3" truck.color = h888888 truck.speed = get_speed_from_gps_tracker(truck.license_no) truck.destination = taxi.destination
In this case we have defined a yellow taxi and a grey truck, both of them heading to the same place, and automatically obtained the speed a function that talks to the GPS tracker on each car (the GPS identifies the cars by their plate numbers).
Now let's say one of the cars is coming back to the office, and we want to record this. We can just do truck.destination = "Office"
. But suppose we will type this line many times - what if somewhere we make a typo, or do something like truck.destination = "Main Office"
, and a different function that checks which car is at the office (by looking for the exact string "Office" and not "office" or "Main Office") gets broken? What if the name of the office changes? To avoid this, we decide to make a single function for recalling a car to the office (this is called the Don't Repeat Yourself principle). We put this at the bottom of our program, and make a few other improvements, so maybe the whole file looks like this:
import gps_library main() { taxi = new Car() taxi.license_no = "GAY4PAY" taxi.color = hFFCC00 taxi.speed = get_speed_from_gps_tracker(taxi.license_no) set_location(taxi, "Mr. Smith's house") truck = new Car() truck.licence_no = "45A1CF3" truck.color = h888888 truck.speed = get_speed_from_gps_tracker(truck.license_no) truck.destination = taxi.destination // Some code here // Now recall both cars to office recall_to_office(taxi) recall_to_office(truck) } struct Car { string license_no int color int speed Location destination } struct Location { string name float lat float lon } recall_to_office(Car c) { c.recall_to_office } set_location(Car c, string target) { loc = new Location() loc.name = s loc.lat = get_lat_from_gps(s) loc.lon = get_lon_from_gps(s) c.destination = target }
Code organization and reuse
You can see how already the file is getting cluttered. In a real project, especially if you have tools generating some code for you, you can easily end up with hundreds of such subroutines strewn across files. You might notice that in this case, set_location
really only pertains to the behavior of Car
s - so perhaps they should go together? This is what OOP does. Instead of a struct, you have something called an Class
, which defines a kind of object
(object is an OOP terms similar to variable, but usually only simple data types like numbers are called variables, although in most modern OOP languages everything is considered an object). If the struct is a bag of variables, then the class is a bag of variables and functions ("methods" in OOP jargon).
Now we can refactor the above code into:
import gps_library main() { taxi = new Car() taxi.license_no = "GAY4PAY" taxi.color = hFFCC00 taxi.speed = get_speed_from_gps_tracker(taxi.license_no) taxi.set_destination ("Mr. Smith's house") truck = new Car() truck.licence_no = "45A1CF3" truck.color = h888888 truck.speed = get_speed_from_gps_tracker(truck.license_no) truck.destination = taxi.destination // Some code here // Now recall both cars to office taxi.recall_to_office() truck.recall_to_office() } class Car { string license_no int color int speed Location destination set_destination(string target) { loc = new Location() loc.name = s loc.get_coords_from_gps() destination = target } recall_to_office() { set_location("Office") } } class Location { string name float lat float lon get_coords_from_gps() { lat = get_lat_from_gps(s) lon = get_lon_from_gps(s) } }
At this point, you either think that organizing methods into classes makes things better, or you think it's completely insane. Ultimately, OOP is a matter of personal preference - and if the latter, then maybe it isn't for you.
You might think that the slightly rearranged code is a bit tidier, but not that much better. In fact, organizing code is not the main benefit of OOP. The main benefit is code reuse. In the above example, you could put the Car
and Location
classes into a separate file, and distribute it to other people who want to write software that operates on car. They won't need to modify anything (assuming the current functionality is sufficient for them) and can simply plug in these classes into their own program, avoiding duplication of work you have already done.
On the other hand, if one day you find a better implementation of car logic, you can simply swap the two files for each other to try the new one. You won't need to rewrite your whole program to try the new car code you found. Of course, the implementation of the class you found will have to match very closely, or you will have to rewrite things anyway, but modern OOP languages have mechanisms to help people write compatible code, and there very powerful IDEs like Eclipse, Netbeans and Visual Studio that do a lot of the class-stitching work for you.
Inheritance
Besides structs-with-methods, the other major feature of OOP is inheritance. It's a way of defining new classes by saying "this class is just like class X, but has this extra feature".
tour_bus = new Bus() tour_bus.passengers = 20 tour_bus.set_destination("Beach") // ... class Bus : Car { int passengers }
Conveniently, if you later change the logic of class X, the change will propagate to all classes inheriting from X. This helps avoid repetition, and allows you to easily extend classes created by other people or classes from your own past projects.