In Depth Look at OOP

A CS degree look at OOP

by samjay22

Author Avatar

To begin, let us talk about what OOP is; Object-Oriented Programming is a form of programming in which objects are used instead of loose-leaf code. This programming form has many benefits that include making development easier to maintain and update, simulating real-world use, lower memory, higher organization, and reusability.

There are four fundamental principles to follow in OOP, Encapsulation, Abstraction, Inheritance, and Polymorphism. These all play an essential role in making OOP a practical and useful form of development; hence why many big tech corporations use OOP in their development.

Before continuing, please note in OOP, functions are referred to as methods. Methods represent the transfer of messages/data or procedures associated with a class. Methods essentially are what a function does in an object/class. Also, note what a superclass is. A superclass is a class that will inherit from a class. For example, I have a CarSuperClass; this superclass contains all of the functions needed for every car in my game. Instead of assigning all of the variables and methods needed in each car class, I can inherit from the CarSuperClass.

Now let us take a more in-depth look to see what these four principles are.

Encapsulation is the idea that we can hide direct access to data by using a private key while using available methods. This principle is more used in C#, C++, Java, and Python; however, it is usable in Lua. An example in Roblox would be, let us say I had a PlaneClass, in this class, only the pilot would have access to fly the plane. Therefore, we give the pilot a key directly from the server to the pilot's client, so only the intended user can access data. The pilot can only call the fly function with his private key, leading to only Encapsulation.

Abstraction is the idea that we only want the user or client to see the code's functionality rather than the framework itself. This principle allows users to see what the code does instead of how the code works. For example, I have a PlayerData superclass; I have methods that deal directly with saving and loading player data in this class. When the player joins, the player object/instance will inherit from the new data object I have created. Furthermore, instead of having a script that indirectly deals with player data, the class now directly deals with player data.

Inheritance is one of OOP programming's essential principles; this principle is the idea of the relationship between 2 or more classes. If I were to create a class that deals with player combat and then created a character class, I would inherit the player combat class inside the character class. By doing this, I no longer need to define the combat methods in the character class as I am inheriting all that the combat class methods. This inheritance shortens and organizes code and makes code easier to maintain and update in the long term.

Polymorphism is the idea an object can act or behave in many ways. The most common use we see in modern development is in Java; this occurs when a class references a variable that refers to a child class. An example of this would be to create a player data superclass that contained a method called SaveData. Then if I were to inherit this class in a character class, we would have an inheritance. Now the character class has the SaveData method. If I were to change this SaveData function to add health instead of saving data, then that would be polymorphism.

Now that we have the four fundamental principles in OOP, we need to learn how to achieve OOP in Lua. In Lua, to achieve OOP like functionality, we use a unique table called a metatable. These tables give us the ability to create methods and classes that can follow the four principles.

Now, what is a metatable? Metatables are tables that allow us to change behavior. Metatables can define how Lua computes expression, as well as global functions. However, each table in Lua has its metatable; however, we would need to define and expand upon the table using setmetatable and using the given metamethods.

Lets start with the most simple form of metatables.

local Table = {} -- Here we can see a table that we just made. Now we need to add functionality to it.
--[[
To do this we need to use metamethods. There is a list of metamethods on the roblox WIKI; the metatable page lists all of their uses and how to use them.
]]

--Lets start with the first step. We will be using the .__index metamethod.
--[[This method allows us to check if the index is in the given table; if they are not, it will index itself. However, if there is no reference present, then it will return nil.]]

Table.__index = Table -- We refrence the table we want to pull the information from.

--[[
Now that we have setup the index function we can add methods to the table.
]]
--Lets start with our constructor.

function Table.New(Name, Shape, Size, Color)
 -- here we create our init method. This creates the class once called.
    return setmetatable({
        Name = Name;
        Shape = Shape;
        Color = Color;
        Size = Size;
    }, Table)
end

local Object = Table.New("Apple", "Square", "55", "Green")
local Red_Object = Table.New("Red Apple")
print(Object.Size) -- 55, here we can print out this objects properties by indexing them.
print(Red_Object.Name) -- Red_Object, we can make as many objects as we want; they will all have different data.

Now we know what a class looks like. However, how can we use the four principles that we mentioned earlier? Good question, ill show you.

This is an example of Encapsulation:

local Plane_Class = {} -- we init our table

Plane_Class.__index = Plane_Class -- we make the index

--[[
Note the usage of : instead of .  The key : is used when calling self or the object that the function is called from. The key . is used when init the class.
]]


function Plane_Class.New() -- we make our constructor to make the class
    return setmetatable({
    Key = "ABC_DE_ADF_A123";    
    },Plane_Class)
end


function Plane_Class:FlyPlane(PKey) -- make the fly method
    if self.Key == PKey then
        print("Take off vrrrr")
    end
end



local Plane = Plane_Class.New() -- create the plane object
Plane.__metatable = "Locked" -- here we lock the MT so it cannot be changed
Plane:FlyPlane(Plane.Key) -- we attempt to fly the plane, this will print Take off

This is an example of Abstraction:

local PlayerDataClass = {}

PlayerDataClass.__index = PlayerDataClass




function PlayerDataClass.New(P)
    return setmetatable({
    Player = P;    
    }, PlayerDataClass)
end


function PlayerDataClass:SaveData()
    print("Do stuff with data brrr")
end



function PlayerDataClass:LoadData()
    print("Do more stuff with data")
end


--Player joining

local Player_1 = PlayerDataClass.New("Jimbo_123xx")
local Player_2 = PlayerDataClass.New("Legendary_Foxx2015)
Player_1:SaveData() -- Save Data
Player_2:LoadData() -- Load data

This is an example of Inheritance :

local PlayerCombat = {}
local CharClass = {} -- we make our char class

local PlayerCombat_Copy = PlayerCombat -- make copys to prevent C -Stack errors
local CharClass_Copy = CharClass

PlayerCombat.__index = function(Table, Index) -- when indexing we return from the main table.
    return PlayerCombat_Copy[Index]
end
CharClass.__index = function(Table,Index) -- we inherit the function from player combat. Here we check if the index exist in both tables. if they dont then we return nil.
    return CharClass_Copy[Index] or PlayerCombat_Copy[Index]
end


function PlayerCombat.New(Player) -- create new player combat constructor
    return setmetatable({
    Player = Player;    
    }, PlayerCombat)
end


function PlayerCombat:Fight() -- we  make a fight function that will be inherited
    print("Pow Pow")
end

function CharClass.Spawn() -- spawn function
    return setmetatable({
    CharHealth = 100;    
    }, CharClass)
end

function CharClass:Heal() -- heal function
    print("Heal stuff :D")
end



local CharClass = CharClass.Spawn() -- create a char class.
print(CharClass:Heal()) -- call the heal method
print(CharClass:Fight()) -- we call the inherited fight function

Polymorphism will not really have a needed use in Lua; It is good to know; however, I have never really needed a reliable use. If you were to spend time learning C++ or Java, then this would be important. However, given the context of Lua and its limitations, there is no practical use.

Suppose you stayed till the end of this short tutorial on OOP,then congrats! You know everything you need to know for the first two years of a CS degree.

View in-game to comment, award, and more!