miércoles, 27 de julio de 2011

Métodos privados y "protected" en Objective-C

El lenguaje Objective-C tiene algunas cosas a las que todavía no me termino de acostumbrar, después de más de un año de estar trabajando con él.

Una de estas cosas es la visibilidad de los métodos.

Me explico: en los lenguajes orientados a objetos, se suele tener por lo menos tres tipos de métodos: públicos, que son accesibles por todos; privados, que son solo accesibles dentro de la clase que los define; y "protected" que son visibles para la clase que los define y para sus clases derivadas.

Métodos públicos

En Objective-C todos los métodos son públicos, en el sentido que siempre se pueden ejecutar aunque no estén declarados como tal.

Para declarar un método como público y que quede visible para el resto del mundo, simplemente se declara en la definición de la clase en el .h

Métodos privados

Si bien cualquier método se puede invocar, los que no están declarados en el .h son considerados privados.

Al no declararlos en el .h, no es fácil conocer la firma de estos métodos, por lo que tampoco es fácil invocarlos. Además, si se usa un método privado, el compilador muestra un aviso que puede ser que el objeto no responda a ese método.

Métodos protected

En el lenguaje no hay forma de definir un método como protected. Lo que mejor que se puede hacer, es definirlo como privado, y documentarlo de alguna forma.

El problema es que al definirlo como privado, el compilador va a dar un warning de que no existe el método.

El "work arround" que encontré, es volver a declararlo en una categoría, para que el compilador sepa que existe.

Supongamos que tenemos una clase MySuperClass, que define un método privado
-(void)myProtectedMethod
y que tengo una clase MyDerivedClass que hereda de MySuperClass.

Si quiero usar el método en MyDerivedClass sin que me de un warning, puedo en el archivo MyDerivedClass.m, declarar una categoría para MySuperClass donde defino este método. Suena complicado, el código no lo es tanto.
// MyDerivedClass.m

#import "MyDerivedClass.h"

@interface MySuperClass (Protected)
-(void)myProtectedMethod;
@end


@implementation MyDerivedClass
...
@end

No se si es la mejor opción, pero es efectiva. Por supuesto, si alguien lo resolvió de otra forma, son bienvenidos los comentarios.

