我本周在工作中一直在研究一些已有10年历史的C代码,在实施了一些修改之后,我找到了老板并问他是否需要做其他事情。那是他放下炸弹的时候。我的下一个任务是遍历7000行左右并了解更多代码,和以稍微模块化代码。我问他如何将源代码模块化,他说要开始将旧的C代码放入C ++类中。
作为一名优秀的工作者,我点头表示赞成,回到我的办公桌前,我现在坐在那里,想知道世界上如何采用这些代码,并“模块化”它。它已经有20个源文件,每个文件都有自己的用途和功能。此外,还有三个“主要”结构。这些结构中的每一个都有30多个字段,其中许多是其他较小的结构。尝试理解它是一个完全混乱,但程序中几乎每个函数都传递一个结构的指针并大量使用结构。
有没有什么干净的方式让我把它变成课堂?如果可以的话,我决心这样做,我只是不知道如何开始。
答案 0 :(得分:35)
答案 1 :(得分:20)
真的,7000行代码不是很多。对于如此少量的代码,可以按顺序完成重写。但这个代码将如何被调用?大概是调用者期望一个C API?或者这不是图书馆吗?
无论如何,重写与否,在开始之前,请确保您有一套测试,您可以在现有代码上轻松运行,无需人工干预。然后,在进行每项更改后,对新代码运行测试。
答案 2 :(得分:19)
这种对C ++的看法似乎是随意的,问你的老板他为什么需要这样做,弄清楚你是否可以不那么痛苦地达到同一目标,看看你是否可以用新的不那么痛苦的方式对一个子集进行原型设计,然后去演示给你的老板,并建议你采取不那么痛苦的方式。
答案 3 :(得分:12)
首先,告诉你的老板你没有继续,直到你有:
http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672
在较小程度上:
http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052
其次,没有办法将代码模块化到C ++类中。这是一项艰巨的任务,您需要向您的老板传达重构高度重构代码的复杂性。
归结为做一个小改动(提取方法,移动方法等等......)然后进行测试 - 这没有捷径。
我确实感觉到你的痛苦......
答案 4 :(得分:5)
我想这里的想法是增加模块化将隔离代码片段,以便促进未来的变化。我们有信心改变一件,因为我们知道它不会影响其他作品。
我看到两个噩梦场景:
然而,也许有一个幸福的媒介。可能存在一些重要且概念上孤立的逻辑,但由于缺乏数据隐藏等目前这些逻辑很脆弱(好的C不会受此影响,但我们没有这个,否则我们会留下好的单独的)。
拉出一个类来拥有该逻辑及其数据,包含该部分可能很有用。用C或C ++做它是否更好是值得商榷的。 (我的愤世嫉俗者说“我是C程序员,很棒的C ++学习新东西的机会!”)
所以:我认为这是一只可以吃的大象。首先要确定它是否应该被吃掉,糟糕的元素只是没有乐趣,结构良好的C应该被单独留下。第二次找到合适的第一口。我会回应尼尔的评论:如果你没有一个好的自动化测试套件,你就注定了。
答案 5 :(得分:5)
我认为更好的方法可以完全重写代码,但你应该问你的老板他想要你“开始将旧的C代码放入c ++类”的目的。 你应该询问更多细节
答案 6 :(得分:4)
当然可以做到 - 问题是成本是多少?这是一项艰巨的任务,即使对于7K LOC也是如此。你的老板必须明白这会花费很多时间,而你却无法处理闪亮的新功能等。如果他不完全理解这一点,并且/或者不愿意支持你,那么就没有必要开始。
正如@David已经建议的那样,重构书是必须的。
从您的描述中可以看出,代码的很大一部分已经是“类方法”,其中函数获取指向结构实例的指针并在该实例上运行。所以它可以很容易地转换成C ++代码。当然,这不会使代码更容易理解或更好地模块化,但如果这是你老板的主要愿望,那就可以做到。
另请注意,这部分重构是一个相当简单的机械过程,因此无需单元测试即可相当安全地完成(当然还有超级软件编辑)。但是对于更多的东西,你需要进行单元测试,以确保你的改变不会破坏任何东西。
答案 7 :(得分:4)
这项练习不太可能获得任何好处。好的C代码已经比C ++通常更加模块化了 - 使用指向结构的指针允许编译单元在C ++中与pImpl一样独立 - 在C中你不必公开数据在结构内部公开其接口。所以,如果你转动每个C函数
// Foo.h
typedef struct Foo_s Foo;
int foo_wizz (const Foo* foo, ... );
使用
进入C ++类// Foo.hxx
class Foo {
// struct Foo members copied from Foo.c
int wizz (... ) const;
};
与C代码相比,您将减少系统的模块性 - 如果将任何私有实现函数或成员变量添加到Foo类型,Foo的每个客户端现在都需要重建。
C ++中有很多类提供给你的东西,但模块化不是其中之一。
向你的老板询问这项工作实现的业务目标是什么。
关于术语的说明:
系统中的模块是具有良好定义的接口的组件,可以用具有相同接口的另一个模块替换,而不影响系统的其余部分。由这些模块组成的系统是模块化的。
对于这两种语言,模块的接口按照惯例是头文件。考虑string.h
和string
定义C和C ++中简单字符串处理模块的接口。如果string.h的实现中存在错误,则安装新的libc.so。这个新模块具有相同的接口,动态链接到它的任何东西都可以立即获得新实现的好处。相反,如果std::string
中的字符串处理存在错误,则需要重建使用它的每个项目。 C ++在系统中引入了大量的耦合,语言没有什么可以缓解的 - 实际上,充分利用其功能的C ++的更好用途通常比等效的C代码紧密耦合。
如果你尝试使C ++模块化,你通常会得到类似COM的东西,其中每个对象都必须同时具有接口(纯虚基类)和实现,并且你用间接替换有效的模板生成代码。
如果您不关心您的系统是否由可替换模块组成,那么您不需要执行操作来使其模块化,并且可以使用C ++的一些功能,例如类和模板,适用于,可以提高模块内的凝聚力。如果您的项目是生成单个静态链接的应用程序,那么您没有模块化系统,并且您可以完全不关心模块化。如果你想创建类似anti-grain geometry的东西,这是使用模板将不同的算法和数据结构耦合在一起的漂亮例子,那么你需要在C ++中做到这一点 - 很好,没有其他普遍的东西是强大的。
所以要非常小心你的经理所谓'模块化'。
如果每个文件已经具有“它自己的目的和功能”并且“程序中的每个函数都传递了指向其中一个结构的指针”那么将它更改为类的唯一区别就是将指针替换为具有隐式this
指针的结构。这对系统的模块化程度没有影响,事实上(如果结构只在C文件而不是标题中定义),它将减少模块化。
答案 8 :(得分:3)
使用“仅”7000行C代码,从头开始重写代码可能更容易,甚至不用了解当前代码。
并且没有自动化的方法可以做甚至协助您设想的模块化和重构。
7000 LOC可能会发声,但其中很多都是样板。
答案 9 :(得分:2)
尝试查看是否可以在将代码更改为c ++之前简化代码。基本上我认为他只是希望你将函数转换为类方法并将结构转换为类数据成员(如果它们不包含函数指针,如果它们确实将它们转换为实际方法)。你能和这个程序的原始编码器联系吗?它们可以帮助你完成一些理解,但主要是我会搜索那段代码,它是整个事物的“引擎”,并从那里开始新的软件。另外,我的老板告诉我,有时候简单地重写整个事情会更好,但现有的程序是模仿运行时行为的非常好的参考。当然,专门的算法很难重新编码。我可以向你保证的一件事是,如果这段代码不是最好的,那么你以后会遇到很多问题。我会去找你的老板并宣传你需要从头开始重做程序部分的事实。我刚去过那里,我很高兴我的主管给了我重写的能力。现在2.0版比原版早了几年。
答案 10 :(得分:2)
我从http://www.javaworld.com/javaworld/jw-03-2001/jw-0323-badcode.html?page=7读到了这篇题为“让坏代码变好”的文章。它针对Java用户,但我认为它的所有想法都非常适用于您的案例。虽然标题听起来像只是为了代码不好,但我认为这篇文章是针对维护工程师的。
总结法瑞尔博士的想法,他说:
我认为在遵循其他人的建议后,如果你有空闲时间,这可能是一篇很好的文章。
祝你好运!