C++类和接口的设计原则探讨

2016-02-19 14:04 3 1 收藏

人生本是一个不断学习的过程,在这个过程中,图老师就是你们的好帮手,下面分享的C++类和接口的设计原则探讨懂设计的网友们快点来了解吧!

【 tulaoshi.com - 编程语言 】


  我这篇文章的主旨是介绍一部分类和接口的高质量设计的准则。这些准则不但应该保证设计并且实现的类或者接口本身有高质量代码,而且更重要的是在工业领域应该尽可能的使代码的更新和维护不影响客户的活动,主要也就是保持二进制代码兼容(binary compatibility)和源代码兼容(source compatibility)。我希望这些准则能帮助刚从学校进入工业领域的朋友尽快适应更高标准的编程要求,尽快提升自己的设计能力。
  
  文中以C++类的设计为讨论范围。
  
  总提
  
  面向对象编程对于产出高质量,易维护的代码是非常有帮助的。面向对象编程的概念构建于三个基本特征之上:封装,继续,多态。在C++中,class是面向对象编程概念的核心和具体形式。class通过私有成员体现“封装”,通过直接继续或者组合体现“继续”,通过虚函数和动态绑定(dynamic binding)体现“多态”。class的设计质量直接决定了整个系统的质量。
  
  从整体功能层面谈class设计,有这么三条原则:
  
  ·单一功能原则(Single Responsibility Principle)
  
  一个class就其整体应该只提供单一的服务。假如一个class提供多样的服务,那么就应该把它拆分,反之,假如一个在概念上单一的功能却由几个class负责,这几个class应该合并。
  
  ·开放/封闭原则(Open/Close Principle)
  
  一个设计并实现好的class,应该对扩充的动作开放,而对修改的动作封闭。也就是说,这个class应该是答应扩充的,但不答应修改。假如需要功能上的扩充,一般来说应该通过添加新类实现,而不是修改原类的代码。添加新类不单可以通过直接继续,也可以通过组合。
  
  ·最小惊奇原理(Least Surprise Principle)
  
  在重载函数,或者子类实现父类虚函数时,应该基本维持函数原来所期望的功能。比如:
  
  class Pet {
   public:
  virtual Talk() = 0;
  };
  
  class Cat : public Pet {
   public:
  void Talk() { cout "miao"; }
  };
  
  class Dog : public Pet {
   public:
  void Talk() { BiteOwner(); }
  };
  class Dog 在实现虚函数Talk的时候,没有像我们期望的那样输出狗吠声,而是咬起主人来了。这是应该避免的。
  
  接口和实现
  
  在系统中,观察一个class有两个角度,从外部或者用户角度我们看到的是接口,从内部我们看到的是实现。因为系统肯定要不断修改,因此实现免不了不停的变化,但是接口又被要求尽量保持稳定。这两者的矛盾必须通过良好的设计尽量避免,基本原则就是将实现细节与接口隔离。下面列出几条比较具体点的:
  
  ·接口的设计保持最小而完整
  
  精简接口函数个数,使每一个函数有代表性,函数功能恰好覆盖class的职能。一个最小的接口可以使维护简单,增加潜在的代码重用性,减少客户的迷惑,并且也可以缩小头文件长度和编译时间。当改进函数时,应该用类似函数名实现改进而保留原函数,代码注释里应该有相应的说明。可以增加新函数,但不能删除旧函数。
  
  ·成员变量应该都为私有
  
  显而易见,public变量破坏封装性以及接口和实现的分离;protected变量也可能使客户编写继续类而依靠于父类的实现细节。
  
  ·避免函数返回成员变量的指针或引用
  
  这么做也会使客户代码依靠于实现细节。
  
  ·考虑是否禁用编译器缺省产生的函数
  
  这些函数包括:复制构造函数,赋值操作符(operator =)。假如我们不打算定义自己的版本而不禁用默认版本的话,可能使客户代码在不注重的情况下调用这些函数。当实现发生改动时就可能引起问题,比如class多了一个heap memory指针。假如我们答应对象拷贝,比较稳妥的方法是禁用它们,而定义一个专门的clone()函数。
  
  兼容性(compatibility)
  
  不用说,兼容性是非常重要的。Intel和Microsoft之所以如此成功,其中一个重要方面就是他们的产品,不管是硬件还是软件,都做到了很好的兼容老产品。代码的兼容也是如此。难以想象,假如客户依靠于你的library产品,而要因为你的产品的更新而不断的重写他的代码,他还会继续用你的产品。
  
  代码兼容可以简单分为二进制兼容和源代码兼容。二进制兼容也就是说,客户的已编译代码可以在不用重新编译的情况下,直接使用你的不同版本的已编译代码。源代码兼容就是,假如你的代码更新了,客户的代码不需要修改,只需要重新编译就可正常运行。在C++中,接口一般是由头文件和library二进制代码提供,因此,任何可能造成library代码和旧的头文件不一致的情况都可能破坏二进制兼容,因为客户代码必须和新的头文件重新编译一次。
  
  因此,遵循几条准则可以使你更轻松地解决兼容性问题:
  
  ·不改变类的大小或者改变成员变量的顺序
  
  包括几个方面:不增加或减少成员变量;不修改成员变量类型;不改变成员变量的声明顺序;不改变虚函数的有无。显而易见,增加或减少成员变量会改变类的大小,并且需要更新头文件,从而可能造成与客户代码不兼容。类型的变化也可能引起类的大小的变化。成员变量的访问一般是由编译器按偏移量确定,顺序假如改变,偏移量也就会改变,破坏了二进制兼容。至于虚函数的有无,决定是否存在虚函数表指针,也就影响了类的大小和成员变量的顺序。
  
  ·不使用inline函数
  
  inline函数声明于头文件中,并且被编译于客户代码中,假如inline函数访问了private成员,该成员又改变了顺序,那么inline函数虚要被重新编译,破坏了二进制兼容。
  
  ·接口函数不使用虚函数
  
  虚函数的访问和成员变量类似,是通过虚函数表中的偏移。虚函数顺序的改变会影响偏移。因此,在条件答应时,应该避免使用public虚函数。比如:
  
  
   class Picture {
   public:
  virtual void Draw();
  };
  应该改为
  
  class Picture {
   public:
  void Draw();
   private:
  virtual void DoDraw();
  };
  
  void Picture::Draw()
  {
   DoDraw();
  }
  ·不改变接口函数的顺序
  
  在很多嵌入式系统中,链接库通过输出函数表(eXPorted function table)暴露接口以节省空间。此时,对接口函数的访问也是通过索引值进行,因此改变顺序也会破坏兼容性。
  
  ·避免使用函数缺省参数
  
  给函数形参设定缺省值可以方便客户,但是可能破坏兼容。缺省值随头文件给出,缺省值的改变也就会引起兼容问题。
  
  以上就是我能想到的了,希望能对大家有帮助。

