c++ - son - ¿cuál de las siguientes es una característica clave del modelo de red entre pares?
¿Cómo rompe la encapsulación de la palabra clave de amigo(Clase/Función) en C++? (10)
Cita de C ++ Preguntas frecuentes que creo que describe muy bien la situación con el amigo y la encapsulación.
¡No! Si se usan correctamente, mejoran la encapsulación.
A menudo es necesario dividir una clase a la mitad cuando las dos mitades tendrán diferentes números de instancias o diferentes tiempos de vida. En estos casos, las dos mitades generalmente necesitan acceso directo entre sí (las dos mitades solían estar en la misma clase, por lo que no ha aumentado la cantidad de código que necesita acceso directo a una estructura de datos; simplemente ha reorganizado) el código en dos clases en lugar de una). La forma más segura de implementar esto es hacer que las dos mitades sean amigas entre sí.
Si usas amigos como los que acabamos de describir, mantendrás las cosas privadas en privado. Las personas que no entienden esto a menudo hacen esfuerzos ingenuos para evitar el uso de la amistad en situaciones como la anterior, y a menudo destruyen la encapsulación. O bien usan datos públicos (¡grotesco!), O hacen accesibles los datos entre las mitades a través de las funciones públicas get () y set (). Tener una función miembro pública get () y set () para un dato privado está bien solo cuando el dato privado "tiene sentido" desde fuera de la clase (desde la perspectiva de un usuario). En muchos casos, estas funciones de miembros get () / set () son casi tan malas como los datos públicos: ocultan (solo) el nombre del dato privado, pero no ocultan la existencia del dato privado.
Algunos programadores dijeron que "una función amiga rompe la encapsulación en C ++". y algunos programadores también dijeron: "Las funciones de amigo no rompen la encapsulación, sino que, naturalmente, extienden la barrera de encapsulación".
¿Qué significa eso?..
Si una función amiga rompe la encapsulación en C ++, ¿cómo?
La amistad promueve la cohesión a expensas de cierta encapsulación. Todavía tienes una unidad encapsulada (la clase y sus amigos), aunque es menos granular. Pero cuando la alternativa es agregar funcionalidad adicional a la clase, que no necesariamente pertenece, el uso de la amistad le permite el acceso que necesita mientras mantiene la cohesión de la clase. La encapsulación no es el único principio, ni siquiera el más importante, de la POO.
La encapsulación significa que no puedes ver lo que hay dentro, y friend
significa que puedes ver dentro. Dependiendo de su punto de vista, el friend
rompe la encapsulación (dejando que el amigo vea el interior) o lo extiende (dejando que el desarrollador relaje la barrera solo para amigos específicos ).
FWIW, una buena regla general es decir que solo las clases anidadas / internas deben declararse como amigos; esta regla puede resumirse como "sin amigos de larga distancia", es decir, los amigos de una clase (si los hay) deben declararse dentro del mismo archivo de encabezado que la clase en sí.
La palabra clave friend permite que otras clases accedan a miembros de datos privados y protegidos en C ++. Algunas personas piensan que esto está rompiendo la encapsulación, otras personas piensan que está extendiendo la encapsulación a un pequeño número de clases externas que necesitan acceso a datos privados / protegidos. La "encapsulación de ruptura" significa que los miembros y los métodos suelen ser privados por una razón, y la palabra clave de amigo rompe esa encapsulación.
En el código que he visto, la palabra clave friend normalmente rompe la encapsulación y reduce la calidad del código, pero en ocasiones hay situaciones en las que se usa bien.
Aquí hay un tutorial simple sobre la amistad y la herencia en clase si nunca lo has visto antes:
La razón por la que algunos dicen que los "amigos" rompen la encapsulación es porque todo el punto de encapsular datos y funcionalidad es para que nada más necesite ver que los datos pueden, pero los amigos permiten que otras clases vean adentro.
Personalmente, creo que los amigos no rompen la encapsulación porque las clases no deben ser completamente dependientes, sino más bien interdependientes.
Tomemos la analogía de un coche. Normalmente usamos esto como un modelo de abstracción, diciendo que no necesitamos saber cómo funciona el motor para saber que al presionar el pedal del acelerador funciona. Esta es toda la idea de la encapsulación, donde solo las funciones que necesitamos para interactuar con una clase son las únicas que debemos conocer.
Sin embargo, una clase de Mecánicos definitivamente debería saber sobre el funcionamiento interno específico del automóvil, pero no tiene sentido construir un Mecánico en un Automóvil.
Aquí es donde entran los amigos. Usted hace que el Mecánico sea un Amigo del motor, los frenos o lo que sea necesario arreglar y él puede arreglarlo. No puede arreglarlo si todo lo que puede hacer es presionar el gas / freno.
Publico un fragmento de The Design and Evolution of C ++ de Stroustrup,
2.10 El Modelo de Protección, pág. 53.
[...]
Una declaración de amistad fue vista como un mecanismo similar al de un dominio de protección que otorga una capacidad de lectura y escritura a otro. Es una parte explícita y específica de una declaración de clase. Por consiguiente, nunca he podido ver las afirmaciones recurrentes de que una declaración de
friend
"viola la encapsulación" como algo más que una combinación de ignorancia y confusión con la terminología que no es C ++.
Puede pensar que C ++ tiene un sistema de control de acceso muy primitivo, puede acceder a todos los miembros (amigo o función de miembro), puede acceder solo a miembros públicos (todo lo demás, excepto ...) o puede acceder a público y protegido miembros (... reglas especiales para la herencia).
La "encapsulación" en el resumen es igualmente un sistema de control de acceso primitivo, excepto que generalmente decimos que el código que es "parte de la clase" es privilegiado y puede manipular el objeto en todas las formas que el lenguaje permite. El código que no es "parte de la clase" no tiene privilegios y debe usar una interfaz pública más pequeña, probablemente publicada.
Entonces, ¿para qué es "amigo"? Si piensa en las cosas de esta manera, es para escribir código privilegiado que no es una función miembro. Es necesario porque hay algunas cosas que, por razones técnicas, no pueden ser funciones de miembro. Los que puedo pensar de forma remota son las sobrecargas de operadores en las que necesita conversión en el LHS, y las especializaciones de las funciones estándar de la plantilla de algoritmos como std :: swap (este último es un problema menor, ya que si hay una función de intercambio público, es Es poco probable que también sea perjudicial que exista un método de intercambio público. Pero es bueno apoyar a aquellas personas que desean que las interfaces sean ortogonales).
La pregunta entonces es: ¿se rompe la encapsulación para tener un código privilegiado que no es una función miembro? En Java probablemente dirías "sí", ya que el código Java es claramente "parte de la clase" si está en la definición de la clase, y no es parte de la clase si no lo está.
En C ++ es un poco menos claro, ya que las definiciones de las funciones miembro no tienen que estar en la definición de clase para comenzar. Hay muy poca diferencia entre:
// Foo.h
class Foo;
void bar(Foo &);
class Foo {
friend void bar(Foo &);
public:
static void baz(Foo &);
};
// Foo.cpp
void bar(Foo &f) {
// access private members of f
}
void Foo::baz(Foo &f) {
// access private members of f
}
No puedo convencerme a mí mismo de que en ningún sentido significativo la barra "rompe la encapsulación", mientras que baz "preserva la encapsulación". Incluso un pequeño grado de pragmatismo indica que bar y baz están haciendo exactamente lo mismo, y además, aunque bar no es una función miembro, claramente es "parte de la clase" tanto como lo es baz. Las únicas diferencias son sintácticas y no tienen nada que ver con la encapsulación.
Por otro lado, claramente "amigo" se puede usar para romper por completo la encapsulación, al nombrar a todas las demás clases que pueda considerar su amigo (como hacer que todos sus miembros sean públicos o paquetes protegidos en Java, o hacer que todos sus miembros sean públicos en C ++ ). "friend" necesita trabajar con clases y no solo con métodos, de modo que pueda declarar clases anidadas como amigas cuando corresponda, y tener grupos pequeños de clases estrechamente acopladas que no estén anidadas (es decir: dibuje los límites de encapsulación en un nivel más alto que el de una sola clase). Si lo usas para juntar todas tus clases, entonces probablemente terminarás con un código incorrecto.
En realidad, esto no se debe a "amigo", ya que todo lo que se hizo me dio una nueva sintaxis para exponer a los miembros que podría haber expuesto como "público" de todos modos. Pero si busco "amigo" como solución alternativa y lo uso para romper mis propias interfaces públicas inadecuadas, entonces "amigo" me ha dado una excusa para un diseño deficiente. Podría jurarlo en el futuro y recomendar a otros que hagan lo mismo, pero esto es por la misma razón por la que juro beber cada vez que me levanto. Otros pueden ser lo suficientemente sabios o afortunados para disfrutar de los beneficios sin mayores efectos secundarios.
Así que "amigo", en sí mismo permite romper la encapsulación, al igual que lo hace la aritmética de punteros, y al igual que el alcohol permite caer en la cuneta a las 3 am. Sin embargo, con los cuidados y el diseño decente, los usos particulares de los amigos no deben y no deben ser una ruptura de la encapsulación. Y, en caso de que algún futuro empleador potencial esté leyendo esto, cuando bebo, normalmente no estoy atorado ;-)
En última instancia, el problema es que en todos los idiomas que conozco, las interfaces se comportan como herencia, en el sentido de que la interfaz pública de la clase incorpora a todos los miembros de todas las interfaces que implementa. La interfaz de clase es por lo tanto más grande que cualquier otra. Esto no es algo malo, ya que la relación "is-a" es clave para OO. Además, se corresponde bien con lo que ocurre cuando publicas una interfaz, es decir, que cualquier cliente podría estar usándola. Simplemente no se adapta a lo que muchos diseños requieren, lo que significa que las clases tienen una interfaz pequeña por defecto, pero también ofrecen una interfaz más grande para "superusuarios". Entonces, en cambio, la interfaz grande es la predeterminada, y los "subusuarios" se adhieren a las partes aburridas.
Entonces, friend es una herramienta contundente para ofrecer una interfaz más grande para "superusuarios" sin afectar la interfaz de "subusuario". Sin embargo, dado que es tan contundente, en realidad solo funciona cuando las clases relevantes están diseñadas juntas, y los argumentos sobre cómo deben ser las clases acopladas es lo que lleva a los desacuerdos en cuanto a qué "rompe la encapsulación". Recuerde que los niveles de acceso en los idiomas no deben imponer la encapsulación, y C ++, en particular, es un lenguaje de paradigma múltiple. Así que C ++ hace un esfuerzo para ayudar a los programadores a hacer cumplir la encapsulación, pero el programador todavía tiene que diseñar bien y usar las funciones disponibles de acuerdo con sus propios principios de programación.
Si dibuja una línea alrededor de todos los que pueden acceder a los datos privados, es en la medida en que los datos están encapsulados.
Si usas la palabra clave friend
, esa línea tiene que dibujarse alrededor de más cosas. Por lo tanto, la encapsulación se extiende o se rompe, según su punto de vista.
En general, desaliento el uso de friend
. En su lugar, considere agregar accesores para datos privados.
Tan pronto como declare una función de amigo, los especificadores de acceso y, por lo tanto, los detalles encapsulados de su clase serán accesibles por esa función de amigo, que no es una parte / método de su clase. Entonces, básicamente, delegas un cierto control sobre las estructuras de datos y métodos internos a una función externa en la que "confías"
Tienes dos escuelas de pensamiento diferentes.
El primero (Java y C # ...) :: lanza todas las funciones en el área pública de la clase, ya sea que esa función "necesite" acceder al área "privada" o no.
El segundo (C ++ ...) :: proporciona "solo" las funciones necesarias para que esta clase pueda sobrevivir, y proporciona funciones de nivel superior en una colección no relacionada del tipo creado.
IMHO :: C ++ satisface claramente los objetivos de OOP.