hump/docs/class.rst

354 lines
9.1 KiB
ReStructuredText
Raw Normal View History

2015-10-12 06:21:31 +00:00
hump.class
==========
::
Class = require "hump.class"
A small, fast class/prototype implementation with multiple inheritance.
Implements `class commons <https://github.com/bartbes/Class-Commons>`_.
**Example**::
Critter = Class{
init = function(self, pos, img)
self.pos = pos
self.img = img
end,
speed = 5
}
function Critter:update(dt, player)
-- see hump.vector
local dir = (player.pos - self.pos):normalize_inplace()
self.pos = self.pos + dir * Critter.speed * dt
end
function Critter:draw()
love.graphics.draw(self.img, self.pos.x, self.pos.y)
end
List of Functions
-----------------
* :func:`Class.new() <Class.new>`
* :func:`class.init(object, ...) <class.init>`
* :func:`Class:include(other) <Class:include>`
* :func:`class:clone() <class:clone>`
2015-10-12 06:21:31 +00:00
Function Reference
------------------
2016-08-04 01:55:46 +00:00
.. function:: Class.new()
Class.new({init = constructor, __includes = parents, ...})
2015-10-12 06:21:31 +00:00
:param function constructor: Class constructor. Can be accessed with ``theclass.init(object, ...)``. (optional)
:param class or table of classes parents: Classes to inherit from. Can either be a single class or a table of classes. (optional)
:param mixed ...: Any other fields or methods common to all instances of this class. (optional)
:returns: The class.
Declare a new class.
``init()`` will receive the new object instance as first argument. Any other
arguments will also be forwarded (see examples), i.e. ``init()`` has the
following signature::
function init(self, ...)
If you do not specify a constructor, an empty constructor will be used instead.
The name of the variable that holds the module can be used as a shortcut to
``new()`` (see example).
**Examples**::
Class = require 'hump.class' -- `Class' is now a shortcut to new()
-- define a class class
Feline = Class{
init = function(self, size, weight)
self.size = size
self.weight = weight
end;
-- define a method
stats = function(self)
return string.format("size: %.02f, weight: %.02f", self.size, self.weight)
end;
}
-- create two objects
garfield = Feline(.7, 45)
felix = Feline(.8, 12)
print("Garfield: " .. garfield:stats(), "Felix: " .. felix:stats())
::
Class = require 'hump.class'
-- same as above, but with 'external' function definitions
Feline = Class{}
function Feline:init(size, weight)
self.size = size
self.weight = weight
end
function Feline:stats()
return string.format("size: %.02f, weight: %.02f", self.size, self.weight)
end
garfield = Feline(.7, 45)
print(Feline, garfield)
::
Class = require 'hump.class'
A = Class{
foo = function() print('foo') end
}
B = Class{
bar = function() print('bar') end
}
-- single inheritance
C = Class{__includes = A}
instance = C()
instance:foo() -- prints 'foo'
instance:bar() -- error: function not defined
-- multiple inheritance
D = Class{__includes = {A,B}}
instance = D()
instance:foo() -- prints 'foo'
instance:bar() -- prints 'bar'
::
-- class attributes are shared across instances
A = Class{ foo = 'foo' } -- foo is a class attribute/static member
one, two, three = A(), A(), A()
print(one.foo, two.foo, three.foo) --> prints 'foo foo foo'
one.foo = 'bar' -- overwrite/specify for instance `one' only
print(one.foo, two.foo, three.foo) --> prints 'bar foo foo'
A.foo = 'baz' -- overwrite for all instances without specification
print(one.foo, two.foo, three.foo) --> prints 'bar baz baz'
.. function:: class.init(object, ...)
:param Object object: The object. Usually ``self``.
:param mixed ...: Arguments to pass to the constructor.
:returns: Whatever the parent class constructor returns.
Calls class constructor of a class on an object.
Derived classes should use this function their constructors to initialize the
parent class(es) portions of the object.
**Example**::
Class = require 'hump.class'
Shape = Class{
init = function(self, area)
self.area = area
end;
__tostring = function(self)
return "area = " .. self.area
end
}
Rectangle = Class{__includes = Shape,
init = function(self, width, height)
Shape.init(self, width * height)
self.width = width
self.height = height
end;
__tostring = function(self)
local strs = {
"width = " .. self.width,
"height = " .. self.height,
Shape.__tostring(self)
}
return table.concat(strs, ", ")
end
}
print( Rectangle(2,4) ) -- prints 'width = 2, height = 4, area = 8'
.. function:: Class:include(other)
:param tables other: Parent classes/mixins.
:returns: The class.
Inherit functions and variables of another class, but only if they are not
already defined. This is done by (deeply) copying the functions and variables
over to the subclass.
.. note::
``class:include()`` doesn't actually care if the arguments supplied are
hump classes. Just any table will work.
.. note::
You can use ``Class.include(a, b)`` to copy any fields from table ``a``
to table ``b`` (see second example).
**Examples**::
Class = require 'hump.class'
Entity = Class{
init = function(self)
GameObjects.register(self)
end
}
Collidable = {
dispatch_collision = function(self, other, dx, dy)
if self.collision_handler[other.type])
return collision_handler[other.type](self, other, dx, dy)
end
return collision_handler["*"](self, other, dx, dy)
end,
collision_handler = {["*"] = function() end},
}
Spaceship = Class{
init = function(self)
self.type = "Spaceship"
-- ...
end
}
-- make Spaceship collidable
Spaceship:include(Collidable)
Spaceship.collision_handler["Spaceship"] = function(self, other, dx, dy)
-- ...
end
::
-- using Class.include()
Class = require 'hump.class'
a = {
foo = 'bar',
bar = {one = 1, two = 2, three = 3},
baz = function() print('baz') end,
}
b = {
foo = 'nothing to see here...'
}
Class.include(b, a) -- copy values from a to b
-- note that neither a nor b are hump classes!
print(a.foo, b.foo) -- prints 'bar nothing to see here...'
b.baz() -- prints 'baz'
b.bar.one = 10 -- changes only values in b
print(a.bar.one, b.bar.one) -- prints '1 10'
.. function:: class:clone()
:returns: A deep copy of the class/table.
Create a clone/deep copy of the class.
.. note::
You can use ``Class.clone(a)`` to create a deep copy of any table (see
second example).
**Examples**::
Class = require 'hump.class'
point = Class{ x = 0, y = 0 }
a = point:clone()
a.x, a.y = 10, 10
print(a.x, a.y) --> prints '10 10'
b = point:clone()
print(b.x, b.y) --> prints '0 0'
c = a:clone()
print(c.x, c.y) --> prints '10 10'
::
-- using Class.clone() to copy tables
Class = require 'hump.class'
a = {
foo = 'bar',
bar = {one = 1, two = 2, three = 3},
baz = function() print('baz') end,
}
b = Class.clone(a)
b.baz() -- prints 'baz'
b.bar.one = 10
print(a.bar.one, b.bar.one) -- prints '1 10'
Caveats
-------
Be careful when using metamethods like ``__add`` or ``__mul``: If a subclass
inherits those methods from a superclass, but does not overwrite them, the
result of the operation may be of the type superclass. Consider the following::
Class = require 'hump.class'
A = Class{init = function(self, x) self.x = x end}
function A:__add(other) return A(self.x + other.x) end
function A:show() print("A:", self.x) end
B = Class{init = function(self, x, y) A.init(self, x) self.y = y end}
function B:show() print("B:", self.x, self.y) end
function B:foo() print("foo") end
B:include(A)
one, two = B(1,2), B(3,4)
result = one + two -- result will be of type A, *not* B!
result:show() -- prints "A: 4"
result:foo() -- error: method does not exist
Note that while you can define the ``__index`` metamethod of the class, this is
not a good idea: It will break the class mechanism. To add a custom ``__index``
metamethod without breaking the class system, you have to use ``rawget()``. But
beware that this won't affect subclasses::
Class = require 'hump.class'
A = Class{}
function A:foo() print('bar') end
function A:__index(key)
print(key)
return rawget(A, key)
end
instance = A()
instance:foo() -- prints foo bar
B = Class{__includes = A}
instance = B()
instance:foo() -- prints only foo