C ++应用程序:模块设计 [英] C++ app: modules design

查看:178
本文介绍了C ++应用程序:模块设计的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的应用程序包含几个模块(大类),例如网络io,数据存储,控件等.其中一些模块可以配合使用.声明和绑定模块的好方法是什么?我看到几种可能性:

My app contains several modules (big classes) like network io, data storage, controls, etc. Some of them can cooperate. What would be a good way of declaring and binding the modules? I see several possibilities:

1)所有模块都声明为全局模块,因此我们在main.cpp中

1) All modules declared as global, so we have in main.cpp

#include ...
ModuleA a;
ModuleB b;
ModuleC c;

例如,如果a想与模块c对话,我们将在a.cpp中包含以下内容:

and if a wants to talk to module c for example, we will have in a.cpp the following:

#include "c.hpp"
extern ModuleC c;

以此类推;

2)所有在main()中声明的模块,因此它们是本地的.绑定是在构造函数中完成的:

2) All modules declared in main(), so they are local. The bindings are made in constructors:

int main() {
 ModuleC c;
 ModuleA a(c);
 ModuleB b;
}

但是以这种方式很难绑定想要彼此的对象(a(c),c(a))

but in this way would be hard to bind objects which want each other ( a(c), c(a) )

3)第一阶段:在本地声明,第二阶段:与指针绑定:

3) First phase: declare locally, second phase: bind with pointers:

int main() {
 ModuleA a;
 ModuleB b;
 ModuleC c;
 a.Connect(&b);
 b.Connect(&a);
 c.Connect(&a, &b);
}

有更好的方法吗?我希望它采用cpp风格.第三种方式保留指针有点混乱(尽管模块始终存在,但指针的有效性不会出现问题,但仍然存在),并且具有两阶段初始化,这不能保证我们不会忘记初始化一些模块,哎呀-无效的指针.如果某些对象需要交叉绑定,第二种方法(按照我的想法)可能会使所有想法崩溃.第一种方法似乎很自然(因为模块代表应用程序本身),但这不是不好的风格吗?我看到了一些项目,这些模块在某个Universe类中声明了这些模块,并且它们通过该Universe进行协作,就像所有模块都是全局的一样.你觉得呢?

Is there a better way? I'd like it to be in cpp style. Third way keeps pointers which is a bit confusing (though there won't be problems with validness of pointers though modules lives all the time, but still) and has two-phase initialization, which doesn't guarantee that we won't forget to init some module, and oops -- an invalid pointer. The second way (as i think) may crash all the idea if some objects would need cross-binding. The first way seems to be natural (since modules represent the app itself), but isn't it a bad style? I saw some projects where the modules were declared in some universe class and they cooperate via this universe just like it woule be if all of them are global. What do you think?

谢谢.

推荐答案

从计算机科学的角度来看,理想情况下,您希望尽可能减少耦合.从一般的开发角度来看,您希望减少在某处进行更改时所编译的代码量.

From a computer science point of view, you ideally want to decrease the coupling as much as possible. From a general development point of view, you want to reduce the amount of code that is compiled when you make a change somewhere.

因此,要解决此问题,您将需要使用接口和"universe"类.

So, to address this, you'll want to use interfaces and the 'universe' class.

main ()
{
   Universe my_app;
   ModuleA a (my_app);
   ModuleB b (my_app);
   ModuleC c (my_app);
}

class ModuleA : public ModuleAInterface
{
public:
  ModuleA (Universe &my_app) : m_my_app (my_app)
  {
    m_my_app.Register (this);
  }
private:
  Universe &m_my_app;
}

// etc...

class Universe
{
public:
  template <class T>
  void Register (T *module)
  {
    m_modules [T::ModuleID] = module;
  }

  template <class T>
  T *Module ()
  {
    return reinterpret_cast <T *> (m_modules [T::ModuleID]);
  }
private:
  std::map <int, void *> m_modules;
};

请注意,这意味着模块构造函数无法调用其他模块中的函数,因为实际上未定义构造顺序.

Note that this means that Module constructors can't call functions in other modules as the order of construction is effectively undefined.

要使用上述功能,ModuleA可能具有以下功能:

To use the above, ModuleA might have a function like:

void ModuleA::SomeFunction ()
{
  ModuleBInterface *b = m_my_app.Module <ModuleBInterface> ();
}

综上所述,这是一个示例程序,可以使用DevStudio 2005进行编译和运行(创建默认的空控制台应用程序):

So, putting it all together, here's a sample program that compiles and runs using DevStudio 2005 (create a default, empty console application):

#include <map>
#include <iostream>

class Universe
{
public:
  template <class T>
  void Register (T *module)
  {
    m_modules [T::ModuleID] = module;
  }

  template <class T>
  T *Module ()
  {
    return reinterpret_cast <T *> (m_modules [T::ModuleID]);
  }
private:
  std::map <int, void *> m_modules;
};

class ModuleAInterface 
{
public:
  static const unsigned ModuleID = 1;
  virtual ~ModuleAInterface () {};
};

class ModuleBInterface 
{
public:
  static const unsigned ModuleID = 2;
  virtual ~ModuleBInterface () {};
  virtual void OutputString (char *string) = 0;
};

class ModuleCInterface 
{
public:
  static const unsigned ModuleID = 3;
  virtual ~ModuleCInterface () {};
};

class ModuleA : public ModuleAInterface
{
public:
  ModuleA (Universe &my_app) : m_my_app (my_app)
  {
    m_my_app.Register (this);
  }
  void DoSomething ()
  {
    ModuleBInterface *b = m_my_app.Module <ModuleBInterface> ();
    b->OutputString ("Hello");
  }
private:
  Universe &m_my_app;
};

class ModuleB : public ModuleBInterface
{
public:
  ModuleB (Universe &my_app) : m_my_app (my_app)
  {
    m_my_app.Register (this);
  }
private:
  virtual void OutputString (char *string) { std::cout << string; }
  Universe &m_my_app;
};

class ModuleC : public ModuleCInterface
{
public:
  ModuleC (Universe &my_app) : m_my_app (my_app)
  {
    m_my_app.Register (this);
  }
private:
  Universe &m_my_app;
};

int main ()
{
  Universe my_app;
  ModuleA a (my_app);
  ModuleB b (my_app);
  ModuleC c (my_app);
  a.DoSomething ();
}

一旦代码被拆分,仅对接口的更改将导致大量重新编译,更改接口的实现将仅重新编译更改的模块,而无需重新编译.

Once the code has been split up, only changes to the interfaces will cause large recompilation, changing the implementation of the interfaces will only recompile the changed module, nothing else will need to be recompiled.

这篇关于C ++应用程序:模块设计的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