Subversion checkout URL

Private stuff

Pages 6

Clone this wiki locally

There are several ways you can make private parameters with middleclass.

Underscoring

The simplest one is just to precede your attributes with underscores. This is actually written on the Lua 5.1 reference, section 2.1, “Lexical conversions”, as a way to say “this is here, but please don’t use it”.

Private class attributes

In general, the way of “really” getting hidden functions or variables consists on using Lua’s scoping rules. The simplest way of using this is creating each of your classes on separate files, and then declaring any private variable or functions as local, on the “root” scope of the file.

Let me explain what happens here. The _internalClassCounter = 4 line is, in reality, creating a new global variable called internalClassCounter, and assigning it 4. The “really internal” one is “out of reach” on main.lua (unless someone does really tricky stuff with the environments). So getCount() works as expected.

Private instance methods

It is also possible to declare private methods. The trick here is not to “include” them on the class definition. On the following example, we will not declare it on Class3:secretMethod; instead we’ll create a local function. Since we’re not using the : operator any more, we have to make the “self” parameter explicit. Also, since we have to make it local, we have to deviate from the “usual” way of declaring Lua functions (the “usual” way of declaring functions makes them global):

-- File 'MyClass3.lua'local class =require('middleclass')
MyClass3 =class('MyClass3')
local _secretMethod =function(self) -- notice the 'local' at the beginning, the = function and explicit self parameterreturn( 'My name is '..self.name..' and I have a secret.' )
endfunction MyClass3:initialize(name)
self.name= name
endfunction MyClass3:shout()
print( _secretMethod(self) ..' You will never know it!' )
end

-- File 'Main.lua'require('MyClass3')
peter = MyClass3:new('peter')
peter:shout() -- My name is peter and I have a secret. You will never know it!print(_secretMethod(peter)) -- throws an error - _secretMethod is nil here.

This technique also allows the creation of private class methods. In MiddleClass, there’s really no difference between class methods and instance methods; the difference comes from what you pass to their ‘self’ parameter. So if you invoke _secretMethod like this: _secretMethod(MyClass3) it will be a class method.

A slightly more efficient way of creating a class method would be getting rid of the ‘self’ parameter and use MyClass3 directly on the method’s body:

MyClass3 =class('MyClass3')
local _secretClassMethod =function() -- self parameter outreturn( 'My name is '.. MyClass3.name..' and I have a secret.' ) -- use MyClass3 directly.end-- Note that this alternative is only recommended for private class methods. Public class methods should follow the convention of adding one explicit 'class' parameter:
MyClass3 =class('MyClass3')
function MyClass3.classMethod(theClass)
return( 'Being a public class named '.. theClass.name..' is not a bad thing.' )
end

This gives a bit more of flexibility when overriding public class methods on subclasses.

Finally, a subtle point regarding recursive private methods. If you need to create a private method that calls himself, you will need to declare the variable first, and then (on the next line) initialize it with the function value. Otherwise the variable will not be available when the function is created

Private Instance attributes

Instance attributes are a little bit trickier to implement, since we only have one scope to “hide stuff in”, and it has to cope with multiple instances.

One way to do this is using one private class variable as a ‘stash’. If you use one table instead of just a number, you can and hide there all the private information you may need. One problem with this approach is that you need to come out with a ‘key’ per ‘instance’.

Fortunately this is a very simple thing to do, since in lua you can use nearly any type of object as a key – So you can use the instances themselves as keys. In other words, we use ‘self’ as a key.

One problem with this approach is that instances might not be liberated by the garbage collector once they are not used any more (since there’s a reference to them on the ‘stash’ keys). In order to avoid this, we can make the ‘stash’ a weak table.

On the following example, the name attribute is public, but age and gender are private.

Our ‘secret stash’ in the following example will be called _private.

-By the way, the following example also shows how you can do “read-only-ish attributes”: you make them private, and make getters for them, but not setters.

-- File 'main.lua'require('MyClass4')
stewie = MyClass4:new('stewie', 2, 'male')
print(stewie:getName()) -- stewie
stewie.name='ann'print(stewie.name) -- annprint(stewie:getAge()) -- 2
stewie.age=14-- this isn't really modifying the age... it is creating a new public attribute called 'age'-- the internal age is still unalteredprint(stewie:getAge()) -- 2-- the newly created external age is also available.print(stewie.age) -- 14-- same goes with gender:print(stewie:getGender()) -- 'male'
stewie.gender='female'print(stewie:getGender()) -- 'male'print(stewie.gender) -- 'female'

Private members on the same file

There’s also a way of creating private members that other classes/methods on the same file can’t access, if you ever had the need.

Just create an artificial scope with do … end, and declare private members as ‘local’ inside that block. Only the methods inside that block will have access to them:

-- File 'MyClass3.lua'local class =require('middleclass')
MyClass3 =class('MyClass3')
function MyClass3:initialize(name)
self.name= name
enddolocal secretMethod =function(self) -- notice the explicit self parameter here.return( 'My name is '..self.name..' and I have a secret.' )
endfunction MyClass3:shout()
print( secretMethod(self) ..' You will never know it!' )
endend-- functions outside the do-end will not 'see' secretMethod, but they will see MyClass3.shout (because they see MyClass3)