Referencia de clases


SUBMITTED BY: dcanoh

DATE: Jan. 12, 2017, 5:13 a.m.

FORMAT: reStructuredText

SIZE: 13.5 kB

HITS: 3209

  1. Referencia de clases
  2. ===================
  3. A modo de tutorial, en este documento trataré de explicar en qué consiste la programación orientada a objetos en Lide y las adaptaciones que se hicieron en el lenguaje base (Lua) para que el programador pueda diseñar e implementar sus propias clases o incluso modificar aspectos del framework Lide.
  4. Dentro del mundo de LUA encontramos diferentes implementaciones/adaptaciones de programación orientada a objetos, unos son proyectos conformados y otros son simples scripts que nos ayudan a interactuar con clases, subclases, métodos y propiedades dentro del lenguaje de programación.
  5. Por diversos motivos decidimos adoptar una librería pequeña pero altamente customizable y escalable que se adapta a nuestras necesidades como proyecto, dicha implementación fue llamada `yaci.lua <http://lua-users.org/wiki/YetAnotherClassImplementation>`_ (por `Julien Patte <https://github.com/jpatte>`_) la incluimos dentro del core e hicimos algunas modificaciones menores para que se adecuara a lo que nosotros queríamos.
  6. Creación de una clase:
  7. **********************
  8. Básicamente hay dos formas de definir una nueva clase:
  9. Llamando la función ``newclass()`` o usando el método ``BaseObject:subclass()``
  10. Cuando usted está creando una clase debe especificar un nombre para ella, esto no es absolutamente necesario, pero puede ser de mucha ayuda en el momento de debuggear sus aplicaciones. **Si usted no especifica ningún nombre la clase será llamada “Unnamed”.**
  11. Cuando usted use ``Object:subclass()``, la nueva clase será directamente una subclase de :ref:`Object<ClassObject>`, por otro lado ``newclass()`` acepta un segundo argumento, el cual será otra superclase para su objeto, si usted no especifica ninguna superclase, la :ref:`clase Object<ClassObject>` será elegida.
  12. .. note::
  13. Esto quiere decir que todas las clases son subclases de :ref:`Object<ClassObject>`.
  14. Un ejemplo puede ilustrarlo mejor:
  15. .. code-block:: lua
  16. -- 'Humano' es una subclase de 'BaseObject'
  17. Humano = newclass("Humano")
  18. -- 'Ingeniero' es una subclase de 'Humano'
  19. Ingeniero = newclass("Ingeniero", Humano)
  20. -- 'Estudiante' es otra subclase de 'Humano'
  21. Estudiante = Humano:subclass("Estudiante")
  22. Definición de nuestra clase:
  23. ****************************
  24. Constructor
  25. +++++++++++
  26. Naturalmente nuestras clases pueden definir un constructor, o no…
  27. La forma de definir nuestros constructores es la siguiente:
  28. .. code-block:: lua
  29. function Humano:Humano(nombre, edad)
  30. self.nombre = nombre
  31. self.edad = edad
  32. end
  33. function Ingeniero:Ingeniero(nombre, edad, titulo)
  34. self.super:init(nombre, edad) --> Nótese que esto llama el constructor de la superclase
  35. self.titulo = titulo
  36. end
  37. .. important::
  38. Las subclases pueden llamar el constructor de sus superclases a través del campo super ``self.super:init()``.
  39. Nótese que ``Object:init()`` existe sin embargo, se recomienda nunca usarlo.
  40. Métodos
  41. +++++++
  42. Los métodos pueden ser definidos de una manera muy sencilla:
  43. .. code-block:: lua
  44. function Humano:Saludar()
  45. print(Hola Mundo)
  46. end
  47. function Ingeniero:Leer( libro )
  48. print(Leyendo: ..libro)
  49. end
  50. Eventos lua (meta-métodos)
  51. ++++++++++++++++++++++++++
  52. No confundir éstos eventos con la *clase Event*, éstos eventos corresponden a las interacciones entre los objetos dentro del lenguaje de programación, algunos de éstos pueden ser: ``__tostring``, ``__add``, ``__eq``.
  53. Para más información sobre meta-methods y meta-tables en Lua véase la referencia del lenguaje.
  54. Usted también puede definir eventos para las instancias de la clase, exactamente de la misma manera que define los métodos:
  55. .. code-block:: lua
  56. function Humano:__tostring ()
  57. return Un Humano llamado: .. self.nombre .. , que tiene .. self.edad .. años.
  58. end
  59. function Ingeniero:__tostring()
  60. return Un Ingeniero de .. self.titulo .. llamado: .. self.nombre .. , que tiene .. self.edad .. años.
  61. end
  62. Cualquier evento puede ser usado, exceptuando ``__index`` y ``__newindex`` los cuales son necesarios para el funcionamiento de la librería.
  63. Usted puede usar esta característica para definir operadores como: ``__add``, ``__eq``, etc. ``__tostring`` es un evento realmente útil, la clase :ref:`Object<ClassObject>` implementa una versión estándar para ella que simplemente retorna "a xxx" donde 'xxx' es el nombre de la clase de dicha instancia.
  64. Instanciación
  65. +++++++++++++
  66. Toda clase tiene el método ``new()``, usado para la instanciación. Todos los argumentos que pasemos a éste métodos son pasados al constructor:
  67. .. code-block:: lua
  68. Anthony = Humano:new (Anthony, 33)
  69. Camila = Ingeniero:new (Camila, 21, Electrónica)
  70. El resultado es el mismo que si usted “llama” las clases directamente:
  71. .. code-block:: lua
  72. Julieth = Humano (Julieth, 13)
  73. Jefferson = Ingeniero (Jefferson, 23, Sistemas)
  74. Métodos de las clases
  75. +++++++++++++++++++++
  76. Así como ``subclass()`` y ``new()``, las clases tienen algunos otros métodos:
  77. * ``inherits()`` Puede ser usado para chequear si una clase hereda de otra clase:
  78. Por ejemplo: ``Ingeniero:inherits(Humano)`` retorna ``true``, y ``Estudiante:inherits(Ingeniero)`` retorna ``false``. (Generalmente usado para propósitos internos)
  79. * ``name()`` Retorna el nombre de la clase (El que usted especifico cuando la creó).
  80. * ``super()`` Retorna la superclase.
  81. * ``made()`` Es usado para chequear si una instancia implementa ésta clase o no.
  82. Por ejemplo, ``Humano:made(Anthony)`` retorna true Mientras que ``Estudiante:made(Jefferson)`` retorna ``false``.
  83. * ``virtual()`` Es usado para declarar métodos abstractos y virtuales explícitamente, ver abajo.
  84. * ``cast()`` & ``trycast()`` son usados para casting. Ver abajo para más detalles.
  85. Ejecución
  86. *********
  87. Métodos de las instancias
  88. +++++++++++++++++++++++++
  89. Todas las instancias permiten accesar a las variables definidas en el constructor de su clase (y de sus superclases). Ellos también tienen un método ``class()`` que retorna la clase, y un campo ``super`` que es usado para acceder a la superclase por si usted sobrescribió el método, veamos:
  90. .. code-block:: lua
  91. A = newclass("A")
  92. function A:test() print(self.a) end
  93. A:virtual("test") -- declare test() as being virtual; see below
  94. function A:init(a) self.a = a end
  95. B = newclass("B", A)
  96. function B:test() print(self.a .. "+" .. self.b) end
  97. function B:init(b) self.super:init(5) self.b = b end
  98. b = B:new(3)
  99. b:test() -- prints "5+3"
  100. b.super:test() -- prints "5"
  101. print(b.a) -- prints "5"
  102. print(b.super.a) -- prints "5"
  103. Los miembros de la superclase son creados (e inicializados) cuando el método ``self.super:init()`` es llamado. Usted generalmente debe llamar este método al principio del constructor para inicializarlo. Nótese que b es una instancia de ``B``, ``b.super`` es simplemente una instancia de ``A`` (entonces tenga cuidado, aquí ``super`` es dinámico, no estático).
  104. Variables estáticas
  105. +++++++++++++++++++
  106. Cada vez que usted define un nuevo método para una clase, éste es registrado en una tabla ``static``; de esta manera nosotros no vamos a mezclar los métodos de las clases con los servicios de las clases. Ésta tabla es accesible mediante el campo ``static``. Esto generalmente permite acceso a variables estáticas en las clases, por ejemplo:
  107. .. code-block:: lua
  108. A = newclass("A")
  109. function A:init(a) self.a = a end
  110. A.test = 5 -- a static variable in A
  111. a = A(3)
  112. prints(a.a) -- prints 3
  113. prints(a.test) -- prints 5
  114. prints(A.test) -- prints nil (!)
  115. prints(A.static.test) -- prints 5
  116. Métodos virtuales
  117. +++++++++++++++++
  118. Los métodos de las clases no son virtuales por defecto, lo que quiere decir que ellos no son implícitamente sobre-escritos por potenciales implementaciones de las subclases. Para declarar un método como virtual usted tiene que declararlo explícitamente usando el método ``virtual()`` de su clase. La llamada a ``virtual()`` debe estar escrita fuera de cualquier método, y antes de la definición del método:
  119. .. code-block:: lua
  120. A = newclass("A")
  121. function A:whoami()
  122. return "A"
  123. end
  124. A:virtual("whoami") -- whoami() is declared virtual
  125. function A:test()
  126. print(self:whoami())
  127. end
  128. B = newclass("B", A)
  129. function B:whoami()
  130. return "B"
  131. end
  132. -- no need to use B:virtual() here
  133. myB = B()
  134. myB:test() -- prints "B"
  135. Con esto también es posible declarar algunos métodos como abstractos (p.e. métodos puramente virtuales); usted solo tiene que llamar ``A:virtual()`` con el nombre del método sin definirlo.
  136. Un error ocurrirá si usted intenta llamarlo sin definirlo antes en la jerarquía.
  137. Aquí un ejemplo:
  138. .. code-block:: lua
  139. A = newclass("A")
  140. A:virtual("whoami") -- whoami() is an abstract method
  141. function A:test()
  142. print(self:whoami())
  143. end
  144. B = newclass("B", A)
  145. function B:whoami() -- define whoami() here
  146. return "B"
  147. end
  148. myB = B()
  149. myB:test() -- will print "B"
  150. myA = A() -- no error here!
  151. myA:test() -- but will raise an error here
  152. Atributos privados
  153. ++++++++++++++++++
  154. Por defecto, las subclases heredan todos los métodos y todos los atributos definidos por su(s) clase(s) padre. Esto puede llevar a algunas confusiones cuando definimos atributos que comparten el mismo nombre en diferentes niveles en la jerarquía:
  155. .. code-block:: lua
  156. A = newclass("A")
  157. function A:init()
  158. self.x = 42 -- define an attribute here for internal purposes
  159. end
  160. function A:doSomething()
  161. self.x = 0 -- change attribute value
  162. -- do something here...
  163. end
  164. B = A:subclass("B")
  165. function B:init(x)
  166. self.super:init() -- call the superclass's constructor
  167. self.x = x -- B defines an 'x' attribute. Problem: 'x' is actually already defined by A!
  168. end
  169. function B:doYourJob()
  170. self.x = 5
  171. self.doSomething()
  172. print(self.x) -- prints "0": 'x' has been modified by A because A defined it first
  173. end
  174. Es posible definir atributos privados en una clase dependiendo del orden en que esos atributos son inicializados.
  175. Nótese que “privado” no es el mejor término para definirlo aquí (porque éste no es un mecanismo de protección real); yo preferiría hablar de atributo “compartido” y “no compartido” entre las clases y sus subclases.
  176. Usted también notará que esta distinción está hecha por la misma subclase (y no por la superclase), la cual puede decidir (en su constructor) qué atributos de la superclase pueden ser eventualmente heredados desde la superclase o sobrescritos privadamente.
  177. Por ley usted casi siempre definirá los atributos de la clase antes de llamar el constructor de su superclase.
  178. Vamos a ver éste ejemplo con un pequeño cambio en ``B:init()``:
  179. .. code-block:: lua
  180. A = newclass("A")
  181. function A:init()
  182. self.x = 42 -- define an attribute here for internal purposes
  183. end
  184. function A:doSomething()
  185. self.x = 0 -- change attribute value
  186. -- do something here...
  187. end
  188. B = A:subclass("B")
  189. function B:init(x)
  190. self.x = x -- B defines a private 'x' attribute
  191. self.super:init() -- call the superclass's constructor
  192. end
  193. function B:doYourJob()
  194. self.x = 5
  195. self.doSomething()
  196. print(self.x) -- prints "5": 'x' has not been modified by A
  197. print(self.super.x) -- prints "0": this is the 'x' attribute that was used by A
  198. end
  199. Como usted puede ver los diferentes behaviors de los atributos ``x`` y ``y`` vienen en el orden de inicialización en el constructor.
  200. La primera clase que define un atributo va a obtener la posesión de ese atributo, even si algunas superclases declaran un atributo con el mismo nombre “después” en el proceso de inicialización.
  201. Yo personalmente sugiero inicializar todos los atributos “no compartidos” al inicio del constructor, luego llamar el constructor de la superclase, then eventually use some of the superclass' methods. Por el contrario si usted quiere acceder a un atributo definido por una superclase no establezca este valor antes de que el constructor de la superclase has done it.
  202. Castings
  203. ++++++++
  204. Los Castings son muy útiles si usted necesita acceder a un método (no virtual) desde un método localizado más arriba en la jerarquía de clases. Esto puede hacerse con los métodos ``cast()`` y ``trycast()`` de todas las clases. Aquí un simple ejemplo:
  205. .. code-block:: lua
  206. A = newclass("A")
  207. function A:foo()
  208. print(self.x) -- prints "nil"! There is no field 'x' at A's level
  209. selfB = B:cast(self) -- explicit casting into a B
  210. print(selfB.x) -- prints "5"
  211. end
  212. B = newclass("B",A)
  213. function B:init(x)
  214. self.x = x
  215. end
  216. myB = B(5)
  217. myB:foo()
  218. C:cast(x)
  219. Intenta buscar el sub-objeto o super-objeto en ``x`` correspondiente a la ``clase C``, Buscando arriba y abajo en la jerarquía. Intuitivamente nosotros vamos a obtener ``myB.super == A:cast(myB)`` y ``myB == B:cast(myB.super)``.
  220. Por supuesto que esto funciona con mas de dos niveles de herencia. Si el casting falla ocurrirá un error.
  221. ``C:trycast(x)`` hace exactamente lo mismo excepto que ésto simplemente retorna ``nil`` cuando el casting es imposible en vez de ocurrir un error.

comments powered by Disqus