分析一套源代码的代码规范和风格并讨论如何改进优化代码

---恢复内容开始---

 我的工程实践题目是基于opengl的车载虚拟仪表软件开发,是一项校企合作的项目,在项目中以前做过的学长也向我们分享了以往做过的相关的项目的源代码,代码主要是由c语言写出,通过调用opengl的api接口规范来实现的图像旋转,拉伸,平移等操作,源代码如下 :

#include "config.h"
#include "main.h"
#include "./lib/lib_opengl.h"
#include "./font.h"
#include "./resource.h"

#ifdef DEBUG_LOCATION
#undef DEBUG_LOCATION
#endif
#define DEBUG_LOCATION "MAIN"

pthread_t taskTimer;
extern void *threadTimer(void);

#define SIZE_OF_BOX 150
int main(int argc, char* argv[])
{
float rotate = 0;
FT_ULong strW[10] = {0};
u32 delay = 0;
InitGL();
initFont();
initResource();
loadImage(IDIMG_BG);

enableGlStatus();

//多线程
//pthread_create(&taskTimer, NULL, threadTimer, NULL)

while(1)
{
delay++;
if(delay > 1000)
{
if(rotate < 9.0)
rotate += 1.0;
else
rotate = 0;

delay = 0;

glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(0.0f, 0.0f, 0.0f, 255.0f);
#if 0
glPushMatrix();
//themeHandle();

glTranslatef(960, 360, 0);
glDisable(GL_TEXTURE_2D);
glRotatef(45, 1, 0, 0);
glRotatef( rotate, 0, 1, 0);
//glVertexPointer(3, GL_FLOAT, 0, vertices);

glColor4f(255, 0, 0, 255);
static GLfloat front[] = { SIZE_OF_BOX, -SIZE_OF_BOX, SIZE_OF_BOX,SIZE_OF_BOX, SIZE_OF_BOX, SIZE_OF_BOX,-SIZE_OF_BOX, -SIZE_OF_BOX, SIZE_OF_BOX,-SIZE_OF_BOX, SIZE_OF_BOX, SIZE_OF_BOX,};
glVertexPointer(3, GL_FLOAT, 0, front);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

glColor4f(0, 255, 0, 255);
static GLfloat back[] = { -SIZE_OF_BOX, -SIZE_OF_BOX, -SIZE_OF_BOX,-SIZE_OF_BOX, SIZE_OF_BOX, -SIZE_OF_BOX,SIZE_OF_BOX, -SIZE_OF_BOX, -SIZE_OF_BOX,SIZE_OF_BOX, SIZE_OF_BOX, -SIZE_OF_BOX,};
glVertexPointer(3, GL_FLOAT, 0, back);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

glColor4f(0, 0, 255, 255);
static GLfloat top[] = {SIZE_OF_BOX, SIZE_OF_BOX, SIZE_OF_BOX, SIZE_OF_BOX, SIZE_OF_BOX, -SIZE_OF_BOX,-SIZE_OF_BOX, SIZE_OF_BOX, SIZE_OF_BOX, -SIZE_OF_BOX, SIZE_OF_BOX, -SIZE_OF_BOX,};
glVertexPointer(3, GL_FLOAT, 0, top);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

glColor4f(255, 255, 0, 255);
static GLfloat bottom[] = { -SIZE_OF_BOX, -SIZE_OF_BOX, SIZE_OF_BOX,-SIZE_OF_BOX, -SIZE_OF_BOX, -SIZE_OF_BOX,SIZE_OF_BOX, -SIZE_OF_BOX, SIZE_OF_BOX,SIZE_OF_BOX, -SIZE_OF_BOX, -SIZE_OF_BOX, };
glVertexPointer(3, GL_FLOAT, 0, bottom);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

glColor4f(0, 255, 255, 255);
static GLfloat left[] = {-SIZE_OF_BOX, -SIZE_OF_BOX, SIZE_OF_BOX, -SIZE_OF_BOX, SIZE_OF_BOX, SIZE_OF_BOX, -SIZE_OF_BOX, -SIZE_OF_BOX, -SIZE_OF_BOX,-SIZE_OF_BOX, SIZE_OF_BOX, -SIZE_OF_BOX,};
glVertexPointer(3, GL_FLOAT, 0, left);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

glColor4f(255, 0, 255, 255);
static GLfloat right[] = {SIZE_OF_BOX, -SIZE_OF_BOX, -SIZE_OF_BOX, SIZE_OF_BOX, SIZE_OF_BOX, -SIZE_OF_BOX,SIZE_OF_BOX, -SIZE_OF_BOX, SIZE_OF_BOX,SIZE_OF_BOX, SIZE_OF_BOX, SIZE_OF_BOX,};
glVertexPointer(3, GL_FLOAT, 0, right);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

glPopMatrix();
#endif

glPushMatrix();
glEnable(GL_TEXTURE_2D);

drawImage(IDIMG_BG, 0, 0);
setFontColor(255, 255, 255, 255);

memset(strW, 0, sizeof(strW));
sprintl(strW, "%.0f", rotate);
drawFont(FONT_GEELY, 100, 100, 32, strW);

memset(strW, 0, sizeof(strW));
sprintl(strW, "%.0f", 9.0 - rotate);
drawFont(FONT_GEELY, 100, 150, 32, strW);

setFontColor(255, 255, 255, 255);
glPopMatrix();

swapBuffers();
}
}

return 0;
}

