Padrão de fábrica ou padrão de construtor? qual é adequado para ler dados de modelo de elemento finito de um arquivo de texto?

Uma continuação para outra pergunta ( Tomando uma decisão de projeto sobre a leitura de dados do modelo de um arquivo de entrada ).

Desejo fazer outra pergunta sobre o construtor ou o padrão de fábrica. (Eu li que o construtor é mais complexo do que a fábrica e talvez não precise usá-lo por enquanto). Portanto, aqui estão os dados que preciso ler:

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 ... 

Existem muitas outras tabelas como essas. Algumas tabelas têm relacionamentos pai e filho (cada sistema de coordenadas tem sua própria linha de grade). Eu fiz estruturas que representam cada tabela, 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; }; 

essas estruturas de dados são incorporadas à classe do modelo, o que fará uma classe enorme, pois há 50 estruturas de dados estranhas como esta:

class Model { private: ActiveDOF activeDOF; CoordinateSystem coordinateSystem; .... public: Model() {} ... } 

cada tabela tem que ter seu método definido e método get. Este design me preocupa porque se eu decidir mudá-lo, vai demorar muito. Agradeço qualquer sugestão. Acho que as informações aqui também colocarão a questão anterior sob uma luz melhor.

Agora, eu não sei para onde o construtor ou método de fábrica deve ir, dada a situação. Eu olhei alguns códigos e gráficos UML, mas não fui capaz de entender como implementar a fábrica ou construtor para fazer as estruturas necessárias para o meu projeto. Eu tenho que ter acesso a cada tabela por nome, porque elas podem estar sujeitas a alterações dentro do modelo, então por enquanto estou evitando fazer de cada uma delas uma subclasse de uma classe base virtual, para que eu possa armazená-los em um contêiner.

Além disso, faz sentido que, em vez de declarar uma instância da estrutura de dados, eu deva manter um ponteiro para eles? se todas as estruturas de dados forem derivadas de um classe base virtual chamada Record, então o modelo será mais ou menos assim:

class Model { private: ActiveDOF* activeDOF; CoordinateSystem* coordinateSystem; .... std::Vector<Record*> data; public: Model() {} ... } 

Acho que é um trabalho extra alocar, deslocar memória para eles, mas isso pode ajudar a gerenciar os dados e continuar digitando? Estou certo em presumir isso?

Comentários

  • I ‘ mn Não tenho certeza se a resposta é uma de suas escolhas. O padrão de fábrica é geralmente usado em combinação com o polimorfismo. O padrão do construtor é usado quando a construção do objeto é complexa. Você não está ‘ usando polimorfismo e cada estrutura é bastante simples. isso parece uma ” como faço para acessar os dados ” questão direta.
  • ” Factory ” e ” Builder ” resolvem problemas diferentes . ” Factory ” seria adequado se você tiver diferentes derivações da classe Model (ou diferentes derivações de uma classe base comum ModelBase ou IModel), e você deseja centralizar a decisão sobre qual derivação concreta será instanciada. Não ‘ não vejo esse problema em sua pergunta, então qual ‘ é sua ideia de usar uma fábrica aqui?

Resposta

Você está tentando resolver um problema de acesso a dados e isso requer uma solução de acesso a dados.

Primeiro, vamos examinar suas soluções propostas e por que elas não resolvem os problemas que você tem.

O Padrão de Fábrica

O Padrão de Projeto de Fábrica (também chamado de Padrão de Método de Fábrica) é útil quando você deseja utilizar polimorfismo e deseja separar a construção de objeto completamente das classes de implementação.

Wikipedia :

Na programação baseada em classe, o padrão do método de fábrica é um padrão de criação que usa métodos de fábrica para lidar com o problema de criação de objetos sem ter que especificar o classe exata do objeto que será criado. Isso é feito criando objetos chamando um método de fábrica — qualquer um especificado em uma interface e implementada por classes filhas ou implementada em uma classe base e opcionalmente substituída por classes derivadas — em vez de chamar um construtor .

(ênfase, minha)

E mais adiante, ele desenvolve os problemas que esse padrão resolve:

O padrão de projeto Factory Method resolve problemas como:

  • Como um objeto pode ser criado para que subclasses possam redefinir qual classe instanciar?
  • Como uma classe pode adiar a instanciação para subclasses?

Os problemas que você descreve em sua pergunta são diferentes destes. Você não está lidando com subclasses ou polimorfismo. O padrão de fábrica não é uma solução.

O padrão Builder

O padrão Builder (da Wikipedia ):

O Builder é um padrão de design projetado para fornecer uma solução flexível para vários problemas de criação de objetos na programação orientada a objetos. A intenção do padrão de design Builder é separar a construção de um objeto complexo de sua representação.

(ênfase minha)

Aqui, novamente, vemos uma incompatibilidade entre o problema que você está descrevendo e o uso pretendido desse padrão.

O padrão de design Builder resolve problemas como:

  • Como pode uma classe (o mesmo processo de construção) criar diferentes representações de um objeto complexo ?
  • Como pode uma classe que inclui a criação de um objeto complexo ser simplificado?

