Un seguimiento de otra pregunta ( Tomando una decisión de diseño sobre la lectura de datos del modelo desde un archivo de entrada ).
Deseo hacer otra pregunta sobre el patrón de constructor o de fábrica. (Leí que el constructor es más complejo que la fábrica y es posible que no necesite usar el constructor por ahora). Así que aquí están los datos que tengo que leer:
TABLE: "DEGREES OF FREEDOM" X=Yes Y=Yes Z=Yes R1=Yes R2=Yes R3=Yes TABLE: "ANALYSIS OPTIONS" Solver=Advanced SolverProc=Advanced TABLE: "COORDINATE SYSTEMS" Name=GLOBAL Type=Cartesian X=0 Y=0 Z=0 TABLE: "GRID LINES" CoordSys=GLOBAL AxisDir=X GridID=A XRYZCoord=-720 LineColor=Gray8Dark Visible=Yes CoordSys=GLOBAL AxisDir=X GridID=B XRYZCoord=-432 LineColor=Gray8Dark Visible=Yes CoordSys=GLOBAL AxisDir=X GridID=C XRYZCoord=-144 LineColor=Gray8Dark Visible=Yes CoordSys=GLOBAL AxisDir=X GridID=D XRYZCoord=144 LineColor=Gray8Dark Visible=Yes CoordSys=GLOBAL AxisDir=X GridID=E XRYZCoord=432 LineColor=Gray8Dark Visible=Yes CoordSys=GLOBAL AxisDir=X GridID=F XRYZCoord=720 LineColor=Gray8Dark Visible=Yes ...
Hay muchas más tablas como estas. Algunas tablas tienen relaciones entre padres e hijos (cada sistema de coordenadas tiene su propia línea de cuadrícula). He hecho estructuras que representan cada tabla, como esta:
struct ActiveDOF { bool Ux; bool Uy; bool Uz; bool Rx; bool Ry; bool Rz; }; struct GridLine { enum Direction{X, Y, Z}; enum Type{PRIMARY, SECONDARY}; enum Location{START, END}; std::string coodSystem; Direction direction; std::string gridID; float XRYZ; Type lineType; QColor color; bool visible; Location bubleLoc; bool allVisible; float bubleSize; }; struct CoordinateSystem { std::string name; std::string type; QVector3D center; // center x, center y, cetner z QVector3D about; // aboutx, about y, about z std::vector<GridLine> gridLines; };
estas estructuras de datos se incorporan a la clase modelo, lo que hará una clase enorme ya que hay 50 estructuras de datos extrañas como esta:
class Model { private: ActiveDOF activeDOF; CoordinateSystem coordinateSystem; .... public: Model() {} ... }
cada tabla debe tener su método set y su método get. Este diseño me preocupa porque si decido cambiarlo, va a llevar mucho tiempo. Agradezco cualquier sugerencia. Creo que la información aquí también aclarará mejor la pregunta anterior.
Ahora, no sé a dónde debería ir el constructor o el método de fábrica, dada la situación. Miré algunos códigos y gráficos UML pero no pude entender cómo implementar la fábrica o el constructor. para hacer las estructuras requeridas para mi diseño. Tengo que tener acceso a cada tabla por su nombre, porque pueden estar sujetas a cambios dentro del modelo, por lo que por el momento estoy evitando convertir cada una de ellas en una subclase de una clase base virtual, para poder almacenarlos en un contenedor.
Además, ¿tiene sentido que en lugar de declarar una instancia de la estructura de datos, deba mantener un puntero a ellos? si todas las estructuras de datos se derivan de un clase base virtual llamada Record, entonces el modelo será algo como esto:
class Model { private: ActiveDOF* activeDOF; CoordinateSystem* coordinateSystem; .... std::Vector<Record*> data; public: Model() {} ... }
Creo que es un trabajo extra asignar, dislocar memoria para ellos, pero puede ayudar a administrar los datos y seguir escribiendo más? ¿Estoy en lo cierto al suponer eso?
Comentarios
Respuesta
Está intentando resolver un problema de acceso a datos, y requiere una solución de acceso a datos.
Primero, examinemos las soluciones propuestas y por qué no resuelven los problemas que tiene.
El patrón de fábrica
El patrón de diseño de fábrica (también llamado patrón de método de fábrica) es útil cuando desea utilizar polimorfismo y desea separar la construcción de objetos por completo de las clases de implementación.
En la programación basada en clases, el patrón del método de fábrica es un patrón de creación que utiliza métodos de fábrica para tratar el problema de crear objetos sin tener que especificar el clase exacta del objeto que se creará. Esto se hace creando objetos llamando a un método de fábrica — especificado en una interfaz e implementada por clases secundarias, o implementada en una clase base y opcionalmente anulada por clases derivadas — en lugar de llamar a un constructor .
(énfasis, mío)
Y más abajo se explica los problemas que resuelve este patrón:
El patrón de diseño del método de fábrica resuelve problemas como:
- ¿Cómo se puede crear un objeto? para que las subclases puedan redefinir qué clase instanciar?
- ¿Cómo puede una clase aplazar la instanciación a subclases?
Los problemas que describe en su pregunta son diferentes a estos. No se trata de subclases o polimorfismo. El patrón de fábrica no es una solución.
El patrón del constructor
El patrón del constructor (de Wikipedia ):
The Builder es un patrón de diseño diseñado para proporcionar una solución flexible a varios problemas de creación de objetos en la programación orientada a objetos. La intención del patrón de diseño de Builder es separar la construcción de un objeto complejo de su representación.
(énfasis, mío)
Aquí, nuevamente, vemos una discrepancia entre el problema que está describiendo y el uso previsto de este patrón.
El patrón de diseño Builder resuelve problemas como:
- ¿Cómo puede una clase (el mismo proceso de construcción) crear diferentes representaciones de un objeto complejo ?
- ¿Cómo puede una clase que incluye la creación de un objeto complejo ¿simplificarse?
(énfasis, mío)
La clave aquí es que la construcción de un objeto (un objeto = 1 objeto) es compleja. Esto podría deberse a una gran cantidad de argumentos del constructor, o debido a la complejidad de ensamblar sus dependencias.
Individualmente, cada estructura o clase es bastante simple, por lo que el patrón del constructor tampoco encaja bien.
Patrones de acceso a datos (la solución)
Los problemas que en realidad está tratando de resolver son cómo realizar el acceso a los datos, y es posible que esté buscando una Objeto de acceso a datos .
En software de computadora, un objeto de acceso a datos (DAO) es un objeto que proporciona una interfaz abstracta para algún tipo de base de datos u otro mecanismo de persistencia. Al asignar las llamadas de la aplicación a la capa de persistencia , el DAO proporciona algunas operaciones de datos específicas sin exponer detalles de la base de datos.
En su caso, reemplace «base de datos» por «archivo de texto». Un patrón de diseño relacionado es el Patrón de diseño del repositorio , que divide el acceso a los datos en 3 soluciones principales:
- Repositorio — la interfaz pública para «cosas de acceso a datos»
- Gateway — sabe cómo hablar con una base de datos Oracle o leer / escribir en un archivo de texto. Esto incluiría serializar objetos y estructuras a SQL o al formato de datos esperado en el archivo de texto.
- Factory — mapea los datos devueltos por la puerta de enlace a los objetos y estructuras utilizado por el resto de su aplicación
Propiedad de la memoria
Dado que no tiene la ventaja / limitación de la administración automática de la memoria, la propiedad de la memoria asignada a Estos objetos y estructuras son definitivamente una preocupación. Aquí debe tomar una decisión:
- ¿Es el objeto de acceso a datos responsable de limpiar esta memoria?
- ¿El objeto de acceso a datos ¿Espera que la persona que llama (código de cliente) asuma la responsabilidad de liberar esta memoria?
Si el objeto de acceso a datos se crea cuando se inicia la aplicación, y permanece activo hasta que se cierra la aplicación, el acceso a datos objeto podría tomar posesión de esta memoria.
Si el objeto de acceso a datos se crea a pedido y luego se elimina, el código del cliente debe asegurarse de que esta memoria esté limpia ned up.
Defina esto en los comentarios o la documentación del objeto de acceso a datos, y aplíquelo en la revisión del código.
Comentarios
- Me gusta este enfoque. Parece mucho más flexible. Cualquier otra cosa que pregunte expondrá mi ignorancia al respecto. ¿Debo implementar DAO yo mismo? De la manera que yo lo entiendo, ¿debería escribir el Gateway y Factory? ¿Es la puerta de enlace el mismo que el patrón de puente GOF?
- Yo diría que la puerta de enlace en el patrón de repositorio es otra variación del patrón de puente. De hecho, parece que la base del patrón del repositorio es un patrón puente más organizado y coordinado que se centra específicamente en el acceso a los datos. Y sí, ‘ probablemente necesitará escribir estos componentes usted mismo.
- Siguiendo los enlaces que publicó, llegué a en.wikipedia.org/wiki/ODB_(C%2B%2B) , ¿Cómo encaja este ODB en la imagen? ¿Puedo escribir un programa para convertir mi entrada de texto a sqllite y luego usar ODB para la persistencia? es este sonido? Tengo que decir que me encontré con ODB y miré dos ejemplos, por lo que este no es un comentario informado.
- La asistencia con la escritura de código está fuera del tema de este sitio, sin embargo, ‘ estoy seguro de que puede convertir el texto a sqlite y usar una biblioteca existente.
Respuesta
Un constructor sería «construido» dentro de un invariente de fábrica (método), para leer un archivo de texto, escribirías 1 fábrica con un método para leer dicho archivo. Ahora, si necesita construir su modelo de forma incremental a partir de los datos que lee (piense en datos anidados), el patrón de construcción dentro del método de fábrica le resultará muy útil. Sin embargo, un solo modelo probablemente será suficiente para cargar sus datos.
Comentarios
- Algún pseudo código puede ayudar a ser muy útil para escuchar. Nunca antes había usado patrones de fábrica o de constructor.
Respuesta
Dado el tamaño y la complejidad de su estructura, un constructor parece más apropiado, especialmente si su modelo es (o puede hacerse) inmutable. Tendrá que aceptar un acoplamiento estrecho entre el constructor y el modelo, pero vale la pena, especialmente si necesita construir modelos en varios lugares. Un constructor también le permite encapsular la validación.
Sin embargo, si su modelo es mutable y solo se crea en un lugar, entonces una fábrica podría ser más apropiada y conveniente y YAGNI entra en juego.
En respuesta a su otra pregunta , nunca debe almacenar punteros sin procesar en C ++. El hecho es que std::unique_ptr
no tiene inconvenientes y trivializa la destrucción de objetos. Cuando ese vector se sale del alcance, no liberará la memoria referencias como está escrito, pero con un unique_ptr, lo haría.
Crearía un vector para grandes cantidades de elementos repetidos, especialmente si es probable que la cantidad cambie, pero para componentes de tamaño mediano que nunca se comparten, también podría incluirlos directamente en la estructura.
Model
(o diferentes derivaciones de una clase base comúnModelBase
oIModel
), y desea centralizar la decisión sobre qué derivación concreta se instancia. No ‘ no veo este problema en tu pregunta, entonces, ¿cuál ‘ es tu idea detrás de usar una fábrica aquí?