所遇不良设计(一)

  咱们在设计时,必定要考虑系统的将来的可扩展性,为将来作好准备,预备接口。如今软件的设计为了下降其耦合度,大多将软件设计成插件模式。程序员

1 扩展的常量

  系统中出现大量的常量,随着系统模块的添加,会不断增长这些常量; 好比说日志的类型、货币的类型等等。而日志是每一个模块一定调用的模块,货币在好多涉及到支付的模块里面会被调用,当咱们要增长一个类型的时候,每每牵涉到其余模块,当编译的时候,其余模块也要相应的从新编译。增长一个宏,花了半个小时去编译,在项目中,我看到不少人抱怨。安全

   我遇到的问题(货币的常量):ide

  enum CURRENT_TYPE{
      GOLD_TYPE;
      .
      .
      .
      COIN_TYPE;
      ...
  }
View Code

  我认为,将要扩展的常量丢到配置文件里面,这样可能在速度以及存储上带来额外的开销。我以为对系统性能没多大的影响,配置文件通常在系统开启时加载好。函数

  "gold"=0
  .
  .
  .
  "coin"=n
  ....
View Code

  将上述配置加载到hashmap中去,这样子在调用的时候使用hashmap["gold"]来获取其类型。有人(多是一个acmer)就会说这没有宏高效,的确如此啊; 可是货币的数量毕竟是少的,这点速度牺牲不算什么,换来了灵活的可扩展性。性能

  注明: 一些结构不复杂的常量设置,我并非很喜欢xml,xml的表现太过复杂; 咱们能不能选择像httpd.conf(Apache的配置文件)那样简单明了。这可能也只是个人一厢情愿罢了,首先游戏的模板数据比较复杂,涉及到嵌套,这样简单的属性配置文件是表现不出数据关系的。同时为了达到统一性,最终会仍是xml比较好。spa

2 多变的函数

  有时候,咱们要调用上乘的接口,发现没有咱们要调用的接口,必需要到上层模块里面添加适合咱们业务的接口; 每遇到这种类似的业务,都要如此。这就说明上层的设计不够完美。要想一想咱们要让机器人可以使用锤子、钳子 扳手,咱们是否是要拆卸它的手臂,给它换一个手臂呢? 这样很麻烦,咱们何不为其设计接口,给它换上什么器械,它就能干不一样的活。在C++里面使用多态,也可使用模块; 在C里面就是传送同一类型的函数指针。插件

  C++的第一种模式:设计

  //底层
  class Base {
      virtual T Func (Arg* arg);
  };
  
  void Interface (Base* a, Arg* p_arg){ //这是接口
      a->Func(p_arg);
  }
  
  //扩展
  class ABase : public Base {
      virtual T Func (Arg* p_arg);
  }

  ABase* pABase;
  Interface(pABase, p_arg);
View Code

  咱们只须要不断的继承Base而且从新写Func就能给Interface换上新的“器械”了,这样子不用更改底层的代码,致使从新编译。3d

  C++的第二种模式:指针

  template <class T>
  void Interaface(T* p_t, Arg* p_arg){ //这是接口
      p_t->Func(p_arg);
  }
View Code

  只要写一个类型而且实现Func函数,不须要继承任何东西,就给Interface换上新的“器械”了,一样也减小了编译时间。

  总结:上述C++的两种方法,我更加倾向于运用多态,运用模板,你就必须把你的类申明和实现所有丢到头文件里面,这样子发布API的时候,别人会看到你的实现,并且头文件会显得臃肿。

  咱们再来讲说C的实现,C的实现很简单,就是传送相同的函数指针。

  void Interface (T (*) (Arg* p_arg))
View Code

  这样子,你只须要在外部重写这个函数传进去,就OK了,函数名字能够不同,可是类型不同,也是一种很酷的方法。其实这种方式咱们在STL的一些库函数里面常常看到,好比遍历:

  void Traverse (iterator<T> beg, iterator<T> end, void (*oper) (T& a)); 
View Code

  只须要变换oper,咱们能够对容器里必定范围内的元素进行显示,变化他们的值等。

3 强势的friend

  哥们你要调用我,得要先申请friend,一副盛气凌人的样子。我要说可能每一个人负责某个模块,谁会料到某人的离职或者生病了,的确每一个人写的模块,其本人是最清楚的。整个项目是整个团队的,而不是某我的的。有些人喜欢将本身原本应该开放给其余模块的API设置为protected和private; 觉得这样会很安全,禁止别人调用,我以为调用又如何了,又不是会涉及到支付以及破坏系统,更况且后台系统,不会把API暴露给外部,只是本系统内部使用。何况为了保护本身的API,把其设置为protected,我以为是隔靴搔痒。看看我下面是如何破解,哥们我不求你把我当作friend:

  底层系统类A,咱们须要调用其Func。

  class A {
  protected:
      void Func() {
          ....
      }
  }
View Code

  咱们的破解招数以下:

  class B:public A {
  public:
      void Func(){
          A::Func();
      }
  }
  
  B b;
  b.Func()
View Code

  虽然这样子绕了一个圈子,不如直接调用来得好,咱们仍是没有作friend,就获取了A的资源; 要作到保护,作绝点,应该就是private了。哥们要放低架势,要是好多模块都要调用你的模块,每次要在你的头文件里面加入friend,会致使大量从新编译的。这也是我讨厌friend的缘由,要作咱就作好基友或者好闺蜜。

4 层叠的工程

  作项目的过程当中,你们确定遇到过这样问题,就是遇到指针都要判断空,造成了强迫症,就是怕访问到空指针,致使系统崩溃的。即便是底层已经帮咱们作了,我仍是要判断。咱们先来看看情形吧。

  bool Func1 (Player* pPlayer, ...) {
      if (NULL == pPlayer) {
          return false;
      }
      ...
  }

  bool Func2 (Player* pPlayer, ...) {
      if (NULL == pPlayer) {
          return false;
      }
      ...
      if(false == Func1(pPlayer, ....) {
         return false;
      }
      ...
  }

  
  bool Func3 (Player* pPlayer, ...) {
      if (NULL == pPlayer) {
          return false;
      }
      ...
      if(false == Func2(pPlayer, ....) {
         return false;
      }
      ...
  }
View Code

  首先,调用接口者,可能没有去研究所调用的接口吧,还有可能有那种强迫症吧,才如此作的吧。程序员主要是怕系统崩溃了,找到本身的头上吧。若是应要判断空,应该整个系统来约定一下,最上层来判断或者最底层判断。其余时候不判断的; 我仍是以为应该由底层来判断的,这样子可以让系统更加健壮。还有我以为能够不用返回false的,其实一个函数里面返回false的因素比较多,返回给外部都不知道里面究竟是什么缘由致使的false。我以为作成异常抛出可能更加好一些,不一样因素致使返回失败,抛出不一样的异常,这样子好跟踪错误。同时这样子约定,若是该函数不须要处理异常,那也把异常给扔出去,直到谁调用要处理异常,才try…catch。

相关文章
相关标签/搜索