Capítulo 16. Control de accesos
Se ha dicho anteriormente que Ruby no tiene funciones, sólo métodos. Sin embargo existe más de una clase de métodos. En esta capítulo vamos a presentar el control de accesos.
Vamos a considerar lo que pasa cuando se define un método en el “nivel superior”, no dentro de una clase. Se puede pensar que dicho método es análogo a una función de un lenguaje más tradicional como C.
ruby> def square(n)
ruby| n * n
ruby| end
nil
ruby> square(5)
25
Nuestro nuevo método parece que no pertenece a ninguna clase, pero de hecho Ruby se lo asigna a la clase Object, que es la superclase de cualquier otra clase. Como resultado de esto cualquier objeto es capaz de utilizar este método.
Esto es cierto, pero existe un pequeño pero; es un método privado a cada clase. A continuación hablaremos más de lo que esto significa, pero una de sus consecuencias es que sólo se puede llamar de la siguiente forma
ruby> class Foo
ruby| def fourth_power_of (x)
ruby| square(x) * square(x)
ruby| end
ruby| end
nil
ruby> Foo.new.fourth_power_of 10
10000
No se nos permite aplicar explícitamente el método a un objeto:
«fish».square(5)
ERR: (eval):1: private method ‘square’ called for «fish»:String
Esto preserva con inteligencia la naturaleza puramente OO de Ruby (las funciones siguen siendo métodos de objetos, donde el receptor implícito es self), a la vez que proporciona funciones que se pueden escribir de igual forma que en lenguajes tradicionales.
Una disciplina mental común en la programación OO, que ya se señaló en un capítulo anterior, tiene que ver con la separación de la especificación y la implementación o qué tareas se supone que un objeto realiza y cómo realmente se consiguen. El trabajo interno de un objeto debe mantenerse, por lo general, oculto a sus usuarios; sólo se tiene que preocupar de lo que entra y lo que sale y confiar en que el objeto sabe lo que está realizando internamente. Así, es generalmente útil que las clases posean métodos que el mundo exterior no ve, pero que se utilizan internamente (y que pueden ser mejorados por el programador cuando desee, sin modificar la forma en que los usuarios ven los objetos de esa clase). En el trivial ejemplo que sigue, piénsese que engine es el motor interno de la clase.
ruby> class Test
ruby| def times_two(a)
ruby| print a,» dos veces es «,engine(a),»\n»
ruby| end
ruby| def engine(b)
ruby| b*2
ruby| end
ruby| private:engine # esto oculta engine a los usuarios
ruby| end
Test
ruby> test = Test.new
#<Test:0x401c4230>
ruby> test.engine(6)
ERR: (eval):1: private method ‘engine’ called for #<Test:0x401c4230>
ruby> test.times_two(6)
6 dos veces es 12
nil
Se podría esperar que test.engine(6) devolviese 12, pero por el contrario se nos comunica que engine es inaccesible cuando actuamos como usuario del objeto Test. Sólo otros métodos de Test, como times_two tienen permiso para utilizar engine. Se nos obliga a pasar por el interfaz público, que es el método times_two. El programador que está al cargo de la clase puede modificar engine (en este caso cambiando b*2 por b+b suponiendo que así mejora el rendimiento) sin afectar cómo los usuarios interactúan con el objeto Test. Este ejemplo, por supuesto, es demasiado simple para ser útil; los beneficios de control de accesos se manifiestan cuando se comienzan a crear clases más complicadas e interesantes.