(ênfase, minha)

A chave aqui é a construção de um objeto (um objeto = 1 objeto) é complexo. Isso pode ser devido a um grande número de argumentos do construtor ou à complexidade de montar suas dependências.

Individualmente, cada estrutura ou classe é muito simples, portanto, o padrão do construtor também não é adequado.

Padrões de acesso a dados (a solução)

Os problemas que você na verdade estão tentando resolver são como executar o acesso aos dados e pode estar procurando por um Objeto de acesso a dados .

No software de computador, um objeto de acesso a dados (DAO) é um objeto que fornece uma interface abstrata para algum tipo de banco de dados ou outro mecanismo de persistência. Mapeando chamadas de aplicativos para a camada de persistência , o DAO fornece algumas operações de dados específicas sem expor detalhes do banco de dados.

No seu caso, substitua “banco de dados” por “arquivo de texto”. Um padrão de design relacionado é o Padrão de Design do Repositório , que divide o acesso aos dados em três soluções principais:

  • Repositório — a interface pública para “material de acesso a dados”
  • Gateway — sabe como se comunicar com um banco de dados Oracle ou ler / gravar um arquivo de texto. Isso incluiria a serialização de objetos e estruturas para SQL ou o formato de dados esperado no arquivo de texto.
  • Fábrica — mapeia os dados retornados pelo Gateway para os objetos e estruturas usado pelo resto do seu aplicativo

Propriedade da memória

Uma vez que você não tem a vantagem / limitação do gerenciamento automático de memória, propriedade da memória alocada para esses objetos e estruturas é definitivamente uma preocupação. Aqui você deve tomar uma decisão:

  • O objeto de acesso a dados é responsável por limpar esta memória?
  • O objeto de acesso a dados espera que o chamador (código do cliente) assuma a responsabilidade por liberar essa memória?

Se o objeto de acesso a dados for criado quando o aplicativo for iniciado e permanecer ativo até que o aplicativo seja encerrado, o acesso aos dados objeto pode se apropriar desta memória.

Se o objeto de acesso a dados for criado sob demanda e, em seguida, descartado, o código do cliente precisa ter certeza de que essa memória está limpa definido.

Defina isso nos comentários ou na documentação do objeto de acesso a dados e aplique isso na revisão do código.

Comentários

  • Gosto dessa abordagem. Parece muito mais flexível. Qualquer outra coisa que eu perguntar irá expor minha ignorância sobre o assunto. Devo implementar o DAO sozinho? Pelo que entendi, devo escrever o Gateway e Factory? O gateway é o mesmo que o padrão de ponte GOF?
  • Eu diria que o gateway no padrão de repositório é outra variação do padrão de ponte. Na verdade, parece que a base do padrão de repositório é um padrão de ponte mais organizado e coordenado focado especificamente no acesso a dados. E sim, você ‘ provavelmente precisará escrever esses componentes sozinho.
  • seguindo os links que você postou, cheguei a en.wikipedia.org/wiki/ODB_(C%2B%2B) , Como esse ODB se encaixa na imagem? Posso escrever um programa para converter minha entrada de texto em sqllite e usar ODB para persistência? é esse som? Devo dizer que me deparei com ODB e observei dois exemplos, portanto, este não é um comentário informado.
  • A assistência para escrever o código está fora do tópico deste site, no entanto, ‘ tenho certeza de que você pode converter o texto para sqlite e usar uma biblioteca existente.

Resposta

Um construtor seria “construído” dentro de um invariente de fábrica (método), para assim ler um arquivo de texto, você escreveria 1 fábrica com um método para ler o referido arquivo. Agora, se você precisar construir incrementalmente seu modelo a partir dos dados que lê (pense em dados aninhados), o padrão do construtor dentro do método de fábrica seria útil para você. No entanto, um único modelo provavelmente será suficiente para carregar seus dados.

Comentários

  • Algum pseudocódigo pode ajudar a ser muito útil ouvir. Nunca usei padrões de fábrica ou de construtor antes.

Resposta

Dado o tamanho e a complexidade de sua estrutura, um construtor parece mais apropriado, especialmente se o seu modelo for (ou puder ser) imutável. Você terá que aceitar algum acoplamento estreito entre o construtor e o modelo, mas vale a pena, especialmente se você precisar construir modelos em vários lugares. Um construtor também permite que você encapsule a validação.

No entanto, se o seu modelo for mutável e criado apenas em um local, uma fábrica pode ser mais apropriada e conveniente e o YAGNI entra em ação.


Em resposta à sua outra pergunta , você nunca deve armazenar ponteiros brutos em C ++. O fato é que std::unique_ptr não tem desvantagens e banaliza a destruição de objetos. Quando esse vetor sai do escopo, ele não libera a memória que referências conforme escritas, mas com um unique_ptr, sim.

Eu criaria um vetor para grandes quantidades de elementos repetidos, especialmente se a quantidade provavelmente mudasse, mas para componentes de tamanho médio que nunca são compartilhados, você também pode incluí-los diretamente na estrutura.

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *