项目稍微大一点就会变乱,有什么解决方法么

2013-06-07 20:51:59 +08:00
 wog
昨天看了下自己这一年写的代码,觉得实在惨不忍睹,
python的还好些,c和c++的基本上感觉拿出来都会丢人的。
这个问题其实我1年前就感觉到了,当时找一位前辈去问怎么解决,他推荐我学两个东西,一个是git,另一个是设计模式,然后让我多写代码。
这一年来我也确实一直在学,各种代码写了3~4万行的样子,可是到现在效果不是很明显(就是从以前的一千多行开始变乱提高到两千多行开始变乱)
我想问下大家遇到过这种情况么,应该怎么解决呢?
==================
另外悄悄地问下,我在写简历的时候如果附上我的github地址,然后面试官看到我之前的项目很乱,会不会减分呢?
4662 次点击
所在节点    问与答
26 条回复
saharabear
2013-06-07 21:00:42 +08:00
乱就乱吧,真实。
wog
2013-06-07 22:36:24 +08:00
@saharabear
那就太好了,我还在考虑是不是应该清空github的记录呢
alexrezit
2013-06-07 22:41:16 +08:00
@wog
想清就清吧. 反正我自己的 Github 上比较老一点的 repo 都删了, 留着丢人且无用, 更糟糕的是还容易误导别人学习一些不好的编码习惯. 不知道你是说一个文件两千多行么?
wog
2013-06-07 23:22:35 +08:00
@alexrezit 不是,整个一个项目超过两千多行就感觉控制不了了,是特别是c++的,继承什么的一多就乱了,反倒是c的还好点,不会太乱
fangzhzh
2013-06-07 23:47:22 +08:00
我是OO software engineer, 所以例子难免使用很多oo的概念.

设计模式,面向对象, 都是一种手段,不是目的.

# 几个大原则:
* 先想框架, 再想细节.
* 代码要分层, 划分好边界.
* 把最容易变化的部分抽象并隔离出来.
* 一个比较虚的, 简单->复杂. 考虑的足够复杂, 实现就可能很简单.
* 解耦合. 分配好职责,一个类只做并做好一件事情, 通知机制尽量不要使用依赖过强的方式.

## 先框架, 再细节
上来就写代码不是一种好习惯.

写代码前, 技术选型以后, 先不要想代码上怎么实现. 要不要多线程, 是不是要使用stl, boost, 是不要要使用一些第三方库, 是不是要自己实现链表, 字典等等. 这个功能是不是适合一个排序.先不要从这些地方想.

先想一想, 要做什么,逻辑上可以分成几个部分, 每个部分的关系, 之间的联系. 然后再想每个部分是什么, 需要具体做哪些工作, 实现哪些功能. 最后再考虑这些功能需要使用什么技术.


## 分层
### 大方面:
一般是MVC, M数据层, V层肯定是要分开的. 控制层简单了和UI混在一起,有时候单拎.
### 小方面:
一般的小功能一个工作类就可以搞定了. 稍微大一些的,举个异步的列子. 实现上一般有一个 manager管理所有的请求, 或并行, 或队列串行, 把请求分发给正确的工作类. 工作类结束了, 把结果交给manager , manager负责把结构返回给请求者.

层次上, 工作类和管理类分开, 客户只看到了管理类的统一接口, 接口类不变, 工作类可以有无限增加, 并且可以透明替换. 那么client不再依赖工作类, 边界划分的很清楚.

一个项目改动最多的就是工作类--即业务逻辑处理类. 分好层+边界, 多次的修改也不会对框架, 层次破坏, 也就不会乱了.

## 把最容易变化的部分抽象并隔离出来
四人帮的设计模式你如果看过的话, 应该看到过这句话很多次. 把最容易变化的部分抽象出來, 就可以打到添加,修改需求时, 改动不影响以前代码最小.

抽象不是为了重用, 而是为了被重用. 接口是抽象的一种实现方式. 如果设计的足够精良, 那么你的设计就会对修改封闭, 对添加开放.

举个烂大街例子: 你设计个一个类叫animal, 你需要实现叫声这个方法, 你可以使用
if( 汪星人)
汪汪
else if( 喵星人 )
喵喵
….

于是每增加一个动物,你的代码就要加上一行 else if, 修改原来代码.

如果把此接口抽象出来, 每个动物尽管实现自己的doBark好了, 那么 代码简单到
ani.doBark();

具体doBark, 由自己确定, 以后增加了类, 只需要这个类实现了doBark接口, 就可以不修改以前代码的基础上, 直接被以前的代码使用.

这个例子太简单, 太肤浅, 其实根本说明不了什么. 但是这种想法是要牢记的, 把*最容易变化*的部分抽象出来.

## 一个比较虚的, 简单->复杂. 考虑的足够复杂, 实现就可能很简单.
这个本来就很虚, 说起来就更虚的. 最好不说了.

## 解耦合. 通知机制尽量不要使用依赖过强的方式.

我说一个实际的问题吧. c++的数据库类D. 负责接受请求, 查询数据库, 将数据发送回去. 最显然的解决方案是: 客户类A像数据库类D发送请求, D分析请求, 是全部查询, 还是部分查询, 是不是可以查询, 还要考虑多个A之间的互斥.
如果可以查询, 那么D查询数据库, 打包取回来的数据, 返回给A. 如果不能查询, 那么本次的请求就丢掉了.

