2013-02-20 3 views
2

Actuellement, notre système utilise quelque chose de similaire à Java Entity Beans, bien qu'il soit écrit en C++/SQL. Essentiellement, il existe des classes qui symbolisent (plus ou moins) les tables, et les instances de ces classes sont équivalentes aux tables-lignes. Je veux ajouter que cette approche est imparfaite pour commencer, voir cet essai célèbre: http://blogs.tedneward.com/2006/06/26/The+Vietnam+Of+Computer+Science.aspx D'autre part, cela fonctionne bien, tant que vous acceptez qu'il en résultera quelque chose qui est impur et un peu un hack à fois. Néanmoins, le problème réel est le suivant: Bien que plusieurs de ces entités soient relativement légères en termes d'encombrement mémoire (une douzaine de colonnes contenant des ints, des floats et des chaînes) et donnent de bonnes performances, certaines ne le sont vraiment pas.Objets Entity sécurisés pour le type avec des données importantes

  1. Certains contiennent des blobs binaires tels que des maillages ou des images. On pourrait argumenter que ceux-ci ne devraient pas être stockés dans la base de données pour commencer, mais c'est un autre sujet.
  2. Certains ne contiennent pas beaucoup de données (en octets), mais l'extraction de l'ensemble complet est une requête très grande et plutôt lente en raison du nombre de jointures impliquées.

La torsion: Ces objets «gras» sont souvent utilisés sans leurs données complètes. Imaginez que vous avez une classe "Passeport" qui contient des données biométriques, des arbres de relations familiales, mais aussi le nom et la date de naissance. Si vous voulez afficher une liste de passeports, vous n'avez besoin que des données de base.

Ce que je fais actuellement est de créer une instance de passeport, mais le remplir en deux étapes. La première étape ajoute seulement les champs faciles, mais laisse les champs lourds ouverts (comme NULL). L'instance peut ensuite être passée à une fonction qui ajoutera tous les champs difficiles. Cela fonctionne sans accroc, tant que je ne me trompe pas et que j'utilise une instance «superficielle» où j'ai besoin de la version «complète». Bien sûr, je peux ajouter toutes sortes de contrôles internes, mais non seulement cette échelle est mauvaise (les réimplémenter pour chaque entité, plus ou moins), elle est également très sujette aux erreurs.

Mon problème est donc: Je voudrais distinguer les deux versions au moment de la compilation, pas seulement au moment de l'exécution. De cette façon, j'attraperais la plupart des erreurs avant qu'elles ne se produisent.

La seule idée qui semble fonctionner est de diviser les deux parties en deux moitiés d'entité, et de les passer comme des tuples. Si le deuxième tuple est manquant, il est évident que les données de graisse n'ont pas encore été chargées. Bien que cela fonctionne, il en résulte une syntaxe ignobles:

std::vector< EntityTuple<EmptyPassport, FullPassport>> 

Et tout sécurité de type que je reçois est au prix de la lisibilité, ce qui est une grande amélioration. Actuellement, je n'ai pas de meilleures idées, et je suspecte que cela soit réellement impossible en C++, mais je peux me tromper. Non-C++ - les suggestions sont les bienvenues aussi, il pourrait y avoir une meilleure façon de le faire à l'avenir. Bien sûr, si quelqu'un peut expliquer pourquoi c'est impossible, je l'accepterai aussi.

Répondre

1

Vue d'ensemble

Permettez-moi de vous suggérer quelques idées pour gérer les propriétés "lourds" comme blobs, les images, les fichiers. Rappelez-vous, il n'y a pas "une solution pour tous". Personnellement, je rejette l'idée d'un drapeau «charger toutes les propriétés lourdes», & suggérer des idées alternatives. Avant de poursuivre, je continue, s'il vous plaît, ignorez les petites erreurs de syntaxe ou de logique et concentrez-vous sur la logique des exemples de code.

[1] Définir Exemple