源代码的结构如下:

文件名/类名/函数名/变量名等命名:

文件/类/函数的命名方式主要根据代码实现的功能来命名的,

类名中主要是调用了opengl库中的一些常用的类如下:

  • BufferUtils
  • TextureUtils
  • Shader
  • GLSurfaceView
  • Renderer
  • Model
  • GLBitmap
  • GLUnfoldBitmap

函数名中  glClear主要用于清除指定的缓存数据并重设为当前的清除值。mask是一个可以通过逻辑或操作来指定多个数值的参数;

                glClearClolor用于设置当前使用的清除颜色值,用于RGBA模式下对颜色缓存的清除工作。这里的red,green,blue,alpha都会被截断到【0,1】的范围内。默认的清除颜色是(0,0,0,0)表示黑色。

                glPushMatrix();glPushMatrix、glPopMatrix操作其实就相当于栈里的入栈和出栈。

                void glTranslatef(GLfloat x,GLfloat y,GLfloat z);函数功能:沿X轴正方向平移x个单位(x是有符号数)沿Y轴正方向平移y个单位(y是有符号数)沿Z轴正方向平移z个单位(z是有符号数)。
                glEnable/glDisable —— 开启和关闭服务器端GL功能               
                void glRotatef(GLfloat angle,GLfloat x,GLfloat y,GLfloat z);先解释一下旋转方向,做(0,0,0)到(x,y,z)的向量,用右手握住这条向量,大拇指指向向量的正方向,四指环绕的方向就是旋转的方向;函数功能:以点(0,0,0)到点(x,y,z)为轴,旋转angle角度; 
                glColor4f:其参数类型与glClear相同。只是此函数设定的是绘笔的颜色,即绘制出的图形颜色值。

哪些做法符合代码规范和风格一般要求:

    • 类的属性和方法 必须添加访问修饰符(private 、protected 以及 public ),abstract 以及 final 必须声明在访问修饰符之前,而 static必须声明在访问修饰符之后。
    • 控制结构的关键字后 必须要有一个空格符,而调用方法或函数时则一定不能有。
    • 控制结构的开始花括号 ({ )必须写在声明的同一行,而结束花括号 (} )必须写在主体后自成一行。
    • 控制结构的开始左括号后和结束右括号前,都 一定不能有空格符。
    • 行的长度 一定不能 有硬性的约束。
    • 软性的长度约束 一定要限制在 120 个字符以内,若超过此长度,带代码规范检查的编辑器 一定要发出警告,不过 一定不可 发出错误提示。
    • 每行不应该多于 80 个字符,大于  80 字符的行 应该折成多行。
    • 非空行后 一定不能 有多余的空格符。
    • 空行可以使得阅读代码更加方便以及有助于代码的分块。
    • 每行一定不能 存在多于一条语句。

列举哪些做法有悖于“代码的简洁、清晰、无歧义”的基本原则,及如何进一步优化改进:

 在代码中使用大量的指针,我觉得这样会使代码更加复杂,难以理解。源代码中没有足够的注释,这让后面接手者还有改进者难以入手。没有统一的命名规范。对一些变量没有详细的说明。

改进之处:应尽量减少指针的使用,其次加入合适的注释,便于理解和修改。

总结同类编程语言或项目在代码规范和风格的一般要求:

1、一个实体应该只有一个紧凑的职责
一次只解决一个问题:只给一个实体(变量、类、函数、名字空间、模块和库)赋予一个定义良好的职责。随着实体变大,其职责范围自然也会扩大,但是职责不应该发散。事物往往是由小变大的发展。但是,到了一定的程度就需要有一些措施,比如公司大了,就需要改变管理的架构;程序库大了就需要重构。这样的过程一定是痛苦的,但如果为了避免短痛而无限制的拖下去,这个摊子就烂掉了。

