zoukankan      html  css  js  c++  java
  • 所遇不良设计(一)

      我们在设计时,一定要考虑系统的未来的可扩展性,为未来做好准备,预备接口。现在软件的设计为了降低其耦合度,大多将软件设计成插件模式。

    1 扩展的常量

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

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

      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比较好。

    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换上新的“器械”了,这样子不用更改底层的代码,导致重新编译。

      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。

  • 相关阅读:
    phpwind管理权限泄露漏洞
    CGI Hack与Webshell研究资料整理
    深入浅出net泛型编程[转载]
    加上checkbox的treeview控件源程序
    BCB消息消息机制
    开源ZPU介绍
    带复选框可以多选的组合框控件 TCheckCombobox,非常完美
    别人用delphi写的很简单实用的多列功能的treeview treelistview
    智能DVR视频监控系统,源代码
    delphi事件参数sender的用法例程
  • 原文地址:https://www.cnblogs.com/wind-qu/p/3602862.html
Copyright © 2011-2022 走看看