Tout d'abord, nous allons commencer par un exemple simple:

public class EmployeeClass 
{ 
    public: 
    int  Key; 
    char FirstName[150]; 
    char LastName[150]; 
    Image* Photo; // <- picture 
    Blob* Contract; // <- scanned contract 
}; // class 

Tout d'abord, vous ne devez pas charger ou ne se charge pas, le "lourd" propriétés, parce que le modèle "Entité" ou une autre technique de programmation le dit.

En fait, je n'ajouterais pas de drapeau pour les objets lourds, car cela signifie charger toutes les propriétés "lourdes" ou ne charger aucune propriété "lourde". Et, parfois, vous devrez peut-être charger certains d'entre eux, mais pas tous.

[2] Propriétés de charge

Habituellement, la logique d'un programme, indique, au moment de charger les propriétés.

La pratique courante d'utiliser un son constructeur différent, pour chaque cas:

public class EmployeeClass 
{ 
    public: 
    int  Key; 
    char FirstName[150]; 
    char LastName[150]; 
    Image* Photo; 
    Blob* Contract; 

    public: 
    // --> generic constructor 
    EmployeeClass() 
    { 
     Key = 0; 
     strcpy(FirstName, ""); 
     strcpy(LastName, ""); 
     Photo = null; 
     Contract = null; 
    } // EmployeeClass() 