来源:http://www.tulaoshi.com/n/20160219/1606085.html

延伸阅读
一、 什么是VxD 从多任务操作系统Windows 3.1起,计算机中的任一物理设备x可同时被基于Dos或Windows的多个进程使用,这种一对多的关系称为"设备虚拟化",各进程通过运行在核心层的VxD(虚拟x设备驱动程序)存取物理设备x。操作系统提供给用户的软件服务也可以用VxD实现。计算机中的其他资源,如CPU、内存等也可同时被多个进程使用...
C++类对象的拷贝构造函数 作者:韩耀旭 对于普通类型的对象来说,它们之间的复制是很简单的,例如: int a=100;int b=a; 而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量。下面看一个类对象拷贝的简单例子。 #include <iostreamusing namespace std;class CA{public:CA(int b){a=b;}void Show (){c...
结构体和类有相同的特性,但又有很大的区别,类是构成面向对象编程的基础,但它是和结构体有着机器密切的关系。 我们在c语言中创建一个结构体我们使用如下方法: C++ 代码 //程序作者:管宁   //所有稿件均有版权,如要转载,请务必闻名出处和作者 strUCt test { priva...
实现LZARI压缩算法的C++类 作者: 阙荣文(querw) 下载源代码 这是一个基于LZARI算法的数据压缩的类.Haruhiko Okumura 于1989年7月4日用c语言写实现了这个算法.但是上面用到了一些全局或静态的变量,在MFC下用起来很不方便.我把它改写成了一个c++类,使它可以方便的压缩和解压缩,更重要的是,我新增加了两个...
如何在VC中导出类,这是一个常有人问起的问题,下面我以一个简单的例子来说明这个问题: 首先使用Wizard创建一个Win32 Dynamic-Link Library工程,然后定义一个简单的C++类CInDLL。由于该类会被工程之外的文件所引用,所以需要对这个类进行引出。因为只有引出后所生成的DLL中才带有供足够的信息以在连接和运行时被正确引入到进程空间...

经验教程

901

收藏

33
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部