After all the content we’ve created so far, it’s time to introduce you to a concept which, at the same time, is the main paradigm of Ruby and other modern programming languages: object-oriented programming.
But as always, we have to have a good context before going deeper on the topic and also see what other kinds of programming paradigms are out there.
What is a programming paradigm?
I think we need to understand first what a paradigm is. Or at least to remember what it is. In science and philosophy, a paradigm is a distinct set of concepts or thought patterns, including theories, research methods, postulates, and standards for what constitutes legitimate contributions to a field. So, as you can see we’re talking about patterns or a way of thoughts here programming creators lies on to build software. Now that we refresh the paradigm term, let’s check what a programming paradigm is.
According to Wikipedia: Programming paradigms are a way to
classify programming languages based on their features. Languages can be classified into multiple paradigms. Some paradigms are concerned mainly with implications for the execution model of the language, such as allowing side effects, or whether the sequence of operations is defined by the execution model. Other paradigms are concerned mainly with the way that code is organized, such as grouping a code into units along with the state that is modified by the code. Yet others are concerned mainly with the style of syntax and grammar.
Common programming paradigms include:
- - Imperative: in which the programmer instructs the machine how to change its state,
-
procedural which groups instructions into procedures,
-
object-oriented which groups instructions with the part of the state they operate on,
- - Declarative: in which the programmer merely declares properties of the desired result, but not how to compute it functional in which the desired result is declared as the value of a series of function applications,
-
logic in which the desired result is declared as the answer to a question about a system of facts and rules,
-
mathematical in which the desired result is declared as the solution of an optimization problem
-
reactive in which the desired result is declared with data streams and the propagation of change
As you can see the world of paradigms is extensive, and sometimes a bit complex to understand. And almost always the best way to understand each one of them is creating code, but maybe you’ll need to use different programming languages which is not the purpose of this blog post. For that reason we’re going to focus in object-oriented programming languages and then try to go deeper with examples in Ruby
What is Object-Oriented Programming?
Object Oriented programming (OOP) is a programming paradigm that relies on the concept of classes and objects. It is used to structure a software program into simple, reusable pieces of code blueprints (usually called classes), which are used to create individual instances of objects. There are many object-oriented programming languages including Ruby, JavaScript, C++, Java, and Python.
OOP is all about the mindset, and less about programming power or simplicity (in most cases). The benefit is that it's one way of breaking complex problems into more manageable ones - and for many people, it's quick to grasp and provides a consistent method for structuring their code.
Think about how code evolves from simple problems to complex ones under the OOP line of thinking:
For very simple problems, you're essentially writing a script.
The next step is to find code that you (would) repeat in your script. That code is then generalized into functions.
If you look at large collections of functions, patterns start to emerge. Many perform work on some type of "thing". In an object oriented world, that "thing" becomes an object, and all the functions doing work on it become methods. It's a great way to group and think about your functions. It's a simplistic OOP world at this point.
After that, patterns start emerging between different objects. You have many objects that have similar sets methods, though not quite the same. They're also used in very similar situations. What they do have in common are their abstract behaviors. At this point, you introduce interfaces (or abstract base classes) and subclassing to help define those behaviors in a consistent manner and ensure that they can be used interchangeably.
Even further! You start noticing patterns between largely dissimilar classes. There may be certain portions of functionality that sets of classes have in common with each other. You generalize those into protocols/mixins and use multiple inheritance (or similar methodologies) to use those generalized collections of methods/behavior in your classes.
A class is an abstract blueprint used to create more specific, concrete objects. Classes often represent broad categories, like Car or Dog that share attributes. These classes define what attributes an instance of this type will have, like color, but not the value of those attributes for a specific object.
Classes can also contain functions, called methods available only to objects of that type. These functions are defined within the class and perform some action helpful to that specific type of object.
I know there is a lot of new jargon to understand. Later we’ll be creating classes, objects and methods in Ruby. But for now it’s necessary to have some theoretical knowledge about it.
What are the main reasons to use OOP?
You could ask at this moment what are the advantages of using the OOP paradigm. We’ll here we’ll be discussing some things to have in mind
Faster development: Reuse enables faster development. Object-oriented programming languages come with rich libraries of objects, and code developed during projects is also reusable in future projects.
Higher-quality software: Faster development of software and lower cost of development allows more time and resources to be used in the verification of the software. Although quality is dependent upon the experience of the teams, object oriented programming tends to result in higher-quality software.
Software-development productivity: Object-oriented programming is modular, as it provides separation of duties in object-based program development. It is also extensible, as objects can be extended to include new attributes and behaviors. Objects can also be reused within and across applications. Because of these three factors – modularity, extensibility, and reusability – object-oriented programming provides improved software-development productivity over traditional procedure-based programming techniques.
Software maintainability: For the reasons mentioned above, object oriented software is also easier to maintain. Since the design is modular, part of the system can be updated in case of issues without a need to make large-scale changes.
Lower cost of development: The reuse of software also lowers the cost of development. Typically, more effort is put into the object-oriented analysis and design, which lowers the overall cost of development.
However as always we have to see the other side of the coin, so what could be some cons of using OOP?
Larger program size: Object-oriented programs typically involve more lines of code than procedural programs.
Slower programs: Object-oriented programs are typically slower than procedure based programs, as they typically require more instructions to be executed.
Steep learning curve: The thought process involved in object-oriented programming may not be natural for some people, and it can take time to get used to it. It is complex to create programs based on interaction of objects. Some of the key programming techniques, such as inheritance and polymorphism, can be challenging to comprehend initially.
Not suitable for all types of problems: There are problems that lend themselves well to functional-programming style, logic-programming style, or procedure-based programming style, and applying object-oriented programming in those situations will not result in efficient programs.
Creating our first Class and Objects
Before going deeper we have to understand the syntax to create classes and objects. I’ll be explaining this from scratch so pay attention to these concepts start following me with your text editor
The first thing to do is create a new file with the extension .rb
➜ blog_tutorials touch oop.rb
Then open your text editor are we’ll declare our first class
class Person
end
As you can see we used a reserved word in Ruby calles “class” and then we gave it a name. Is the name class, in this case “Person”. After that we have a blank space to start creating the body of the class and finally we have the “end” keyword to close the body.
If we execute this code in the console, it won’t print anything. Let’s try it
So, we’re ok, no errors, but also showing nothing
We have a class declared, and now we’re going to create an object. To do that let’s write outside the class next thing
class Person
end
p1 = Person.new
puts p1
Here is where things become interesting, because we have a class and we have an object. The line number 5 is doing something called “instantiation”. Instantiate is the act of creating a new object and assigning it to a variable. To instantiate a class we need to call the class name (Person in this case) and then the method “.new”. Right there is where the magic happens.
If we print out the result we’ll see next output
What is that weird output? It’s the object allocation in memory. In plain text is where the object is stored by ruby in your memory. Do you remember our
blog post about memory? We’ll this is like the memory address to access rapidly to the object once we need it again.
Now the good thing about classes and objects, as we discussed previously in the advantages, is that we can reuse the code inside the class and create different objects who are “born” from that class. That means we can create multiple “instances” from just one class.
class Person
end
p1 = Person.new
p2 = Person.new
p3 = Person.new
puts p1, p2, p3
If we print this code we receive 3 different objects
Finally let’s see the logic inside the class. Let’s greet our users.
class Person
# behavior or logic to apply in each instance
def greet
"Hello world!"
end
end
p1 = Person.new.greet
p2 = Person.new
p3 = Person.new.greet
puts p1, p2, p3
Let’s execute it
Could you see the difference?
The method is just returning a string. And when we’re instantiating the first and third person we are calling that method (immediately after the instantiation). Second sentence doesn't call the method. This is intentionally, just to show you that the greeting method is just invoked by the ones who call it. Pretty awesome!
Let’s refactor a bit the code to have it ready to do more and different tasks
#1. Class declaration
class Person
# 2. Behavior or logic to apply in each instance
def greet
"Hello world!"
end
end
#3. Instantiation
p1 = Person.new
p2 = Person.new
p3 = Person.new
#4. Printing out and calling a class method in some of them
puts p1.greet
puts p2
puts p3.greet
Let’s execute it
Here the behavior and the output does not change. We’ve just refactored the code and left the instance variables like p1 or p2 alone with their own instance (Person.new). And below that we printed out the instance with their method.
These are the first steps in the OOP world, we’ll be doing more exercises and going a bit deeper on this. So stay tuned
I hope you learned a lot
Thanks for reading
Daniel Morales