    // --> "light" constructor 
    EmployeeClass 
    (
     int AKey, 
     char* AFirstName, 
     char* ALastName 
    ) 
    { 
     Key = AKey; 
     strcpy(FirstName, AFirstName; 
     strcpy(LastName, ALastName); 
     Photo = null; 
     Contract = null; 
    } // EmployeeClass() 

    // --> "heavy" constructor 
    EmployeeClass 
    (
     int AKey, 
     char* AFirstName, 
     char* ALastName, 
     Image* APhoto, 
     Blob* AContract 
    ) 
    { 
     Key = AKey; 
     strcpy(FirstName, AFirstName; 
     strcpy(LastName, ALastName); 
     Photo = APhoto; 
     Contract = AContract; 
    } // EmployeeClass() 

    void Insert(); 
}; // class 

void Test() 
{ 
    ... 
    int AKey = 0; 
    char AFirstName[150]; 
    char ALastName[150]; 
    Image* APhoto = null; 
    Blob* AContract = null; 

    // --> calling "light" constructor 

    AKey = 1; 
    strcpy(AFirstName, "Mary"); 
    strcpy(ALastName, "Thompson"); 

    EmployeeClass* AEmployee = new EmployeeClass 
     (AKey, AFirstName, ALastName); 

    AEmployee->Insert(); 

    // --> calling "heavy" constructor 

    AKey = 2; 
    strcpy(AFirstName, "John"); 
    strcpy(ALastName, "Doe"); 

    Image* APhoto = LoadPhoto(); 
    Blob* AContract = LoadContract(); 

    EmployeeClass* AEmployee = new EmployeeClass 
     (AKey, AFirstName, ALastName, APhoto, AContract); 

    AEmployee->Insert(); 

    // --> calling "dummy" constructor, 
    // --> more work, but, more flexible 

    AKey = 1; 
    strcpy(AFirstName, "Mary"); 
    strcpy(ALastName, "Thompson"); 

    EmployeeClass* AEmployee = new EmployeeClass(); 
    AEmployee->Key = AKey; 
    strcpy(AEmployee->FirstName, AFirstName); 
    strcpy(AEmployee->LastName, ALastName); 
    AEmployee->Photo = LoadPhoto(); 
    AEmployee->Contract = LoadContract(); 
    AEmployee->Insert(); 

    ... 
} // void Test() 

De nombreux développeurs utilisent uniquement le « constructeur de lumière commun », & rejette l'idée d'avoir plusieurs constructeurs.

[3] Aide supplémentaire

Sautons, pour un moment, les propriétés "lourds", se poursuivra plus tard.

Ceci est une suggestion que de nombreux développeurs C/C++ n'aiment pas, mais, personnellement, je trouve très utile lorsqu'il s'agit d'objets d'entité. J'utilise "l'initialisation en deux étapes".

Pour chaque classe d'entité, je déclare un constructeur, sans paramètre qui efface les champs, et, ajoute, une méthode virtuelle, avec un identifiant très spécifique, qui prend le rôle de constructeur.

Ensuite, je peux ajouter plusieurs méthodes virtuelles qui fonctionnent comme des constructeurs, comme décider wheter charger les propriétés « lourds » ou non.

Ainsi, l'exemple précédent devient quelque chose comme ceci:

public class EmployeeClass 
{ 
    public: 
    bool F_EmployeeClass_IsReady; 
    public: 
    int  Key; 
    char FirstName[150]; 
    char LastName[150]; 
    Image* Photo; 
    Blob* Contract; 

    public: 
    // --> only generic constructor 
    Employee() 
    { 
     F_EmployeeClass_IsReady = false; 

     Key = 0; 
     strcpy(FirstName, ""); 
     strcpy(LastName, ""); 
     Photo = null; 
     Contract = null; 
    } // EmployeeClass() 

    virtual bool IsReady() 
    { 
     return F_EmployeeClass_IsReady; 
    } // bool IsReady(...) 

    // --> works like "generic" constructor from previous example 
    virtual void Create() 
    { 
     Key = 0; 
     strcpy(FirstName, ""); 
     strcpy(LastName, ""); 
     Photo = null; 
     Contract = null; 

     F_EmployeeClass_IsReady = true; 
    } // void Create() 

    // --> works like "light" constructor from previous example 
    virtual void CreateLight 
     (
     int AKey, 
     char* AFirstName, 
     char* ALastName 
    ) 
    { 
     Key = AKey; 
     strcpy(FirstName, AFirstName); 
     strcpy(LastName, ALastName); 
     Photo = null; 
     Contract = null; 

     F_EmployeeClass_IsReady = true; 
    } // void CreateLight() 

    virtual void Destroy() 
    { 
     F_EmployeeClass_IsReady = false; 
    } // void Destroy() 

    // --> works like "heavy" constructor from previous example 
    virtual void CreateHeavy 
     (
     int AKey, 
     char* AFirstName, 
     char* ALastName, 
     Image* APhoto, 
     Blob* AContract 
    ) 
    { 
     Key = AKey; 
     strcpy(FirstName, AFirstName); 
     strcpy(LastName, ALastName); 
     Photo = APhoto; 
     Contract = AContract; 

     F_EmployeeClass_IsReady = true; 
    } // void CreateHeavy() 

    void Insert(); 
}; // class 

void Test() 
{ 
    ... 
    int AKey = 0; 
    char AFirstName[150]; 
    char ALastName[150]; 
    Image* APhoto = null; 
    Blob* AContract = null; 

    // --> calling "light" constructor 

    AKey = 1; 
    strcpy(AFirstName, "Mary"); 
    strcpy(ALastName, "Thompson"); 

    EmployeeClass* AEmployee = new EmployeeClass(); 
    AEmployee->CreateLight(AKey, AFirstName, ALastName); 

    AEmployee->Insert(); 

    AEmployee->Destroy(); 
    delete AEmployee; 

    // --> calling "heavy" constructor 

    AKey = 2; 
    strcpy(AFirstName, "John"); 
    strcpy(ALastName, "Doe"); 

    Image* APhoto = LoadPhoto(); 
    Blob* AContract = LoadContract(); 

    EmployeeClass* AEmployee = new EmployeeClass(); 
    AEmployee->CreateHeavy 
    (AKey, AFirstName, ALastName, APhoto, AContract); 

    AEmployee->Insert(); 

    AEmployee->Destroy(); 
    delete AEmployee; 

    // --> calling "dummy" constructor, 
    // --> more work, but, more flexible 

    AKey = 1; 
    strcpy(AFirstName, "Mary"); 
    strcpy(ALastName, "Thompson"); 

    EmployeeClass* AEmployee = new EmployeeClass(); 
    AEmployee->Create(); 

    AEmployee->Key = AKey; 
    strcpy(AEmployee->FirstName, AFirstName); 
    strcpy(AEmployee->LastName, ALastName); 
    AEmployee->Photo = LoadPhoto(); 
    AEmployee->Contract = LoadContract(); 

    AEmployee->Insert(); 

    AEmployee->Destroy(); 
    delete AEmployee; 

    ... 
} // void Test() 

Dans l'exemple précédent, chaque entité est créée à l'aide de 2 étapes, le constructeur « factice », et une méthode complémentaire, différente pour chaque cas, avec un identifiant significatif, utile pour choisir comment préparer un objet entité.

Il en va de même pour la destruction de chaque objet.

[4] Méthodes Propriétés lourd

Enfin, vous voudrez peut-être ajouter un peu de méthode qui sont en charge de chargement des propriétés « lourdes » en cas de besoin. Parfois sont appelés explicitement, et parfois, appelés automatiquement.

public class EmployeeClass 
{ 
    public: 
    bool F_EmployeeClass_IsReady; 
    public: 
    int  Key; 
    char FirstName[150]; 
    char LastName[150]; 
    Image* Photo; 
    Blob* Contract; 

    public: 
    // --> only generic constructor 
    Employee() 
    { 
     F_EmployeeClass_IsReady = false; 

     Key = 0; 
     strcpy(FirstName, ""); 
     strcpy(LastName, ""); 
     Photo = null; 
     Contract = null; 
    } // EmployeeClass() 

    virtual bool IsReady() 
    { 
     return F_EmployeeClass_IsReady; 
    } // bool IsReady(...) 

    void LoadPhoto(); 
    void SavePhoto(); 

    void LoadContract(); 
    void SaveContract(); 

    // --> works like "generic" constructor from previous example 
    virtual void Create() 
    { 
     Key = 0; 
     strcpy(FirstName, ""); 
     strcpy(LastName, ""); 
     Photo = null; 
     Contract = null; 

     F_EmployeeClass_IsReady = true; 
    } // void Create() 

    // --> works like "light" constructor from previous example 
    virtual void CreateLight 
     (
     int AKey, 
     char* AFirstName, 
     char* ALastName 
    ) 
    { 
     Key = AKey; 
     strcpy(FirstName, AFirstName); 
     strcpy(LastName, ALastName); 
     Photo = null; 
     Contract = null; 

     F_EmployeeClass_IsReady = true; 
    } // void CreateLight() 

    virtual void Destroy() 
    { 
     F_EmployeeClass_IsReady = false; 
    } // void Destroy() 

    // --> works like "heavy" constructor from previous example 
    virtual void CreateHeavy 
     (
     int AKey, 
     char* AFirstName, 
     char* ALastName, 
     Image* APhoto, 
     Blob* AContract 
    ) 
    { 
     Key = AKey; 
     strcpy(FirstName, AFirstName); 
     strcpy(LastName, ALastName); 
     Photo = APhoto; 
     Contract = AContract; 

     F_EmployeeClass_IsReady = true; 
    } // void CreateHeavy() 


    // --> works like "heavy" constructor from previous example 
    virtual void CreateAndLoad 
     (
     int AKey, 
     char* AFirstName, 
     char* ALastName 
    ) 
    { 
     Key = AKey; 
     strcpy(FirstName, AFirstName); 
     strcpy(LastName, ALastName); 

     LoadPhoto(); 
     LoadContract; 

     F_EmployeeClass_IsReady = true; 
    } // void CreateAndLoad() 

    void Insert(); 
}; // class 

void Test() 
{ 
    ... 
    int AKey = 0; 
    char AFirstName[150]; 
    char ALastName[150]; 
    Image* APhoto = null; 
    Blob* AContract = null; 

    // --> calling "load" constructor 

    AKey = 1; 
    strcpy(AFirstName, "Mary"); 
    strcpy(ALastName, "Thompson"); 

    EmployeeClass* AEmployee = new EmployeeClass(); 
    AEmployee->CreateLoad(AKey, AFirstName, ALastName); 

    AEmployee->Insert(); 

    AEmployee->Destroy(); 
    delete AEmployee; 

    ... 
} // void Test() 

Avec les méthodes supplémentaires, vous pouvez avoir un constructeur [fake] qui les ignore, et ne pas charger les propriétés « lourds », un constructeur [fake] qui les appellent.Ou, utilisez le constructeur [faux] qui ne les utilise pas, & appelez explicitement le chargeur pour une propriété "lourde" spécifique.

Ceux-ci, aide aussi, si vous chargez des images à partir d'un chemin de système de fichiers, & les sauver dans un champs de base de données, ou vice-versa, charger des fichiers à partir d'un champ de base de données, & les sauver à un chemin de système de fichiers.

Cheers.

+0

Merci pour l'explication détaillée. C'est très proche de ce que nous faisons actuellement, et cela fonctionne assez bien. Il n'est cependant pas sûr de la manière dont il gère le contenu lourd. –

+0

@Kajetan Abt: Mon sugg. était destiné à ("penser différemment"), gérer les propriétés d'une manière alternative, savoir dès le début où il va exiger ces propriétés, et ne pas détecter après, si elles ont été chargées. Bonne chance. – umlcat

1

[EDIT] Votre idée de "drapeau" c'est ok.

Dans une autre réponse, je propose une solution alternative, personnellement, je pense que c'est une meilleure solution, mais, ne signifie pas que votre idée est fausse.

L'exemple suivant, que j'ai appliqué dans certains cas, c'est la même idée de «drapeau», une implémentation possible, que vous voudrez ou ne voulez pas suivre.

Exemple:

public class EmployeeClass 
{ 
    // --> logic fields 
    private: 
    bool F_HeavyLoaded; 

    // --> "entity" fields 
    public: 
    int  Key; 
    char FirstName[150]; 
    char LastName[150]; 
    Image* Photo; // <- picture 
    Blob* Contract; // <- scanned contract 

    public: 
    // --> constructors 

    EmployeeClass 
    (
     int AKey, 
     char* AFirstName, 
     char* ALastName 
    ) 
    { 
     Key = AKey; 
     strcpy(FirstName, AFirstName; 
     strcpy(LastName, ALastName); 
     Photo = null; 
     Contract = null; 

     F_HeavyLoaded = false; 
    } // EmployeeClass() 

    // --> "heavy" constructor 
    EmployeeClass 
    (
     int AKey, 
     char* AFirstName, 
     char* ALastName, 
     Image* APhoto, 
     Blob* AContract 
    ) 
    { 
     Key = AKey; 
     strcpy(FirstName, AFirstName; 
     strcpy(LastName, ALastName); 
     Photo = APhoto; 
     Contract = AContract; 

     F_HeavyLoaded = true; 
    } // EmployeeClass() 

    public: 
    // --> "entity" methods 


    bool IsHeavyLoaded() { return F_HeavyLoaded; } 

    void Insert(); 
    void Update(); 
    void Delete(); 
}; // class 

Dans cet exemple, il y a un champ de drapeau privé, appelé « F_IsHeavyLoaded », il peut être modifié que dans les constructeurs. Son publiquement disponible en lecture seule, en utilisant une fonction.

Cheers.

+0

Encore une fois, cela fonctionne, mais cela ne résout pas mon problème que je veux vérifier * au moment de la compilation * si une entité est chargée ou non. –

Questions connexes