Class Features

This section presents some advanced features and uses of the LOOP models.

Class Changes

As stated previously, LOOP models are devised mainly to be dynamic therefore it is possible to apply changes over classes. In fact, every model allows that changes over classes may be reflected over all instances. For example, if a field of a class is changed, the new value is available to all instances automatically. This is particularly true in the cached and scoped model that even though they use caches of inherited fields, the use of class proxies to intercept changes over classes allows that caches can be updated.

Class Invocations

Method calls over classes of LOOP models implies function calls with the self parameter set to the class object. This way, changes applied to the self parameter implies changes on the class object and therefore reflects over all class instances. For example, suppose the class Date presented on section Basic Concepts, then the code below

Date:addyears(100) -- changes the default values of
 -- field 'year' from 1900 to 2000
will change the default value of the field year to 2000 so any Date instance that does not override the value of field year will reflect this change.

Interoperability

All LOOP models are interoperable, that is, a class from one model can be used as a base class of another and objects from different models can be mixed together to compose an application. This is possible because a LOOP class is seen as a plain Lua table that is used to retrieve field values.

However there are some drawbacks. For instance, classes from models other than the scoped model only inherits public members from classes of model scoped. Other limitation is that cached and scoped class fields always override the fields of classes from other models, no matter their order in the list of super-classes. For example, consider the following code.

local BaseSuper = loop.base.class { modelname = "simple" }
local CachedSuper = loop.cached.class{ modelname = "cached" }
local MultipClass = loop.multiple.class({}, BaseSuper, CachedSuper)
local CachedClass = loop.cached.class ({}, BaseSuper, CachedSuper)
local nocache = MultipClass()
local cached = CachedClass()
print(nocache.modelname) --> simple print(cached.modelname) --> cached

The nocache object does not use a cache of fields, so it looks up the class hierarchy to find the value of field modelname and finds it in the BaseSuper class. However, the cached object uses a cache of inherited fields that only contains fields from cached classes (i.e. cached and scoped models) therefore it finds the value of field modelname provided by class CachedSuper even though it is after the BaseSuper class in the list of super-classes of CachedClass.

Packaged Classes

LOOP classes can be packaged using the module function provided by Lua 5.1. The simplest way to pack classes using the module function is using the class constructor as a module option. For example, consider que code below.

local string = require "string"
local oo = require "loop.base"
module("chrono.Date", oo.class)
day = 1
month = 1
year = 1900
function addyears(self, years)
 self.year = self.year + years end function __tostring(self)
 return string.format("%d/%d/%d",
 self.month,
 self.day,
 self.year)
end

However, this approach does not allow to define super-classes. To avoid this, use the module variable _M as the class definition table. For example, consider the following class.

local oo = require "loop.simple"
module "chrono.SumDate"
oo.class(_M, require("chrono.Date"))
function __add(self, other)
 assert(oo.instanceof(one, _M) and
 oo.instanceof(other, _M))
 -- missing implementation ...
end function __sub(self, other)
 assert(oo.instanceof(one, _M) and
 oo.instanceof(other, _M))
 -- missing implementation ...
end

The classes packaged using the module function can be used like in the following code.

local SimpleDate = require "chrono.Date"
local AddingDate = require "chrono.SumDate"
local mydate1 = SimpleDate()
local mydate2 = AddingDate()

Super-Class Access

Most LOOP models provide functions for introspection of the class hierarchy, allowing the application to get the super-class of a particular class. However, the access to super-class in method implementations can be cumbersome in some situations. One main problem is that a class method is usually a plain Lua function and therefore is not bound to a particular class. This way, there is no simple way to determine the class of a method in order to figure out its super-class and the inherited method implementation. The most straightforward solution is to place super-classes in local variables and access the inherited methods using the class stored in that variable, like in the example of the following code.

local Square = oo.class()
function Square:draw()
 self.canvas:setcolor(self.color)
 self.canvas:rect(self.xpos - self.width /2,
 self.ypos - self.height/2,
 self.xpos + self.width /2,
 self.ypos + self.height/2)
end local Button = oo.class({}, Square)
function Button:draw()
 Square.draw(self) -- calling inherited method
 self.canvas:setcolor(self.labelcolor)
 self.canvas:setfontsize(self.labelsize)
 local tw, th = self.canvas:textdim(self.label)
 self.canvas:text(self.xpos - tw/2,
 self.ypos - th/2,
 self.label )
end

The first line of method Button:draw() calls the method Square:draw() over the object instance using the value stored in upvalue Square to retrieve the implementation of operation draw available in class Square. However, this explicit reference to the super-class Square may be troublesome in the scenario of maintaining the code in face of changes on the class hierarchy. To avoid such problem the acquisition of the super-class can be done through the Button class stored in variable Button, like in the code below.

local Button = oo.class({}, Square)
function Button:draw()
 oo.superclass(Button).draw(self) -- calling inherited method
 self.canvas:setcolor(self.labelcolor)
 self.canvas:setfontsize(self.labelsize)
 local tw, th = self.canvas:textdim(self.label)
 self.canvas:text(self.xpos - tw/2,
 self.ypos - th/2,
 self.label )
end

Copyright (C) 2004-2008 Tecgraf, PUC-RioThis project is currently being maintained by Tecgraf at PUC-Rio.