|
|
Line 77: |
Line 77: |
|
| |
|
| Gomix" | | Gomix" |
|
| |
| * Métodos públicos: pueden ser invocados por cualquiera, no se aplica ningún control de acceso. Los métodos son públicos por omisión excepto initiliaze que siempre es privado.
| |
| * Métodos protegidos: sólo pueden ser invocados por objetos de la misma clase o subclases. El acceso se mantiene a la familia.
| |
| * Métodos privados: no pueden ser invocados con un receptor explícito, el receptor siempre es self. Esto significa que los métodos privados sólo pueden ser llamados en el contexto del objeto actual, no se puede invocar los métodos privados de otro objeto.
| |
|
| |
| Una diferencia importante con otros lenguajes orientados a objeto es que el control de acceso es determinado dinámicamente en la medida que el programa se ejecuta. Obtendrá una violación de acceso sólo cuando intente ejecutar el método restringido.
| |
|
| |
| ====Especificación del control de acceso====
| |
|
| |
| Se especifica el nivel de acceso de los métodos dentro de una clase o módulo utilizando una o más de las tres funciones public, protected y private. Puede usar cada función en dos formas diferentes.
| |
|
| |
| Si se usan sin argumentos, las tres funciones definen el control de acceso de los métodos subsiguientes.
| |
|
| |
| <code>
| |
| 1 class MiClase
| |
| 2 def metodo1 # 'public' por omisión
| |
| 3 #
| |
| 4 end
| |
| 5
| |
| 6 protected # los subsiguientes métodos serán 'protected'
| |
| 7
| |
| 8 def metodo2
| |
| 9 #
| |
| 10 end
| |
| 11
| |
| 12 private # los subsiguientes métodos serán 'private'
| |
| 13
| |
| 14 def metodo3
| |
| 15 #
| |
| 16 end
| |
| 17
| |
| 18 public # los subsiguientes métodos serán 'public'
| |
| 19
| |
| 20 def metodo4
| |
| 21 #
| |
| 22 end
| |
| 23 end
| |
| </code>
| |
|
| |
| De forma alternativa puede simplemente listar los métodos en dichas funciones.
| |
|
| |
| <code>
| |
| 1 class MiClase
| |
| 2 def metodo1
| |
| 3 #
| |
| 4 end
| |
| 5 # ... y el resto de las definiciones de métodos
| |
| 6
| |
| 7 public :metodo1, :metodo4
| |
| 8 protected :metodo2
| |
| 9 private :metodo3
| |
| 10 end
| |
| </code>
| |
|
| |
| ====Ejemplo private====
| |
|
| |
| El ejemplo abajo muestra un esqueleto para proteger un método peligroso de alteración del estado del objeto por medio del uso de otro método intermediario que se supone asegura el acceso, esto ayuda a mantener el código separado pero simple, uno asegura por ejemplo las credenciales, el otro ajusta el estado del objeto.
| |
|
| |
| <code>
| |
| 1 class Banco
| |
| 2 def initialize
| |
| 3 @tasa="10%"
| |
| 4 end
| |
| 5
| |
| 6 def tasa
| |
| 7 @tasa
| |
| 8 end
| |
| 9
| |
| 10 def interfase_segura(nueva_tasa) # Se supone que es "segura", llama al método privado
| |
| 11 # Código de seguridad
| |
| 12 self.tasa=nueva_tasa
| |
| 13 end
| |
| 14
| |
| 15 private
| |
| 16 def tasa=(nueva_tasa)
| |
| 17 @tasa=nueva_tasa
| |
| 18 end
| |
| 19 end
| |
| </code>
| |
|
| |
| <code>
| |
| 1 >> banco = Banco.new
| |
| 2 => #<Banco:0xb747e318 @tasa="10%">
| |
| 3 >> banco.tasa
| |
| 4 => "10%"
| |
| 5 >> banco.interfase_segura("20%")
| |
| 6 => "20%"
| |
| 7 >> banco.tasa
| |
| 8 => "20%"
| |
| 9 >> banco.tasa="30%"
| |
| 10 NoMethodError: private method `tasa=' called for #<Banco:0xb747e318 @tasa="20%">
| |
| 11 from (irb):63
| |
| 12
| |
| </code>
| |
|
| |
| ====Ejemplo protected====
| |
|
| |
| Por definición este control de acceso limita el acceso a la familia, objetos de la misma clase, y objetos de clases derivadas de la clase que define el método protegido. Abajo un ejemplo simple para comparar manzanas con manzanas, no con peras.
| |
|
| |
| <code>
| |
| 1 class Manzana
| |
| 2 def initialize(peso)
| |
| 3 @peso = peso
| |
| 4 end
| |
| 5
| |
| 6 def <=>(otra_manzana)
| |
| 7 self.peso <=> otra_manzana.peso
| |
| 8 end
| |
| 9
| |
| 10 protected
| |
| 11
| |
| 12 def peso
| |
| 13 @peso
| |
| 14 end
| |
| 15 end
| |
| 16
| |
| 17 class Pera
| |
| 18 def initialize(peso)
| |
| 19 @peso = peso
| |
| 20 end
| |
| 21
| |
| 22 def <=>(otra_pera)
| |
| 23 self.peso <=> otra_pera.peso
| |
| 24 end
| |
| 25
| |
| 26 protected
| |
| 27
| |
| 28 def peso
| |
| 29 @peso
| |
| 30 end
| |
| 31 end
| |
| </code>
| |
|
| |
| <code>
| |
| 1 >> m1 = Manzana.new(100)
| |
| 2 => #<Manzana:0xb74838e0 @peso=100>
| |
| 3 >> m2 = Manzana.new(200)
| |
| 4 => #<Manzana:0xb747dda0 @peso=200>
| |
| 5 >> p1 = Pera.new(50)
| |
| 6 => #<Pera:0xb74783c8 @peso=50>
| |
| 7 >> m1 <=> m2
| |
| 8 => -1
| |
| 9 >> m1 <=> p1
| |
| 10 NoMethodError: protected method `peso' called for #<Pera:0xb74783c8 @peso=50>
| |
| 11 from ./manzanas.rb:7:in `<=>'
| |
| 12 from (irb):11
| |
| 13
| |
| </code>
| |
|
| |
| Al intentar hacer la comparación entre manzana y pera, se invoca pera.peso en el contexto de las manzanas, y ya que el método está protegido, entonces da el error.
| |
|
| |
| Ahora bien, esta idea de uso para protected en realidad está en franco desuso por muchos ya que va en contra de la flexibilidad y dinamismo natural de Ruby y del concepto de Duck Typing que explicaremos a continuación.
| |
|
| |
| ====Duck Typing====
| |
|
| |
| En Ruby no declaramos tipos de variables o tipos para los métodos, todo es simplemente alguna encarnación, un objeto de una clase, y las clases no son tipos. Si deseamos programar con la filosofía Duck Typing lo único que necesita recordar es que el "tipo" de objeto está determinado por lo que puede hacer, no por su clase.
| |
|
| |
| En la práctica esto significa que hay pocas pruebas de valores de objeto. Por ejemplo digamos que estamos programando un método para agregar información de dos objetos de cierta clase y obtener un resultado String. Los programadores con conocmientos de C# o Java estarían tentados a hacer algo como lo siguiente:
| |
|
| |
| <code>
| |
| 1 def anexar(obj1,obj2)
| |
| 2 # probar que se nos han dado lo parámetros correctos
| |
| 3 unless obj1.kind_of?(String)
| |
| 4 fail TypeError.new("Se espera String")
| |
| 5 end
| |
| 6 unless obj2.kind_of?(String)
| |
| 7 fail TypeError.new("Se espera String")
| |
| 8 end
| |
| 9 obj1 << obj2
| |
| 10 end
| |
| </code>
| |
|
| |
| <code>
| |
| 1 >> anexar("Hola", " Mundo")
| |
| 2 => "Hola Mundo"
| |
| 3 >> anexar("Hola", 1)
| |
| 4 TypeError: Se espera String
| |
| 5 from ./dt.rb:7:in `anexar'
| |
| 6
| |
| </code>
| |
|
| |
| Si abraza la filosofía Duck Typing de Ruby podrá escribir este método de una forma mucho más simple.
| |
|
| |
| <code>
| |
| 1 def anexar(obj1,obj2)
| |
| 2 obj1 << obj2
| |
| 3 end
| |
| </code>
| |
|
| |
| Usted no necesita verificar el tipo de los argumentos en tanto que se soporte el método << en obj1 simplemente todo funcionará bien. Si no es así, se lanzará una excepción de todas maneras, el mismo resultado de que si usted hubiera implementado la verificación. Pero de pronto su método es mucho más flexible, puede pasarle otros objetos no necesariamente String, tal vez un Fixnum.
| |
|
| |
| <code>
| |
| 1 >> anexar("Hola", 1)
| |
| 2 => "Hola\001"
| |
| </code>
| |
|
| |
| ¿Qué pasa si obj1 no soporta el método << entonces?
| |
|
| |
| <code>
| |
| 1 >> anexar(1.2, " dos ")
| |
| 2 NoMethodError: undefined method `<<' for 1.2:Float
| |
| 3 from (irb):6:in `anexar'
| |
| 4 from (irb):13
| |
| 5
| |
| </code>
| |
|
| |
| Obtenemos una excepción NoMethodError para el método << para la clase Float en este ejemplo. Una forma de prevenir posible es usar el método respond_to?:
| |
|
| |
| <code>
| |
| 1 def anexar(obj1,obj2)
| |
| 2 # probar que se nos han dado lo par ámetros correctos
| |
| 3 unless obj1.respond_to?(:<<)
| |
| 4 fail TypeError.new("'obj1' necesita la capacidad <<")
| |
| 5 end
| |
| 6
| |
| 7 obj1 << obj2
| |
| 8 end
| |
| </code>
| |
|
| |
| Pero nuevamente esto es más código que mantener y hay que evaluar si realmente quiere tomar ese trabajo ya que igualmente podría manejar las excepciones más arriba.
| |
|
| |
| Si ahora pasa un Array como obj2 obtendrá un error de tipo ya que Array no es un String pero si podemos invocar el método to_s para obtener una representación razonable. En última instancia lo que queremos es que nuestro método si nos devuelva un String.
| |
|
| |
| <code>
| |
| 1 >> arreglo = [0,1,2]
| |
| 2 => [0, 1, 2]
| |
| 3 >> anexar("Hola", arreglo.to_s)
| |
| 4 => "Hola012"
| |
| </code>
| |
|
| |
| Esto nos lleva a una nueva versión del método anexar, usando to_s para "convertir" los objetos en String, ahora ya tampoco necesitamos la verificación respond_to? ya que String responde a << . Tal vez ahora lo que necesita es validar es que tengan una representación en String, es decir, que respondan a to_s.
| |
|
| |
| <code>
| |
| 1 def anexar(obj1, obj2)
| |
| 2 # probar que se nos han dado lo parámetros correctos
| |
| 3 unless obj1.respond_to?(:to_s)
| |
| 4 fail TypeError.new("Se espera que responda a to_s")
| |
| 5 end
| |
| 6 unless obj2.respond_to?(:to_s)
| |
| 7 fail TypeError.new("Se espera que responda a to_s")
| |
| 8 end
| |
| 9
| |
| 10 obj1.to_s << obj2.to_s
| |
| 11 end
| |
| </code>
| |
|
| |
| El código de prueba puede llevarle nuevamente a situaciones indeseables. ¿Soporta obj1 << pero no to_s? Es mejor lidiar con las excepciones que ponerse a probar los tipos y/o los métodos a los que responde. Con esta nueva versión, podemos por ejemplo pasarle dos arreglos, mejor dicho, cualquier par de objetos que tenga una representación String (to_s) y concatenarlos y obtener una salida en String.
| |
|
| |
| <code>
| |
| 1 >> arreglo = [0,1,2]
| |
| 2 => [0, 1, 2]
| |
| 3 >> anexar(arreglo, arreglo)
| |
| 4 => "012012"
| |
| </code>
| |
|
| |
| Ahora veamos nuestro método en acción con varios objetos involucrados:
| |
|
| |
| <code>
| |
| 1 >> anexar("Hola", " Mundo") ; dos String
| |
| 2 => "Hola Mundo"
| |
| 3 >> anexar(1, " Mundo") ; Fixnum + String
| |
| 4 => "1 Mundo"
| |
| 5 >> anexar(1, 1) ; dos Fixnum
| |
| 6 => "11"
| |
| 7 >> anexar(1, 1.1) ; Fixnum + Float
| |
| 8 => "11.1"
| |
| 9 >> anexar(1, [1,2," :) "]) ; Fixnum + Array
| |
| 10 => "112 :) "
| |
| 11 >> anexar({1=> "mundo", 2 => "hola"}, [1,2," :) "]) ; Hash + Array
| |
| 12 => "1mundo2hola12 :) "
| |
| </code>
| |
|
| |
| De lo único que debe preocuparse en su clase particular es que soporte to_s para tomar ventaja de nuestro método, si no fallará con una excepción. Veamos:
| |
|
| |
| <code>
| |
| 1 >> class Miclase
| |
| 2 >> end
| |
| 3 => nil
| |
| 4 >> miobj = Miclase.new
| |
| 5 => #<Miclase:0x7fb2a1b2c770>
| |
| 6 >> anexar("Uh?", miobj)
| |
| 7 => "Uh?#<Miclase:0x7fb2a1b2c770>" ; to_s ya existe!
| |
| 8 >> anexar(miobj, "¡Ah!")
| |
| 9 => "#<Miclase:0x7fb2a1b2c770>\302\241Ah!" ; to_s ya existe...
| |
| </code>
| |
|
| |
| Por supuesto puede sobrescribir el método to_s heredado de Object.
| |
LATAM Fedora!
LATAM Fedora is a regular column of Spanish language contributions around open source software. It is our first expansion into incorporating foreign language content into FWN.
This week's contribution is from Guillermo Gómez, an introduction to bash-completion. Enjoy!
bash-completion
Si usted es administrador de sistemas o desarrollador, y si no ha montado este paquete, tal vez llore de felicidad después de unos minutos de su uso. No haré más comentarios, sólo ejemplos.
- yum install bash-completion
service
$ service ht<tab>
alias
$ unalias <tab><tab>
egrep fgrep grep l. ll ls mc vi which
yum
$ yum install bas<tab><tab>
bashdb.noarch basket-libs.i686 bastet.x86_64
bash-doc.x86_64 basket-libs.x86_64
BasiliskII.x86_64 basket.x86_64
yumdb
$ yumdb rename<tab><tab>
rename rename-force
pkcon (PackageKit)
$ pkcon re<tab><tab>
refresh repo-disable repo-list resolve
remove repo-enable repo-set-data
svn
$ svn c<tab><tab>
cat checkout cl co copy
changelist ci cleanup commit cp
git
$ git cl<tab><tab>
clean clone
modprobe
$ modprobe co<tab><tab>
cobra compal-laptop coretemp
coda configfs
man
$ man ls<tab><tab>
ls lscgroup lseek64 lsof lstat
lsame lscpu lset lsort lstat64
lsattr lsdiff lsetfilecon lspci lsusb
lsblk lsearch lshal lspcmcia
lsb_release lseek lsmod lssubsys
ssh
$ ssh 10.10.10.<tab><tab>
10.10.10.223 10.10.10.226 10.10.10.50
10.10.10.225 10.10.10.233 10.10.10.80
Gomix"