12 comentarios:

  1. Si un lenguaje de programacion te exige hacer estos inventos para logra algo obvio en otros lenguajes, el problema es del lenguaje...

    Cambiando un poco el tema, que lindo estaria tener objetos publicos y privados en GeneXus, no?

    ResponderEliminar
  2. Enrique:

    >Si un lenguaje de programacion te exige hacer estos
    >inventos para logra algo obvio en otros lenguajes, el
    >problema es del lenguaje...

    Estamos de acuerdo. Por otro lado, es más dinámico que otros lenguajes, lo que le da más flexibilidad.

    >Cambiando un poco el tema, que lindo estaria
    >tener objetos publicos y privados en GeneXus, no?

    Sí, estaría bueno. Ya lo habíamos planteado... El tema es publico o privado con respecto a que. Todavía está faltando el concepto de "módulo" me parece.

    ResponderEliminar
  3. Lo que se suele hacer tambien es poner el codigo:

    @interface MySuperClass (Protected)
    -(void)myProtectedMethod
    @end

    en un fuente MySuperClass+Protected.h que se puede incluir en los .m de las subclases.

    Y no me parece tan malo asi, es poner por separado lo que son metodos public y los protected, en vez de hacerlo todos junto; y de hecho es mas flexible porque se pueden hacer mas categorias.

    ResponderEliminar
  4. En realidad no veo la necesidad de separar los métodos en públicos, privados, protegidos ,etc.
    Quizás sea necesario en C++ u Objective-C por alguna cuestión de implementacion, no lo se.

    En Smalltalk por ejemplo todos los métodos son públicos, y no hay problema alguno con esto. Quizás la naturaleza dinámica del ambiente haga esto posible.

    Pero no veo el problema de tener en C++ u otro todos los métodos públicos.

    Saludos,
    Bruno

    ResponderEliminar
  5. Bruno: no conozco demasiado Smalltalk, pero creo que en eso son bastante parecidos.

    ResponderEliminar
  6. Hola,

    Mi pregunta apunta a ver desde un punto de vista practico pq se usan los diferentes tipos de métodos (públicos, privados, etc), pq no me doy cuenta porque existe esa necesidad ?
    Es decir, si en C++ solamente tuvieras métodos públicos, cual seria el problema ?

    ResponderEliminar
  7. Smalltalk:

    Bueno, ponernos a discutir eso (si se necesitan métodos privados/protected) es entrar en un tema bastante filosófico.

    Por lo pronto, en el paradigma de la programación orientada a objetos, se busca cumplir con algunos principios como por ejemplo "encapsulation" (en español no me suena...)

    Si vos dejás todo público, entonces no estás cumpliendo con este principio. Si estás desarrollando una API y dejás todo público, después no vas a poder cambiar los detalles de implementación...

    Por ejemplo, lo que hace Apple con la parte privada de la API (no documentada en realidad, porque no la pueden definir privada), es no aprobar aplicaciones que la usan. Si fuera una "feature" del lenguaje no precisarían hacer esto.

    Ahora, si me preguntás si es estrictamente necesario, claramente no lo es. Se puede vivir sin tener métodos private/protected.

    ResponderEliminar
  8. Marcos,

    Eso que me decís es lo que quería saber, en si no hay ninguna limitación para usar todo publico. En Smalltalk hay métodos privados y públicos, pero solo son una categorización, NO hay diferencia entre ellos, es solamente para tener todo mas ordenado (los geters y setters de una clase se ponen como privados por defecto, pero se pueden poner como públicos tambien).
    No veo pq tener todo publico rompe la "encapsulation", en realidad la "encapsulation" te la da el nombre del mensaje.
    Es decir, si se invoca el método #reverse, y este me devuelve un resultado, y no se que pasa dentro del objeto para solucionar esto.
    Aunque el método sea privado esta encapsulado. Pero como decís vos, esto es muy discutible, por que yo hago enfacis en el nombre del mensaje (hay diferencia entre mensaje y método).
    En lo personal prefiero tener todos los metodos disponibles y yo poder decidir si lo uso o no (en el caso de una DLL).

    Saludos,
    Bruno

    ResponderEliminar
  9. Bruno:
    Cuando el desarrollo se hace en un grupo reducido, el manejo de metodos privados o publicos, se puede resolver sin mayor problema, es todo cuestion de orden.

    Cuando se programa una API publica, que va a ser utilizada por mucha gente de diferentes formas, es aconsejable guiar al programador, en cuanto a que puede usar, para que no se le rompan los desarrollos futuros.

    Si le dejo usar un metodo que yo considero privado y le cambio su implementacion (parametros o su funcionamiento) puedo hacerle caer su programa, sin aviso.

    Es un tema bastante discutible, pero me gusta para ordenar, el tener metodos publicos (los que los demas pueden usar libremente) y metodos privados (los que puedo reorganizar/ refactorizar/ cambiar) sin afectar a nadie.

    ResponderEliminar
  10. Enrique,

    Ok, ahora entiendo, el tema va enfocado a las DLL. Mi pensamiento era cuando integras paquetes de una aplicación. El paquete A lo hizo el usuario "a", el B el "b", y así sucesivamente. En ese caso también definen métodos privados , protegidos, etc ? (tomar esta decisión también consume tiempo).

    Entiendo lo que decis, pq en Smalltalk también se acostumbra a poner privados los métodos que pueden cambiar (pero es una categorizacion dentro del ambiente de desarrollo que no afecta del deployment).

    Pero el uso de métodos categorizados como privados NO esta restringido, y la costumbre general es usarlos igual bajo tu responsabilidad. Personalemente prefiero que me dejen esa flexibilidad.

    Creo que queda a criterio de cada uno (entendiendo por uno alguien con experiencia).

    Saludos,
    Bruno

    ResponderEliminar
  11. Gracias por el dato. A mi me sirvió para mantener unos métodos escondidos y sin embargo poder saltearme los warnings del compilador.

    Creo que te falta un ; al hacer la forward declaration del mensaje que le estás agregando a la interfaz.

    Saludos!

    ResponderEliminar
  12. Varrojo: es verdad lo del ";", lo arreglo, gracias.

    ResponderEliminar