2

目前,我们的系统使用类似于 Java Entity Beans 的东西,尽管它是用 C++/SQL 编写的。本质上,有些类(或多或少)象征着表格,这些类的实例等价于表格行。我想补充一点,这种方法一开始就有缺陷,请参阅这篇著名的文章:http: //blogs.tedneward.com/2006/06/26/The+Vietnam+Of+Computer+Science.aspx 另一方面,它运作良好,只要你接受它会导致一些不纯的东西,有时有点黑客。

然而,实际问题如下:虽然这些实体中有许多在内存占用方面相对较轻(十几列包含整数、浮点数和字符串)并且性能良好,但其中一些实际上并非如此。

  1. 有些包含二进制块,例如网格或图片。有人可能会争辩说,这些不应该一开始就存储在数据库中,但这是另一个话题。
  2. 有些实际上并不包含大量数据(以字节为单位),但是由于涉及的连接数量,获取完整集是一个非常大且相当慢的查询。

扭曲:这些“胖”对象经常在没有完整数据的情况下被使用。想象一下,您有一个“护照”类,其中包含生物特征数据、家庭关系树,还有姓名和出生日期。如果要显示护照列表,只需要基本数据。

我目前正在做的是创建一个 Passport 实例,但分两步填写。第一步仅添加简单字段,但将重字段保持打开状态(为 NULL)。稍后可以将该实例传递给将添加所有困难字段的函数。只要我没有犯错并在需要“完整”版本的地方使用“浅”实例,这将毫无问题。当然,我可以添加各种内部检查,但不仅扩展性很差(或多或少地为每个实体重新实现它们),而且还很容易出错。

因此,我的问题是:我想在编译时区分这两个版本,而不仅仅是在运行时。这样,我会在大多数错误发生之前就发现它们。

唯一可行的想法是将这两部分分成两个实体半,并将它们作为元组传递。如果第二个元组丢失,显然胖数据还没有加载。虽然这有效,但它会导致卑鄙的语法:

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

我得到的所有类型安全都是以可读性为代价的,这并不是一个很大的改进。目前,我没有更好的想法,并且怀疑这在 C++ 中实际上是不可能的,但我可能是错的。也欢迎非 C++ 的建议,将来可能会有更好的方法来做到这一点。当然,如果有人能很好地说明为什么这是不可能的,我也会接受。

4

2 回答 2

1

[编辑] 你的“旗帜”想法没关系。

在另一个答案中,我提供了一个替代解决方案,我个人认为它是一个更好的解决方案,但这并不意味着您的想法是错误的。

下面的例子,我确实在某些情况下应用了,它是相同的“标志”想法,一个可能的实现,你可能想要或不想遵循。

例子:

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

在这个例子中,有一个私有标志字段,叫做“F_IsHeavyLoaded”,它只能在构造函数中修改。它使用函数以只读方式公开提供。

干杯。

于 2013-02-21T17:56:17.417 回答
1

概述

让我提出一些处理“重”属性(如 blob、图像、文件)的想法。请记住,没有“一个解决方案”。我个人拒绝“加载所有重属性”标志的想法,并提出替代想法。

之前,我继续,请忽略小的语法或逻辑错误,并专注于代码示例的逻辑。

[1] 定义示例

首先,让我们从一个简单的例子开始:

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

首先,您不必加载或不加载“重”属性,因为“实体”模型或其他编程技术是这样说的。

事实上,我不会为重物添加标志,因为这意味着加载所有“重”属性或根本不加载任何“重”属性。而且,有时您可能需要加载其中的一些,但不是全部。

[2] 加载属性

通常,程序的逻辑指示何时加载属性。

对于每种情况,通常的做法是使用不同的构造函数:

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()

许多开发人员只使用“普通的轻量级构造函数”,并拒绝拥有多个构造函数的想法。

[3] 附加帮助

让我们暂时跳过“重”属性,稍后将继续。

这是许多 C/C++ 开发人员不喜欢的建议,但是,我个人觉得在处理实体对象时非常有用。我使用“两步初始化”。

对于每个实体类,我声明了一个构造函数,它没有清除字段的参数,并且添加了一个具有非常特定标识符的虚拟方法,它扮演构造函数的角色。

然后,我可能会添加几个充当构造函数的虚拟方法,例如决定是否加载“重”属性。

所以,前面的例子变成了这样:

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()

在前面的示例中,每个实体都是使用 2 个步骤创建的,即“虚拟”构造函数和一个补充方法,每种情况都不同,具有一个有意义的标识符,在选择如何准备实体对象时很有用。

每个对象的销毁也是如此。

[4] 重属性方法

最后,您可能希望添加一些方法,在需要时负责加载“重”属性。有时显式调用,有时自动调用。

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()

使用其他方法,您可能有一个忽略它们的 [fake] 构造函数,并且不加载“重”属性,一个调用它们的 [fake] 构造函数。或者,使用不使用它们的 [fake] 构造函数,并为特定的“重”属性显式调用加载程序。

如果您从文件系统路径加载图像并将它们保存到数据库字段中,这些也有帮助,反之亦然,从数据库字段加载文件并将它们保存到文件系统路径。

干杯。

于 2013-02-20T16:45:51.617 回答