一种解耦合的方式, 队列+变量.
A的请求, 请求时更新 最近一次的请求时间, 把具体请求入队列, 然后A就可以返回.
D 根据自己的状态, 看是否有足够资源执行此次请求, 不行,就队列中等待. 直到有足够资源执行此次请求.
打包结果, 放入结果队列. A可抵达, 使用回调函数, 通知A 返回的结果. 如果A此时不可用, 同样后台等待下次发送结果.
这样保证了,请求不会丢, 结果不会丢, A不用无限等待D的执行过程. 解耦了A的请求与数据库查询操作.

# 上边是战略,下边讲一下战术
* 改代码尽量不影响以前的逻辑
* 复杂的程序逻辑,先用一些条件去掉不可能的情况
* 编写完代码的选择

## 改代码尽量不影响以前的逻辑
这个很容易理解,不容易做到. 这个在修改已有的代码的是很重要的.
千万不要*随便修改*已有的经过检验的业务逻辑, 哪怕你一百万的确定,你可以改对. 不要那么做.
需要需要修改以前代码的情况,大多数是新增了需求, 那么找到合适的位置, 把以前没有过滤的情况,过滤掉. 不要直接修改以前的代码.

## 复杂的程序逻辑,先用一些条件去掉不可能的情况, 每一个顺序下去的条件,都是互斥的
我以前写代码习惯这样
if(可行)
{
if( 条件1)
bla;
else if(条件2.1 && 条件2.2 )
{
if(条件4)
}
}
这种代码写下去,很恐怖.
后来学会了,
if(不可行)
return;
if(条件1)
bla;
if(!条件1 && (条件2.1 && 条件2 )) // !条件1, 这个保证了和上边的互斥, 在特别复杂的逻辑时,要容易理解的多
bla;

## 编写完代码的选择
一般人编完代码,就Run and Pray. 但是这是事倍功半的.

一种实践证明的方法是自己code review. 自己的代码刚写完是很难发现错误的, 所以我有一些 checklist
* 数据切片
关键数据, 用搜索功能, 遍历他出现的地方, 是不是出现了不该出现的地方, 是不是在该出现的地方没有出现,这种办法可以杜绝很多的低级的 typo错误. 而这种错误是最难找的.
* 控制切片
遍历所有的if else, 是不是覆盖了你想要的所有情况, 多否定自己的想法, 如果还有其他情况呢, 如果不是专业昂呢. 脑海中做几个边界测试, for, while的上下边界, if的典型数据.


太多了, 码字辛苦.

虽然码字有这么多,我认为对楼主的帮助仍然不会很大. 可能会让楼主觉得, 哇哦,好牛逼啊. 但是实际操作上, 楼主仍然觉得是没有操作性.

所以,我真正的建议, 找一个好的leader,或者伙伴,表现你的诚意,和可以栽培的潜力, 一个好leader ,可以让你的成长速度加倍, 有些东西是实践中来的,必须是你做了烂的设计, 别人做出不一样的设计; 或者你写了烂代码, 别人给出不一样的代码, 让你对比,你才会有切身的体会, 深刻的体会.

纸上得来终觉浅,绝知此事要躬行.
bengol
2013-06-07 23:51:05 +08:00
@fangzhzh 不明觉厉
fangzhzh
2013-06-07 23:56:26 +08:00
alexrezit
2013-06-08 06:40:27 +08:00
@wog
你试试 Objective-C, 一个文件五百行的节奏.
soli
2013-06-08 09:54:22 +08:00
继承是罪魁祸首
尽量不用继承;不得不用的时候不要超过2层;不得不超过2层的时候坚决不超过3层。
xupefei
2013-06-08 10:05:20 +08:00
研读一下设计模式,之后就不乱了。
dreampuf
2013-06-08 10:53:33 +08:00
可操作层面:
原则(KISSY,SOLID)为主,模式为辅
制定约定(代码规范,项目规范,团队规范)

方法论:
一致优先,简洁其次,优美最尾
wog
2013-06-08 11:10:15 +08:00
@fangzhzh 非常感谢,受教了,看来我确实应该出去找份实习,找个好的leader ,非常感谢前辈
wog
2013-06-08 11:11:41 +08:00
@soli
@dreampuf
非常感谢,受教了
wog
2013-06-08 11:15:36 +08:00
@xupefei 可能是我没研究透,大部分都会用,但有的模式用出来后我的代码就变成灾难级的了
zhangdawei
2013-06-08 11:16:55 +08:00
解耦,功能性函数不超过200行,最多最多,
wog
2013-06-08 11:19:20 +08:00
@alexrezit 没苹果,暂时试不了了,一个文件500行,虽然不知道是怎么写的,但总觉得好厉害的样子……
wog
2013-06-08 11:25:45 +08:00
@zhangdawei 那功能性函数多了还很乱怎么办,还要想着怎么样复用,还要接口怎么设计,感觉管理起来有点力不从心
zhujinliang
2013-06-08 11:56:37 +08:00
慢慢来,写得多了就能驾驭了。项目不急的话把编程当作艺术。
Golevka
2013-06-08 12:24:33 +08:00
一直以来都感觉ED们不明觉厉. 为了不修改已有代码搞出一套dispatch真的很重要么? 至少我用Standard ML从来都是直接case animal of [blah blah blah]的.
railgun
2013-06-08 13:19:44 +08:00
把你的git地址发上来,我们看看有多乱233

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/71636

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX