野生码农的架构实践
一、 自传
IT圈子重所谓“出身”。在B乎、各种码农圈子,经常看到晒学历、晒工作经历。不是BAT,就是麻省,北清。就如龙母见任何人都要报出一堆头衔,以证明自己血统的高贵。
而本人,就是一个纯野生“双非”码农:非计算机科班出身、非IT名企出身。大学学的机电自动化,毕业也是一直跟机器、图纸打交道。没有自带光环、没有拜师学艺、没有群星荟萃,全凭个人兴趣,业余爱好而已。
然而诸葛亮说过,不要妄自菲薄。兴趣是最好的老师。因为偶然机会,帮朋友设计一个进销存软件,一发不可收,经常熬夜研究,不但要理解需求,还要编写代码,从无到有,无师自通。
那时候,成功连接数据库、弹出来第一个窗体,都是极大的喜悦。虽然彼时水平极菜,自己却认为已经是“高手”了。现在想来虽然可笑,但也是这种良好的自我感觉,给了我持续的动力。
对我这种野生码农而言,没有资历可晒,资本就只有一样:码力,写代码的能力。能证明自己的,就是代码和架构的水平,比如这个。
二、 起步:看山是山,看水是水
- 我的起点
起点就是零点。大学里也“学过”计算机,C,FORTRAN。甚至图书馆里借过几本计算机书啃。然而中国的教材,你懂的,最大特长是把简单问题复杂化。当时不喜欢英语,吃了大亏。
后来逐渐明白,无论技术还是教材,还是国外原版的好。后来再没怎么研究过计算机,直到我搞那个进销存软件为止。所以开始,我就是一个软件盲。
感谢宽带,感谢百度,感谢微软。家里有了宽带,就可以百度:看博客园、看csdn、看MSDN。好在.NET门槛很低,帮助齐全,神器加持,如果是C++,陡峭的自学曲线,说不定就放弃了。
- 摸索之路
也许正因为是从一张白纸起步,虽然走了些弯路,但也没有窠臼之见。既不知道什么设计模式,也没有编程套路和规范。一切都是自己摸索,也形成了自己的“风格”。
最开始的进销存设计,我不懂代码,但是需求分析还是有的。只是彼时初生牛犊不怕虎,不知深浅。什么能实现,什么不能实现,实现的难度有多大,一概不知。
这个进销存系统虽是业余练笔之作,但最初拟定的需求,却是参照一款国内流行的管家婆软件。需求来自于管家婆的说明书。
没办法,发挥我国人民山寨的特长,管家婆有几个页面我也几个页面,管家婆有哪些功能我也来一套。
VS神器+Winform,从拖控件开始。有些控件达不到管家婆的效果,就百度。涉及到的财务知识不懂,百度。如此也练就了一身功夫:搜商。现在想找什么资料,关键字往往一击命中。
有人说C#、.NET坏就坏在拖控件上。把码农惯坏了,惯成不求甚解只会拖控件的傻瓜了。我只想说,只会拖控件的确说明你是傻瓜,但这不怪微软,只怪你自己没有追求。
这样经过若干昼夜的鏖战,总算出来几张基本的数据库表,几个还算齐整的页面。代码是吃百家饭,从百度上抠来的。比如数据库的连接啦,button的效果啦,等等。
现在看那些代码,既不是面向对象,也不是面向过程,而是纯粹为实现功能拼凑的代码。前后台代码混在一起,一个类千行代码,一个函数洋洋洒洒。
但有两个习惯让我能进入下一步,一个是追求完美主义,一个是喜欢追根究底。没有这两点,不可能进阶。
比如ADO,里面有一系列对象,什么游标、什么DataTable、什么数据库连接池,这里涉及到大量概念,我这人不习惯死记硬背,为从无到有理解ADO、数据库的一系列概念,熬夜思考若干天,终于有所领悟,理出头绪。而之后,数据结构、套接字、网络协议,这些概念自然而然就会进入你的视野,务求深入理解,磨刀不误砍柴工。我个人的经验,软件行业包罗万象,最好的学习方法就是掌握基本概念之后,用什么学什么。道理一通百通。
而追求代码完美主义,使我看着过往零碎、丑陋的代码,内心感到不满,也开始接触到一些面向对象、多层架构、UML这些概念,感觉真理就在眼前,开始考虑重构代码。
三、 思变:看山不是山,看水不是水
- 从面向“过程”到面向对象
重构的第一步,提取出共性的东西,代之以函数。
原来大量的相同、相似代码合并,放到一个大的工具类,大家共享。这样做的好处显而易见,代码精简了,可读性强了,改起来容易了。
但是重构这个事,很容易矫枉过正。尤其是我接触到“面向对象”这个概念之后。
初识面向对象,总有一种发现真理的感觉。比如订单类,原来的风格是类大而全,保罗万象。
学了面向对象,就将采购单、销售单、入库单、出库单等等继承一个总的Order类,每个Order还要分订单抬头(客户、账号、订单号、日期),明细,又各自有所继承。
那时候的代码,真是万物皆对象,为赋新词强说愁。至于这么分类有什么好处,却没有清醒的认知。所以结果就是把简单的问题复杂化了。
比如每个订单都有自己的增删改查方法,然而订单的参数不同,增删改查都要重载;人说三层架构好,就把界面逻辑中所有数据操作的部分,都塞到一个DAL层。
可以说,这样做的结果还是有好处的,界面部分没有数据库操作的语句。修改界面的人(不一定是自己)只需要知道接口规范就可以。不用在意太多细节。
坏处是,一旦对象发生变化,不但要改接口,还要改数据库,数据库操作的DAL层。
这一阶段,总体代码量甚至有所增加,性能甚至有所下降,但思路更加清晰了,对面向对象的理解深入了,包括它的利弊。
- 设计模式的改造
学习了设计模式之后,对面向对象的理解深入了一层。实际上,经常发现自己不知不觉中已经是设计模式的践行者。因为这些模式都是前人对某一类问题总结出来的最佳设计方案。
设计模式的目标,就是减少耦合性,提升扩展性。从哲学角度,编程并不简单的是写代码的问题。是对客观世界的抽象。牛逼的人能很快领悟到本质,用一个简洁的描述就能让人明白真相,比如
大部分人,对复杂的问题理不出头绪,而且越来越乱。所以编程能力除了逻辑思维之外,很重要的一点是抽象能力。不管是编程还是做其他事,都需要这种能力。
在这之后,我并没有刻意去套设计模式,而是学会了一个简单的套路:分析核心业务对象,用接口提取某一类业务的共性,各业务对象之间用接口交互。
四、 破局:看山还是山,看水还是水
- 简单的就是好的
等面向过程、面向对象、设计模式一路走来,经历了从繁到简、由简入繁的过程,对架构的利弊也有了更多的思考。
这时候,我最强的就是一个字,简。一切回到原点,风格倾向简洁。到后来,甚至变成极简主义,不想看到一行无用的代码。
可能很多码农都有体会,看某些网上的代码,痛苦不堪。因为千头万绪,本来几句话能说清楚的事情,绕很大一个弯。
当年我做组态设计,有用到一种DDE的技术。网上下载了一个库NETDDE,C#代码,为了追求所谓“普适性”做了几层封装,最后不但代码不忍卒读,性能还很差。
于是花了点功夫做重构,删繁就简,代码清晰,性能提高了一个数量级。
越简单的东西,可读性越好,可维护性越好,可扩展性越好,往往性能也越好。
而能用最简单的方式描述一个复杂的事物,我认为就是架构能力的体现。无论在我的组态软件设计,还是程序化交易软件,都遵循了最简化这一原则。
说说个人的一些体会。
首先,对需求分析,提取出核心业务。
比如在进销存,核心业务就是物流、钱流;而驱动物流、钱流周而复始的核心对象就是订单(采购单、销售单、入库单、出库单等等)。
在组态软件,核心业务就是网关,核心对象就是变量(Tag)。无论HMI的动态显示,还是下位机的扫描轮询,承上启下的就是变量。
在程序化交易,核心业务就是行情、交易,核心对象就是Bar(K线)。驱动核心业务运转的就是订单(Order)。
分析出核心业务、核心对象之后,整体思路就清晰了。
第二步,根据业务特点做技术选型。
我认为,作为一个好码农,能够在多种方案中选择一种最佳或者次佳的方案,是基本能力。
在多年编程实践之后,应该对同一个问题已经有了多种方案。比如最基本的,用.NET还是JAVA还是Python?通讯是用Socket还是HTTP?是用字典还是列表?是用第三方还是自己造轮子?
每种方案各有利弊,最后你要善于抓住主要矛盾,比如你需要的是开发效率,就尽量利用.NET;做互联网项目,尤其是复杂业务、在Linux上运行,就要用JAVA;需要的是性能,就要Socket,字典;可能存在并发,那就要考虑高并发解决方案。
第三步,架构设计。
对我最熟悉的桌面程序而言,一般都包括三部分:界面-服务-工具。
在组态项目里,后台服务提供网关功能,核心业务封装在服务;可以驱动多个客户端界面。因此服务要求稳健、高性能。服务这部分需求尽可能完善,尽可能稳定。设计服务部分的一般为架构师、资深码农。
界面需要前后台分离协作,因此界面设计应尽可能简单、直观。最好就是傻瓜式的拖拽控件,复制模板,连连变量。界面设计人员不需要考虑后台怎么实现,只需考虑具体业务需求。甚至都不一定会编程。
工具如组态软件的变量管理器等,设计目的是减少界面人员和服务设计人员的交互工作。把两者共同用到的配置项、变化最多的数据分离出来,用简单的可视化工具管理。VS本身也是这样一种工具。
服务-界面-工具,三者之间用不同的方式交互。服务与界面可以使用通讯协议、WCF交互;界面、工具之间可通过数据库、配置文件交互;工具和服务之间往往通过数据库交互。
- 对性能的关注
数据结构是码农的基本功。很多人说.NETer因为框架的强大对数据结构一无所知。这是P话。因为我在代码追求完美主义,所以我对我程序的性能也追求的孜孜不倦。
很多人说.NET性能如何如何不行,博客园有老赵,xiaotie的文章为什么不去看?老赵和xiaotie的性能优化手段,我也用过。因为不同的数据结构、不同的索引都能带来巨大的性能差异。
比如在组态软件里,如果要按变量名在一个几千变量的变量表搜索到一个变量(Tag),你可以用1.遍历2.二分法排序之后查找3.字典。三种方法的差异有如天壤之别,如果这种操作是频繁的,就一定要仔细考量。
如果你仅仅写代码为交差,为糊口,就不会关注性能,更不可能去编译.NET的代码,或者去百度谷歌搜索最好的性能解决方案。
如果这是一种习惯,日积月累,几年后,同为.NETer ,做出来的东西无论架构还是性能,就有巨大的差别,当然也会反映在个人收入上。
园子里有人发帖说学.NET的如何被PHPer,JAVAer歧视。我只能说,你被歧视怨不得.NET,怨不得微软。只是你自己水平太菜而已。我也接触过不少搞JAVA,PHP的,谁有资格搞语言歧视?
没有一种语言能解决所有问题。各有所长。如果有搞JAVA,PHP或者任何其他语言的人,如果仅仅因为语言的偏见就想贬低我个人的话,可以到github下载我的程序,然后自己想出更好的解决方案来,我就服你。否则自己去打脸。
五、 下面的计划
写一系列帖子,把架构、原理讲清楚。大致如下:
- 网关层接口概述
- 上下位机通讯原理
- 如何实现一个设备驱动
- 如何设计图元
- VS插件模块及原理
- 归档模块及文件格式
- 如何进行功能扩展
- 组态变量表达式实现
github地址:https://github.com/GavinYellow/SharpSCADA。QQ群:102486275。