java - tutorial - ¿Cómo evitar que el cliente vea clases privadas internas en la biblioteca de Android?
load html java (4)
Si entiendo correctamente, usted está preguntando sobre la publicación de su biblioteca para uso de terceros sin revelar parte de su fuente. Si ese es el caso, puede usar proguard , lo que puede ofuscar su biblioteca. Por defecto, todo será excluido / ofuscado, a menos que especifiques que las cosas que quieres excluir se ofuscan / excluyen.
Tengo una biblioteca con varios paquetes-
digamos
paquete a;
paquete b;
dentro del paquete a tengo público a_class
dentro del paquete b Tengo público b_class
a_class usa b_class.
Necesito generar una biblioteca a partir de esto, pero no quiero que el Cliente vea b_class.
La única solución que conozco es aplanar mis paquetes bellamente comprensibles para un solo paquete y usar el acceso predeterminado al paquete para b_class. ¿Hay alguna otra manera de hacerlo? tal vez usando interfaces o algún tipo de patrón de diseño?
Si desea distribuir [parte de] su código sin que el cliente pueda acceder a él en absoluto, eso significa que el cliente tampoco podrá ejecutarlo. : -O
Por lo tanto, solo tiene una opción: poner la parte sensible de su código en un servidor público y distribuir un proxy para acceder a él, para que su código se guarde y se ejecute en su servidor y el cliente todavía pueda ejecutarlo a través de el proxy pero sin acceder directamente a él.
Puede usar un servlet, un servicio web, un objeto RMI o un simple servidor TCP, según el nivel de complejidad de su código.
Este es el enfoque más seguro que se me ocurre, pero también merece un precio a pagar: además de complejar su sistema, introduciría un retraso de red para cada operación remota, lo que podría ser un gran problema dependiendo de los requisitos de rendimiento. Además, debe securizar el servidor para evitar intrusiones de hackers. Esta podría ser una buena solución si ya tiene un servidor que puede aprovechar.
Si rechaza mover el código a un servidor individual controlado, todo lo que puede hacer es obstaculizar al programador cliente cuando intenta usar sus API. Comencemos aplicando buenas prácticas a su diseño:
- Deje que sus paquetes estén organizados como están ahora.
Para cada clase que quieras "ocultar":
- Hazlo no público.
- Extraiga su API pública a una nueva interfaz pública:
public interface MyInterface {...}
- Cree una clase de fábrica pública para obtener un objeto de ese tipo de interfaz.
public class MyFactory { public MyInterface createObject(); }
Hasta ahora, ahora tiene sus paquetes débilmente acoplados, y las clases de implementación ahora son privadas (como predican las buenas prácticas, y usted ya dijo). Aún así, todavía están disponibles a través de las interfaces y las fábricas.
Entonces, ¿cómo puede evitar que los clientes "extraños" ejecuten sus API privadas? Lo que viene a continuación es una solución creativa, un poco complicada pero válida, basada en obstaculizar a los programadores clientes:
Modifique sus clases de fábrica: agregue a cada método de fábrica un nuevo parámetro:
public class MyFactory
{
public MyInterface createObject(Macguffin parameter);
}
Entonces, ¿qué es Macguffin
? Es una nueva interfaz que debe definir en su aplicación, con al menos un método:
public interface Macguffin
{
public String dummyMethod();
}
Pero no proporcione ninguna implementación utilizable de esta interfaz. En cada lugar de su código necesita proporcionar un objeto Macguffin
, créelo a través de una clase anónima :
MyFactory.getObject(new Macguffin(){
public String dummyMethod(){
return "x";
}
});
O, incluso más avanzado, a través de un objeto proxy dinámico , por lo que no se encontraría ningún archivo ".class" de esta implementación, incluso si el programador cliente se atreve a descompilar el código.
¿Qué obtienes de esto? Básicamente es para disuadir al programador de usar una fábrica que requiere un objeto desconocido, indocumentado e indescifrable. A las clases de fábrica simplemente les debería importar no recibir un objeto nulo, y para invocar el método ficticio y verificar el valor de retorno tampoco es nulo (o, si desea un nivel de seguridad más alto, agregue una regla de clave secreta no documentada).
Por lo tanto, esta solución se basa en una sutil ofuscación de su API, para desalentar al programador cliente para que la use directamente. Cuanto más oscuros sean los nombres de la interfaz de Macguffin y sus métodos, mejor.
Necesito generar una biblioteca a partir de esto, pero no quiero que el Cliente vea b_class. La única solución que conozco es aplanar mis paquetes bellamente comprensibles para un solo paquete y usar el acceso predeterminado al paquete para b_class. ¿Hay alguna otra manera de hacerlo?
Sí, haga que b_class
package-private (acceso predeterminado) y lo a_class
en instancia a través de la reflexión para usar en a_class
.
Como conoce el nombre completo de la clase, cargue reflexivamente la clase:
Class<?> clz = Class.forName("b.b_class")
Encuentre el constructor que desea invocar:
Constructor<?> con = clz.getDeclaredConstructor();
Permítase invocar el constructor haciéndolo accesible:
con.setAccessible(true);
Invoca al constructor para obtener tu instancia de b_class
:
Object o = con.newInstance();
Hurra, ahora tienes una instancia de b_class
. Sin embargo, no puede llamar a los métodos de b_class
en una instancia de Object
, por lo que tiene dos opciones:
- Use reflection para invocar los métodos de
b_class
(no es muy divertido, pero es fácil y puede estar bien si solo tiene unos pocos métodos con pocos parámetros). - Haz que
b_class
implemente una interfaz que no teb_class
que el cliente vea yb_class
tu instancia deb_class
a esa interfaz (leyendo entre líneas, sospecho que ya tienes esa interfaz).
Definitivamente querrá ir con la opción 2 para minimizar su dolor a menos que lo lleve nuevamente al punto de partida (contaminando el espacio de nombres con tipos a los que no quiere exponer al cliente).
Para una divulgación completa, dos notas:
1) Hay una (pequeña) sobrecarga para usar la reflexión frente a la instanciación directa y la invocación. Si transfiere a una interfaz, solo pagará el costo de la reflexión en la creación de instancias. En cualquier caso, probablemente no sea un problema a menos que realice cientos de miles de invocaciones en un círculo cerrado.
2) No hay nada que impida que un cliente determinado descubra el nombre de la clase y haga lo mismo, pero si entiendo tu motivación correctamente, solo quieres exponer una API limpia, así que esto no es realmente una preocupación.