I’m getting my head around using classes/objects using the inbuilt approach offered by LuaBind. However because Lua is so flexible with how you do things and doesn’t restrict you, I’m not sure I’m taking the right approach.
Consider the following code:
class 'Farm'
function Farm:__init()
self.num_chickens = 0
end
function Farm:Add_Chicken()
self.num_chickens = self.num_chickens + 1
return Chicken(self)
end
class 'Chicken'
function Chicken:__init(Farm)
self.host_farm = Farm
self.mother = false
end
function Chicken:Lay_Egg()
return Egg(self)
end
class 'Egg'
function Egg:__init(Chicken)
self.parent = Chicken
end
function Egg:Hatch()
self.parent.mother = true
return self.parent.host_farm:Add_Chicken()
end
-- Testing
old_mcdonald = Farm()
chicken = old_mcdonald:Add_Chicken()
egg = chicken:Lay_Egg()
new_chicken = egg:Hatch()
print(old_mcdonald.num_chickens) -- Should be 2
print(chicken.mother) -- Should be true
print(new_chicken.mother) -- Should be false
While this works, is this the right way of doing things?
It doesn’t seem right to me that that the ‘egg’ object should change the states of ‘chicken’ and ‘farm’ objects, but I can’t think of a better way to achieve the same outcome.
Just interested for my own understanding and wonder if any seasoned programmers could offer a view?
It doesn’t seem right to me that that the ‘egg’ object should change the states of ‘chicken’ and ‘farm’ objects¨
Indeed it’s does not seem right that newly arrived eggs should notify the farms management. Seems a bit too bureaucratic
Did you consider observable values/events? Basically, that the chicken laying the egg is an event that will notify the farm.
Of course, in code this requires that the farm register all the chickens, using some notifier method. But then this is also fixing what I see as a shortcoming for the code (you know the number of chickens but can not access any of them).
Ah yes, that would be better, so you would have a table in the Farm object that contained chickens and eggs…
I didn’t think of notifiers actually, so in real world terms this would be analogous to the farm worker regularly checking for eggs. How would you implement this? would you attach a ‘check for new eggs’ function to the app idle observable.
I suppose that would be less efficient than the previous method but less bureaucratic, I would prefer my Renoise Farm Simulatorto be free spirited
How would you implement this? would you attach a ‘check for new eggs’ function to the app idle observable
Idle notifier should really only be used when you can’t work you way around the issue in other ways. Instead I would use the ObservableBang for this purpose.
Add it to the Chicken class and call it something like “on_egg_hatch”. Then, in the eggs Hatch method you can invoke it by calling self.parent.on_egg_hatch:bang()
To keep track of things, the Farm class would need to store Chicken instances in a table. Once created, you can then attach to the on_egg_hatchobservable, using on_egg_hatch:add_notifier (and some function that deals with this wonderful event).
To keep track of things, the Farm class would need to store Chicken instances in a table. Once created, you can then attach to the on_egg_hatchobservable, using on_egg_hatch:add_notifier (and some function that deals with this wonderful event).
Ok, great, after a bit of head scratching I get it.
So this works:
class 'Farm'
function Farm:__init()
self.chickens = {}
end
function Farm:Add_Chicken()
local chicken_index = #self.chickens+1
self.chickens[chicken_index] = Chicken()
self.chickens[chicken_index].on_egg_hatch:add_notifier( self.On_Egg_Hatch )
end
function Farm:On_Egg_Hatch()
print("An egg has hatched!")
end
class 'Chicken'
function Chicken:__init()
self.on_egg_hatch = renoise.Document.ObservableBang()
end
function Chicken:Lay_Egg()
return Egg(self)
end
class 'Egg'
function Egg:__init(Chicken)
self.parent = Chicken
end
function Egg:Hatch()
self.parent.on_egg_hatch:bang()
end
-- Testing
old_mcdonald = Farm()
old_mcdonald:Add_Chicken()
egg = old_mcdonald.chickens[1]:Lay_Egg()
egg:Hatch()
However this doesn’t work if I try and call further Farm methods in the notifier function, probably because the notifier function has to be referenced using ‘self.On_Egg_Hatch’ rather than ‘self:On_Egg_Hatch()’
Therefore if I try this as the notifier function it doesn’t work:
function Farm:On_Egg_Hatch()
print("An egg has hatched!")
self:Add_chicken()
end
It’s a matter of scope. The add_notifier function accepts two arguments, an object (which acts as scope) and the function. Since you’re only passing the function, the scope is missing.
Method #1: specify the scope as an additional argument
But this approach becomes problematic if you need toremove the notifiers at a later stage. Anonymous functions leaveno reference that we can use. For that reason, the first approach is probably always the best.
Thank you very much, this has advanced my understanding a lot. Have always struggled to get my head around the Document API in the past and this helps a fair bit.
So how is this approach for cpu overhead? I’ve only ever used notifiers on renoise.song() _observable objects previously and if I remember correctly, create too many of those and things get a bit sluggish. Does the same apply for this approach? Y’know in case I want to scale up operations to industrial chicken farming.