patron intermediario flyweight ejemplo diseño c++ design-patterns

c++ - intermediario - idioma de pimpl vs. patrón de diseño de puente



patron de diseño proxy (5)

Actualmente hay una biblioteca Boost bajo revisión que implementa el patrón Pimpl . He Pimpl algunos ejemplos básicos utilizando la implementación propuesta de Boost Pimpl si alguien quiere saltar sobre el uso de este código internamente:

https://github.com/sean-/Boost.Examples/tree/a148be39abcb21428857aa50495f8c352600741e/pimpl

ACTUALIZACIÓN: El enlace anterior se ha actualizado para apuntar a la versión archivada. No parece probable que Boost.Pimpl sea aceptado para aumentar en este momento a favor de std::unique_ptr<> como un reemplazo viable (aunque sea menos completo que Boost.Pimpl para algunos casos de uso menos comunes).

Si tiene acceso a C++11 , probablemente esté mejor usando std::unique_ptr<> como implementación de PIMPL:

class MyClass { public: // ... private: class Impl; std::unique_ptr<Impl> impl_; };

Puede ver un ejemplo completo usando std::unique_ptr<> en mi pregunta de estrategia de bloqueo de clase reentrada C ++ 11 .

Al usar el método swap() std::unique_ptr<> , resulta conveniente y muy práctico para la semántica de movimientos de C++11 mover las entrañas de un objeto de un propietario a otro. Por ejemplo, supongamos que MyClass exporta una implementación swap() que simplemente reenvía a std::unique_ptr<> :

void MyClass::swap(MyClass *c) { impl_.swap(c); } MyClass c1, c2; c1.swap(c2);

Esta es ahora una forma segura de excepción de transferir los contenidos de c2 a c1 . Otra gran razón para usar el lenguaje PIMPL es preservar / mantener un ABI estable.

Acabo de notar un nuevo término idiomática pimpl, ¿cuál es la diferencia entre este idioma con el patrón de diseño de Bridge? Estoy confundido acerca de eso.

También noté que el lenguaje pimpl siempre se usa para la función de intercambio, ¿qué es eso? ¿Alguien podría darme un ejemplo?



He usado la aplicación de puntero para ocultar los detalles de la implementación de la interfaz pública / archivo de archivo.

struct Interface { private: class Impl; Impl *pImpl; };

Luego, en algún lugar dentro de Interface::Impl se define e implementa, pero los detalles no están expuestos.

En cuanto a swap, esta es la primera vez que la escucho. ¿Puedes explicarlo?


PIMPL es una forma de ocultar la implementación, principalmente para romper las dependencias de compilación.

El patrón de Bridge, por otro lado, es una forma de soportar múltiples implementaciones.

swap es una función estándar de C ++ para intercambiar los valores de dos objetos. Si cambia el puntero a la implementación por una implementación diferente, esencialmente está cambiando el mecanismo de la clase en tiempo de ejecución.

Pero en su forma básica y común, una clase que usa puntos PIMPL apunta a una implementación única, por lo que no hay una clase abstracta con subclases distintas, solo una clase, declarada hacia adelante y compilada en otra parte. Cambiar la clase de implementación no requiere ninguna recompilación de fuentes que incluyan el encabezado principal.

Por ejemplo, supongamos que tiene muchas funciones miembro privadas, enumeraciones privadas y datos privados. Y estos "bits" privados cambian con bastante frecuencia a medida que la clase se desarrolla y mantiene. Si las dependencias #include son tales que al tocar este archivo de encabezado se vuelve a compilar una gran cantidad de fuentes, tiene un buen candidato para PIMPL.

Por lo tanto, el patrón Bridge se trata del diseño orientado a objetos, mientras que el lenguaje PIMPL se trata del diseño físico de archivos.

(Para obtener más información sobre diseño físico, recomiendo el libro Diseño a gran escala de software C ++ de John Lakos).


Pimpl: En resumen, el patrón pimpl es realmente bueno para ocultar la implementación privada. Si quisiera exponer sus archivos de encabezado para los clientes que necesitaban construir en su interfaz pública, pero no quería exponer sus detalles privados de implementación, podría usar el patrón pimpl para ocultar los detalles. Haría esto por varias razones, pero principalmente su archivo de encabezado no tendría que cambiar cuando cambie los cambios de los detalles de su implementación privada, lo que de otro modo obligaría a los clientes a compilar. De esta manera, se desacopla y se ocultan los detalles de su implementación privada. Por lo general, debe mantener el puntero implícito en un contenedor RAII como un puntero unqiue para asegurarse de que se libera en el momento de la destrucción.

Pimpl // my_class.h class my_class { public: // public here private: class impl; //forward declare impl unique_ptr<impl> pimpl; // private implementation pointer }; // my_class.cpp class my_class::impl { // defined privately here // ... all private data and functions: all of these // can now change without recompiling callers ... }; my_class::my_class(): pimpl( new impl ) { // ... set impl values ... my_class& operator=(my_class other); friend void swap (my_class& lhs, myclass& rhs); // used for assignment }

Probablemente surgió el intercambio porque cuando está haciendo un operador de asignación para esta clase e implementando su propia rutina de intercambio para asignar a los miembros, debe tener en cuenta la asignación de este puntero también.

my_class& my_class::operator=(my_class other) { swap(*this,other); return *this; } void swap ( my_class& lhs, myclass& rhs ) { using std::swap // now use default swap if override doesn''t exist // call swap for other members // swap (lhs.data,rhs.data); // call swap on unique_ptr lhs.pimpl.swap(rhs.pimpl); // doesn''t throw exceptions }

El puente es un patrón totalmente separado que se utiliza para unir elementos. La similitud entre los patrones sería que puede tener un método de proceso en su clase que oculta la llamada real, ya que delegará esta llamada al objeto contenido que manejará la llamada al método real. En otras palabras, si tenía una clase base de interfaz de solicitud que contenía un puntero de clase base de implementador de solicitud. Por lo tanto, en el tiempo de ejecución, podría "puentear" los sistemas al inicializar el puente con un tipo de implementador de solicitud específico, pero alguien que llame al puente simplemente llamará al método de solicitud de proceso que delegaría la llamada al método de solicitud de implementador derivado específico en tiempo de ejecución. Hay varias fuentes en google con bonitos diagramas que pueden explicar esto más claramente también.