An elegant way to make tables act like values ​​when __indexed?

I got this problem with my homebrew OO design:

Entity = {}
function Entity:new(o)
  o = o or {}
  return setmetatable(o, {__index = Entity})
end
function Entity:init() end
function Entity:think() end
function Entity:spawn()
  --put in entity pool and begin drawing/logic
  self:init()
end

Block = Entity:new{
  x = 0,
  y = 0,
  color = {255, 255, 255, 255},
}
function Block:getPos()
  return self.x, self.y
end
--setPos, getColor, setColor etc
function Block:init()
  self:setColor(math.random(255), math.random(255), math.random(255))
end

a = Block:new()
a:spawn() --a new block with a random color
--a few seconds later...
b = Block:new()
b:spawn() --all blocks change to new color

      

The table color

is shared by all prototypes and instances. How can I get this table to behave like a line:

a = {table}
b = a
print(b[1]) -->table
a[1] = "object"
print(a[1], b[1]) -->object, table

      

Unlike an object:

a = {table}
b = a
print(b[1]) -->table
a[1] = "object"
print(a[1], b[1]) -->object, object

      

TL; DR: I need to create a new datatype.

+3


source to share


2 answers


There are three ways to fix your problem:



  • Initialize a table Entity.color

    during object initialization Entity

    - place it in a function Entity:new()

    .
  • Replace the table Entity.color

    with four variables to represent its contents - Entity.colorred

    , Entity.colorgreen

    , Entity.colorblue

    , Entity.coloralpha

    .
  • Do Entity:setColor()

    create a new table self.color

    with new values ​​instead of directly changing the values. self.color = {red, green, blue, alpha}

    instead of self.color[1] = red; self.color[2] = green; self.color[3] = blue; self.color[4] = alpha

    .
+2


source


How can I get this table to behave like a line:

Your example uses Lua variable assignment to copy values, not references. Even if you can influence such a change in Lua (you cannot), it would be an unspeakably terrible idea.

The color table is shared by all prototypes and instances.

Try to put it in a prototype (like "class"), so it is equivalent to a static OOP member that is used by all instances. If you want this to be an instance variable, it must be part of the instance construction, not the class construction. You must do the same for x

and y

and for all instances Block

.



function Block:new()
  local instance = { x = 0, y = 0 }
  instance:setColor(math.random(255), math.random(255), math.random(255))
  return setmetatable(instance, {__index = self})
end

      

Improvements can be added to the constructor, such as passing parameters (for example, for initialization x

, y

etc.), but the important part is that the instance carries its own state.


Having Entity:spawn

just call init on its own doesn't make sense. The example code you show indicates that it should in fact create new instances, but the implementation does not.

0


source







All Articles