
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 2000will 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.