2、正确、简单和清晰第一
软件简单为美:正确优于速度、简单优于复杂、清晰优于机巧。避免使用程序设计语言中的冷僻特性。应该使用最简单的有效技术。对这一点我还算比较了解。我已经学会了很多的技巧,但通常在代码设计中,我尽量避免使用技巧,最简单平实的代码才是最好的代码。很多的技巧容易导致代码可读性变得很差。

3、编程中应该知道何时和如何考虑可伸缩性
小心数据的爆炸性增长。对我来说,我设计的原则通常原因拿空间来换时间,好在存储的增长很快,没有在这一方面给我带来麻烦,值得我警醒的是我对程序的可伸缩性考虑还不够。
   
4、不要进行不成熟的优化
优化的第一原则是不要优化,第二原则是还是不要优化。再三测试,而后优化。对于大的系统来说,优化并不是一件简单的事情,需要考虑的因素很多,CPU、内存、磁盘I\O、网络状况……,在这之间的复杂关系没有梳理清楚之前,进行优化,是一件很不安全的事情。事实上,优化过后有大量的程序需要测试需要做。未必优化完成后,这些后续工作可以跟的上,所以在进行优化之前还是需要多多慎重。
       
5、不要进行不成熟的劣化
放松自己,轻松编程。在可以使用标准库的情况下,优先使用标准库。我想自己实现的算法,并不能比世界上最优秀的专家强,并且写出清晰的,可读性很强的代码,对程序是非常有帮助的。

6、尽量减少全局和共享数据
共享数据会导致冲突;避免共享数据,尤其是全局数据,共享数据会增加耦合度,从而降低可维护性,通常还会导致降低性能。

7、隐藏信息
不要泄密:不要公开提供抽象的实体的内部信息。尽量以接口的方式提供数据,这样的好处是如果对数据操作的要求有所改变的时候,只需要很少的修改。我基本可以做到将数据隐藏,但对于线程安全的容器,现在的相关工作还比较少,前几天出现的问题,给了我一个教训——即使是线程安全的容器,也难保哪天需要更换容器类型。
 
8、懂得何时和如何进行并发性编程
C++目前的标准并没有加上线程的概念,也就是说,所有的线程相关的工作都是平台相关的,我们希望下一代C++标准中出现线程的身影,虽然说现在ACE之类的扩展开源库也够用了。如果需要线程安全,需要了解平台的特性,了解线程的操作和同步化原语。使用C++对平台相关性进行抽象,这部分的工作量相当大,但很多开源库已经做了,推荐使用ACE。保证非共享对象的独立,在多线程程序中,我觉得引用计数是一项双刃剑,需要酌情处理,例如VC6的std::string。加锁的时候,需要注意锁的释放,特别是中途返回和异常退出。加锁的时候需要考量加锁的策略。内部加锁将工作放在了调用者一端,调用者需要对共享对象比较了解,而适当的加以串行化。外部加锁,将锁封装在内部,对外提供线程安全的接口,例如线程安全的容器。在内部加锁的过程中,需要考虑加锁的范围、粒度和线程安全性。对操作的相关性要特别注意。对于大多数数据,应该是不加锁的,特别是一些从来不会修改的数据和从来不会跨线程操作的数据。在进行加锁的同时,不可忽略的是加锁所带来的消耗。如果一旦线程安全的代价过大,就需要考虑修改设计了。

9、尽可能局部地声明变量
避免作用域膨胀,对于变量来说,应该是生命域越短越好。这样做明显的好处是可以避免名字空间的污染。另外说一点额外的话,函数中空间是在编译器就已经决定的。C++只有在函数的压栈和清栈过程中才涉及到栈的空间。这是C++非常强大的静态数据模型,他能保持非常稳定的运行。那么在同一个函数内,也存在不同的生命域,但这些变量的分配在编译时就已经决定,即使存在重名,即使生命域并不是整个函数范围,它都能独享一块空间,直到函数结束。

10、避免函数过长,避免嵌套过深

11、:避免跨编译单元的初始化依赖
保持初始化的顺序:不同编译单元中的名字空间级对象决不应该在初始化上互相依赖,因为谁都不知道初始化是什么样子的。全局变量总是越少越好,而且全局变量之间的初始化不应该相互依赖。

12、尽量减少定义性依赖。避免循环依赖


posted @ 2019-10-10 13:03  耶稣都留不住  阅读(177)  评论(0编辑  收藏  举报