Object oriented progamming: constructors and properties
By Samir Tine, published on January 2023
Let's remember how we created an instance of the Fruct object :
apple = Fruct()
Under the hood, instance creation calls a special Object method called
constructor. But wait, we didn't provide such method to the Fruct object, so how was it possible to create the apple and banana instances ? Because every Object provides an internal constructor method if we don't create one.
Let's try to add such method to the Fruct object :
function Fruct:constructor(fruct_kind, fruct_color, fruct_shape) self.kind = fruct_name self.color = fruct_color self.shape = fruct_shape end
Here, we use the constructor to initialize our instance (represented by the implicit self). We also take advantage of this constructor to add a new attribute to the instance just created : the
kind attribute will contain the kind of fruit.
Let's try to create a new apple now :
redapple = Fruct("red apple", "red", "spherical") redapple:describe() -- prints "I am a red and spherical fruct"
Great ! Let's update the describe function now that we have the new attribute
name set in the constructor :
function Fruct:describe() print("I am a "..self.kind..", a "..self.color.." and "..self.shape.." fruct") end
The Fruct object improves as we learn new concepts of object programming with Luart.
Let's continue by changing the
kind attribute of the
redapple.kind = "banana" redapple:describe() -- prints "I am a banana, a red and spherical fruct"
Let's start with a property in the Fruct object that controls the reading of an attribute :
function Fruct:get_name() return self.kind end
A property use a
getter method to read and return its value. Here we are defining a getter method for the property
This property returns the value of the
self.kind attribute when we try to read the property.
Let's try to use it :
redapple = Fruct("red apple", "red", "spherical") print(redapple.name) -- prints "red apple"
Now let's add a setter method to control write access to the
function Fruct:set_name() error("Fruct.name property is not writable) end
Here, we have prefixed the property setter method by
set_. This method is called every time we try change the value of the
But remember that we don't want it to be changed, so we throw an error to indicate it. Let's see what it gives :
redapple.name = "banana" -- error !
Encapsulation is one of the key features of object-oriented programming. Encapsulation refers to the bundling of attributes and methods inside a single object. It prevents outer objects from accessing and changing attributes and methods of another object. This also helps to achieve data hiding, like here. But wait, the self.kind attribute is still there and can be modified :
redapple.kind = "banana" print(redapple.name) -- prints "banana"
Accessing private data
function Fruct:constructor(fruct_kind, fruct_color, fruct_shape) function self:get_name() return fruct_name end self.color = fruct_color self.shape = fruct_shape end
We removed the
kind attribute in the Fruct constructor, and we defined the getter property method
get_read in the current scope of the constructor function.
There is no other way than creating a new Fruct instance to use another Fruct name :
redapple = Fruct("red apple", "red", "spherical") print(redapple.name) -- prints "red apple" redapple.name = "banana" -- error ! readapple.kind = "banana"-- we create a new field `kind` print(redapple.name) -- prints "red apple" (the get_name() getter method don't use self.kind anymore !)
Lua scope rules permits to mimic private fields by declaring getter/setter method properties inside the constructor function.
But wait a minute, as the
self.kind attribute has been removed, we need to redefine the
describe method :
function Fruct:describe() print("I am a "..self.name..", a "..self.color.." and "..self.shape.." fruct") end
We now come to the end of this tutorial. To summarize :
- The getter and setter methods on an object provide an interface for accessing an instance attribute (called a "property" in this case)
- The getter returns the value of an internal attribute
- The setter sets a new value for an internal attribute