NIM_Duilib_Framework
NIM_Duilib_Framework copied to clipboard
关于代码质量和设计规范的建议
我是一名有10年全平台项目研发经验的的资深码农,开发过嵌入式linux系统,普通的MCU,传感器算法,PC、linux、macox 、ios、android 等跨平台项目,C++ 只是我项目开发中使用的一种开发语言,虽然现在是嵌入式硬件平台技术经理,已从事技术管理工作,但码代码仍然是日常,看优秀的开源的项目仍然是我的日常。
看过你们项目源码,我就想问一下,你们还能不能把代码写得更乱一些,要规范没规范,严重到连缩进都不统一,注释随便写,要设计没设计,看着你们把线程管理,消息代理写的那么乱,真的是心中一万匹马在奔腾,其他的倒是没那么严重,良好的设计规范是最基本的要求。你们还是一家网易大公司,更加应该注意代码质量,相比腾讯 开源出来的项目 写的跟python 语法一样,缩进注释都有严格的规范,如果连这个都做不好,就更别提设计优秀的架构.
良药苦口利于病
谢谢您宝贵的意见!
Talking is cheap, show me the code
花了半个月的时间,完完整整,一行不漏的读完了你们写的所有源码,不包括cef 原始代码,第三方库,比如jsoncpp,libxl, curl等代码,整个项目,主要分为
1 base 基础项目,:主要包含了 开发中常用的 des加密,文件操作,消息代理框架,自制的快内存(我不知道造这个有啥意义,标准库即可),网络(只是一些基本的转换函数),windows 三个常用信号同步封装,原子操作,base64 (自己写有啥意义,用标准不好吗),UTF 编码相互转换,线程框架(这个功能可以,就是代码乱),定时器(有高精度功能),其他util,windows一些常用的封装
2 duilib 改版,duilib 这个地方我是真的像吐槽,原版的duilib 已经写得很清晰了,可以说非常清晰,被你们改得面目全非,缺点比如,属性关键字改成了你们莫名其妙的Box,用VerLayout,HorLayout,TileLayout这些不直观还是咱的,非得搞成不常用的名称,整个核心部分也被改得面目全非,错乱的继承,包含方式,我硬着头皮看完了duilib 部分的代码,想着,这是网易公司的人写出来来的代码,不可思议的乱,哈哈。缺点太多,就不吐槽了,也得说一下你们改过之后的优点,有动画属性,并且动画属性是从control 就有,也就是所有的控件,布局box 都可以设置动画,在AnimationPlayer 中实现,Gif 在Image类中实现,有阴影。增加了多语言,增加了DIP 自动缩放,支持了触控,这些值得肯定,但是还是乱,真乱,我不知道你们团队多少人,但是这项目质量是真心地额不行,假以时日,我完全可以凭借我一己之力,完全重写整个项目,大概3个月足够,保证非常规范,连一样注释,一个换行,一个变量的名称,模块划分,继承关系,包含模块,组件封装,都是规规整整,
3:组件 组件部分其实在我看来,并不是UI库必须的部分,这部分主要是对 UI 的补充,封装了一些常用的操作,比如菜单,莫泰对话框,toast (有点android 的效果),msgbox 封装,cef 控件的封装,以及新增了一些布局方式,其实我觉得可以集成到 ui 中,不用单独作为组件,因为这些在开发项目中也是常用的.
吐槽完了之后,再补充一些建议 你们也有看过很多大型开源项目对吧,Chrome 、android,看看人家是怎么写代码的,别人的代码看得是不是爽心悦目?再看看你们的,如果我下面的人,跟我写这样的代码,还是另谋高就去,新人入职的第一件事,就是代码风格,团队统一,让我发现代码乱写的,代码规范我会让他倒背如流。这个项目代码CTRL + K ->F 我已经大部分格式化了。发现缩进问题是代码的最大问题,乱七八糟的
命名规范问题 你们知道什么叫做命名规范吧,如果不知道,Windows 的大驼峰命令规范就挺好 ,禁止缩写,CreateWindow 和CreateWnd 哪个好?难道担心名称太长影响编译效率?你们的命名大都只有一个单词,风格不统一,有些全小写,有些全大写。有些小驼峰,有些大驼峰,有些干脆就乱来,
文件名规范,类名规范 想不到吧,你们连问文件名类名规范都不是统一的
项目结构 连放到磁盘里面的分类都不是规范的。
总结来看 你们改的DUILIB 比原版的DUILIB 确实功能多很多,也可以做出大项目,但是这个库本身就像一个火锅,里面什么都有,但是用筷子去夹的时候,不知道夹的什么,如果要用好这个库,最好的办法就是熟读源码,但是我相信没有人会向我一样去做,通读源码,不然上面的issue 怎么问的都是一些小白问题:Talking is cheap, show me the code 这句话来自Linux 祖师爷说的,读源码才能领会别人的思维。也没有人会像我一样去读Linux 内核源码,几百万行的lInux 内核源码,源码配合资料去读,全中国也找不出几个了吧。
Bug 反馈一枚:xml layout 中你们会用Control 做占位符。但是xml 写 <Control/> 这种类型是会导致加载失败的,需要写成<Control />(自闭合前加空格),Bug 我已经找到并修复了,你们自己去找。
关于我的一些计划 这个库,应该会作为主要素材,结合其他的项目,重新规划,整理出一个无论是功能还是维护度都非常高的新版UI库,作为公司内部项目使用,比如酷狗PC版本的DUI (看过酷狗的皮肤xml),其实也是原版的Duilib 改良过后,其实非常不错,但是他们没有开放。但是我都有信心可以重新规范一套UI库出来(包括Layout工具),我相信你们可以做出好的项目,不要骄傲,认真做项目,好好写项目,
学学北欧那些小国,安安静静打磨技术,少一些浮躁,无论是硬件还是软件都非常重要
XML 中 <Control/>
这类控件占位符:自闭合控件无属性的Bug 修复方案:
方案A 推介,屏蔽属性列表读取时 跳过首个字符.(可能自闭合控件 是空属性的 比如Control 占位符) `bool CMarkup::_ParseAttributes(LPTSTR& pstrText) { //none attribute if (*pstrText == _T('>')) return true;
//*pstrText++ = _T('\0'); //屏蔽掉代码中去除首个字符的操作. 由_SkipWhitespace 去跳过空白字符
_SkipWhitespace(pstrText);
while (*pstrText != _T('\0') && *pstrText != _T('>') && *pstrText != _T('/')) {
_SkipIdentifier(pstrText);
LPTSTR pstrIdentifierEnd = pstrText;
_SkipWhitespace(pstrText);
if (*pstrText != _T('=')) return _Failed(_T("Error while parsing attributes"), pstrText);
*pstrText++ = _T(' ');
*pstrIdentifierEnd = _T('\0');
_SkipWhitespace(pstrText);
if (*pstrText++ != _T('\"')) return _Failed(_T("Expected attribute value"), pstrText);
LPTSTR pstrDest = pstrText;
if (!_ParseData(pstrText, pstrDest, _T('\"'))) return false;
if (*pstrText == _T('\0')) return _Failed(_T("Error while parsing attribute string"), pstrText);
*pstrDest = _T('\0');
if (pstrText != pstrDest) *pstrText = _T(' ');
pstrText++;
_SkipWhitespace(pstrText);
}
return true;
}`
方案2:加入自闭合控件空属性的判断 bool CMarkup::_ParseAttributes(LPTSTR& pstrText) { //none attribute if (*pstrText == _T('>')) return true;
//self closing "/>" 控件自闭合并且无属性.在 *pstrText++ = _T('\0') 前面,避免 / 被删除
if (*pstrText == _T('/') && *(pstrText + 1) == _T('>')) return true;
*pstrText++ = _T('\0');
_SkipWhitespace(pstrText);
while (*pstrText != _T('\0') && *pstrText != _T('>') && *pstrText != _T('/')) {
_SkipIdentifier(pstrText);
LPTSTR pstrIdentifierEnd = pstrText;
_SkipWhitespace(pstrText);
if (*pstrText != _T('=')) return _Failed(_T("Error while parsing attributes"), pstrText);
*pstrText++ = _T(' ');
*pstrIdentifierEnd = _T('\0');
_SkipWhitespace(pstrText);
if (*pstrText++ != _T('\"')) return _Failed(_T("Expected attribute value"), pstrText);
LPTSTR pstrDest = pstrText;
if (!_ParseData(pstrText, pstrDest, _T('\"'))) return false;
if (*pstrText == _T('\0')) return _Failed(_T("Error while parsing attribute string"), pstrText);
*pstrDest = _T('\0');
if (pstrText != pstrDest) *pstrText = _T(' ');
pstrText++;
_SkipWhitespace(pstrText);
}
return true;
}
大佬你好,本人小白,也在读源码,有个读不懂的地方,希望能赐教。
GlobalManager::Startup函数中有个LoadGlobalResource();解析xml文档,然后调用了WindowBuilder类的两个Create函数(返回Box*)在这里创建control设置attribute等操作。 但是创建之后Create函数return的Box并没有被任何对象接收,这样不就白创建了么? 相关代码如下(GlobalManager.cpp): void GlobalManager::LoadGlobalResource() { ui::WindowBuilder dialog_builder; ui::Window paint_manager; //这里会创建一个 Box 控件,但是dialog_builder是局部变量,出去函数不就销毁了么? dialog_builder.Create(L"global.xml", CreateControlCallback(), &paint_manager); }
而Window.cpp的Create函数直接调用::CreateWindowEx创建了一个窗体,那么xml中定义的UI是从哪里加载出来的呢? 这个问题我找到答案了,WindowImplBase有个OnCreate函数,在这里调用builder.Create函数加载。 WindowBuilder builder; std::wstring strSkinFile = GetWindowResourcePath() + GetSkinFile();
auto callback = nbase::Bind(&WindowImplBase::CreateControl, this, std::placeholders::_1);
auto pRoot = (Box*)builder.Create(strSkinFile.c_str(), callback, this);