设计模式IT教育数学程序员社区之星重构软件
摘要:数学修养对软件开发之路起着什么作用?码农如何修炼自己的内功并成长为优秀的软件开发员?带着相关思考,社区之星第10期采访了中南大学副教授——刘伟。他对数学修养、设计模式、软件架构和重构方面的独特见解,相信会给众多码农和技术大牛带来新的收获。
导语:他虽然是一名大学老师,但却有着十多年软件开发和项目管理经验,他曾任职于湖南移动、创智软件园、NIIT(印度国家信息技术学院)和长沙创智新程教育技术有限公司等机构,也曾为多所高校、软件培训机构担任讲师和多家公司提供企业内训服务。另外他还主持和参与了30多个软件项目的开发工作,涉及教育、电子商务(政务)、企业EAI、移动通信等领域,此外他还致力于科研研究。正是这种复杂的多重经历,让他对软件开发有着独到的见解和心得,为此CSDN记者对这位学者兼技术人进行了专访,希望大家看完后能够对软件开发之路有全新的认识,尤其是对软件开发人员至关重要的设计模式、数学修养、软件架构和重构方面。
刘伟,中南大学软件学院副教授,博士。中国计算机学会会员,美国计算机学会(ACM)会员,希赛教育集团企业内训讲师,新程教育集团顾问兼高级讲师,CSAI顾问团专业顾问及内训讲师。具有十多年软件开发、项目管理及IT教育培训经验,曾担任项目经理、系统架构师、软件工程师、IT培训讲师和高级讲师等职位。研究方向为软件工程与数据挖掘,目前正从事相关算法的研究与CASE工具的开发工作。著有:《设计模式》、《设计模式实训教程》、《设计模式的艺术——软件开发人员内功修炼之道》等书。同时也在编著新书《UML实用教程》和《C#设计模式》。
我与计算机的故事
CSDN:请和大家介绍下你及所从事的工作
刘伟:我现在主要从事软件工程和数据挖掘等领域的科研、教学和研发工作。具体工作包括:
· 软件工程专业课程的教学工作:主要讲授《软件工程》、《设计模式》、《软件体系结构》和《电子商务与数据挖掘》等课程。
· 软件工程和数据挖掘等领域的科研工作:注重理论研究与实际应用的结合,目前主要研究课题包括源代码自动重构、CASE工具研发、源代码中设计模式挖掘、软件度量等,目前承担的几项科研项目的研究工作,已得到包括华为在内的多家企业的关注并愿意参与其中。
· 企业培训与支持工作:在周末和寒暑假,我会给一些企业提供需求分析、UML、设计模式和重构等技术培训,密切与企业联系,为企业项目开发提供一些指导,同时也为高校教学工作添加更多真实的企业案例,已经给多家公司提供过企业内训,反响都还不错。
· 软件研发工作:时间允许的话我会承担一些软件项目的研发工作,目前正在从事一些CASE工具的研发工作。
CSDN:你并非计算机出身,是什么缘故促使你自学计算机课程?在学习上有什么心得可分享?
刘伟:严格来讲大学期间我并不是一名好学生,因为我没把时间和精力放在专业学习上,现在从事的工作也与大学所学专业没有关系,但我并不后悔,反而觉得大学四年让我更加清楚地知道自己想要什么,树立了更长远的目标,那几年也过得很充实,为之后的学习和工作奠定了很好的基础。
我大学本科学的是化学专业,不过从大一(1999年)开始就对计算机产生了极大兴趣,然后自学了所有计算机课程,大二开始在一些校办网站和软件公司兼职。在本科和研究生学习期间,我利用课余时间在多家软件公司兼职,也组织过团队在外面接一些项目。软件开发是极富挑战的创造性工作,我很享受那个集体力和脑力劳动于一体的过程,我想最根本的原因应该还是兴趣所在吧。
在大学期间,我对软件教育和技术推广也具有极大热情,2001年(19岁)开始在一些IT培训机构担任网页制作、SQL Server数据库等课程的兼职讲师,同时考取了高级程序员等证书,还获得了微软认证系统工程师和微软认证数据库管理员。
对于软件知识和技术的学习,我有以下几点建议,供参考:
· 最重要的是兴趣:兴趣是可以慢慢培养,作为一名软件专业老师,我也有意识发掘并培养学生的兴趣,让学生喜欢上这个专业,然后再努力去学习研究,进而全面学习和掌握相关专业知识;
· 其次是学习能力:IT知识更新速度非常快,一定要培养自学能力,我经常跟学生说:真正成功的大学教育并不在于教会学生多少东西,而是教会学习的能力,这种能力让大家终身受用。俗话说:授人鱼,不如授人以渔!只有不断学习,才能真正成为行业领袖和专家。在学习过程中,要善于利用互联网资源,不要把电脑和网络当做娱乐工具,而应该作为学习工具,经常访问一些IT网站,看一些行业新闻、博客和论坛等,多交流、多讨论和多虚心学习。
· 多思考,多实践:软件工程是工程应用类专业,一定要多实践,有条件可以参与一些老师牵头的横向项目(企业项目),或者在不影响学习的情况下接点企业小项目做做(例如小网站或者小型的MIS系统之类),这样能力提升会更快,而且能够学到很多在课堂上学不到的知识和经验。
2010年我在CSDN博客上发表了一篇文章,《写给明天的软件工程师——感悟篇》,大家如果感兴趣的话可以看看。
CSDN:你微博中用“软件兴邦,教育利民!致力于中国软件教育“作为座右铭,想传达什么?
刘伟:这包含了我的人生理想,我现在的目标是成为一名一流的IT教育工作者,这里的IT教育是广义的,除了高校教育、企业培训外,还包括职业教育等,我觉得一个行业要能得到更好的发展,应该有一套成熟、科学、合理的人才培养体系。现在软件行业的地位和重要性毋庸置疑,前段时间我在博客中转载了一篇文章《软件应用无所不在正吞噬整个世界》,现在软件已渗入到各行各业,而且很多传统行业已经离不开软件的支持,例如:零售、交通、出版、医疗、招聘、娱乐、电信、教育等等,还有很多行业本身就是依托于软件的,例如电子商务、在线游戏等。网景公司创始人、硅谷著名投资人马克·安德森的这篇文章中,他列举我们所面临的三大挑战第二条就是人才问题,软件行业缺乏大量能够胜任的软件工程师、经理、营销人员和销售人员,他指出只有教育才能解决这个问题。正因为如此,我觉得自己的选择是非常正确的,我也希望自己能够通过努力,成为一名优秀的软件教育工作者。“软件兴邦,教育利民!致力于中国软件教育”这句话也是用来勉励自己,希望自己能够不断研究和探索软件人才培养的规律和模式,以便更好地从事软件教育工作。
数学之美:程序员应有的数学修养
CSDN:一名优秀的程序员和他的数学修养有没有什么联系?它给程序员带来的最大帮助是什么?
刘伟:我个人觉得是有联系,而且关系很密切。数学作为自然科学之母,更多的是培养人的逻辑思维能力,是思维的体操。
我们要区别一下合格程序员和优秀程序员,合格程序员可以完成一些常规的代码编写工作,例如CRUD,这里确实用不到太多的数学知识,但是如果希望能写出高质量的代码(例如具有良好的时间复杂度和空间复杂度),或者参与一些技术难度较大的软件开发工作(例如涉及到构建数学模型的软件,如交通调度系统、与电子商务有关的数据挖掘系统、计算机模拟药物设计软件等),良好的逻辑思维能力是必须的,而数学正可以培养这种能力。
良好的数学修养将让程序员在设计和实现软件时思维更严谨、思路更开阔、考虑也会更缜密,有助于写出高质量的软件,还有利于参与一些技术复杂度较高的软件,这对于个人综合素质的提高和职业发展都很有帮助。我建议大家有时间看看数学书,做做数学题,你能够从中找到快乐!
CSDN:那程序员如何培养自己的数学修养?应该培养哪些数学修养?
刘伟:2012年有三本书卖得很火,它们是:腾讯副总裁吴军先生的《数学之美》、日本资深技术作家结城浩的《程序员的数学》和北大高材生顾森的《思考的乐趣:Matrix67数学笔记》。而购买者绝大部分都是IT相关人士,当然最多的还是程序员。由此可见,很多一线程序员都意识到有时间应该学学数学,看看数学书,培养自己的数学修养。这是好事,说明大家都在努力求进步,都在努力提升自己的综合水平。我也读完这三本书,收益颇丰。
2006年我在CSDN博客上发表了一篇题为《浅谈程序员的数学修养》文章,很多人发表了一些不同的看法和意见,当然我在这篇文章中所写的很多都是个人观点,难免会存在一些考虑欠周全的地方,但对于一名优秀程序员是否应该具备一定的数学修养这个观点我还是坚持自己意见,我认为是肯定的。那么如何培养自己的数学修养,我在《浅谈程序员的数学修养》一文中有提到,大家感兴趣的话可以看看这篇文章,另外我还转载了几篇与之相关的文章:《数学是成就卓越开发人员的必备技能》和《编程需要知道多少数学知识》,有一些不同的声音,大家都可以看看。
需要指出的是这里所说的数学修养并不是指数学知识,不是指你各种解题能力,这里所指的数学修养更多的是平时对数学知识的积累。比如适当做数学题来训练自己的思维、看有关数学书来丰富自己的知识体系,当你需要在工作中具体用到一些数学知识时,你可以很快地学习。数学修养的培养更多的是培养自己的逻辑思维能力,这对于一个优秀程序员是非常重要的能力,数学修养不等于数学知识,并不是看几本高深的数学书就能有好的数学修养,真正的数学修养的培养并不需要我们一定要掌握多少高深的数学知识,而是在平常的学习和工作中能够将数学知识和正在解决的问题联系起来。例如当你在设计一个电子商务系统时,除了传统的CRUD,你还应该考虑一下顾客行为分析、商品推荐、网站链接结构优化、广告投入策略、信息检索模型构建等,这里面也许就需要用到贝叶斯网络、粒子群优化、图论、聚类和分类等与数学相关的知识,如果你具备很好的数学修养,平时也注意数学知识的积累,那么即使从头开始学这里的某些知识也不是难事。由此可见,对于一名优秀程序员而言,数学修养是必备的。
具体来说,数学修养的培养可以从算法分析和设计开始,深入理解一些经典算法的设计原理和实现机制,我认为算法是数学的一部分,是数学在计算机领域的应用,就像数学应用在材料、力学、经济学一样。另外,有空的话可以多做数学题和逻辑题,把这些习题当做一种逻辑思维练习,让自己思维更加活跃一点,Bob大叔(Robert C. Martin)称这种练习为卡塔(Kata),我建议大家有时间每天也做一两个卡塔,有些数学题可以不在纸上写,直接用程序实现也可以。另外,前面提到的那三本书也挺不错,建议大家有时间可以看看,权当一个入门,总之一定要认识到数学修养的重要性。对于那些正在或有志于数据挖掘、信号处理、图像处理和搜索引擎等工作的朋友,那数学的重要性就不多言了,大家都懂的。
IT人才培养
CSDN:在大学计算机专业课中哪些课程最难?你又是通过什么方式简单地讲解难点?
刘伟:大学计算机专业课中比较难的课包括离散数学、数据结构、编译原理、算法分析与设计等(来自同学反映),当然有的学校还开设了信息论与编码、数字图像处理、计算机图形学、计算机密码学、数据挖掘等,这些课跟数学以及逻辑思维能力或多或少有点关系,当然也因人而异。上述这些课程对于有些同学而言也并不难,但是跟软件工程、操作系统、计算机网络、计算机组成原理等课程相比,很多同学在学习这些课程时相对更加吃力。
这些课有些我也没有教过,在以前讲授数据结构、算法分析与设计等课时,如果遇到比较难的地方,我会首先利用一些图形和动画来分析其原理,先形成一个感性的认识,然后引入一些比较容易理解和通俗的实例来讲解,通过对具体问题的分析来逐步深入。特别是算法,不能为讲算法而讲算法,最好能够结合一些具体问题来分析,对于算法的本质需要讲透彻一点,但通常数学基础较好的学生可以快速消化这些知识,而数学基础相对较差的在学习过程中遇到的困难更大。因此,我有时候也会要求学生在课后做逻辑思维题,例如数字找规律、图形找规律、逻辑推理、简单的数学证明题等来训练一下思维能力,这其实也是在无形中培养自己的数学修养,通过一段时间的训练,有些同学确实有所进步,在思考问题时思路更开阔、更严谨,对算法的理解也更深入。
CSDN:你曾做过几年开发,后来为什么从技术人转为IT教育工作者?之前的工作对你现在有何帮助?在给高校学生上课之外,你还喜欢与企业交流,为什么?
刘伟:我个人对教育事业可以说是情有独钟,我出生于一个教师家庭,祖父、父亲、母亲都是教师,祖父和父亲是一所中学教师(祖父是老校长,父亲是现任校长),母亲是小学教师。从小就在学校长大,一直很喜欢把自己学到的东西传授给别人,这很快乐。在大三(19岁)时我就在IT培训机构担任网页制作、SQL Server数据库等课程的讲师,那时学生基本上比我大,但学习完之后,大家反应都还挺好,说我讲课思路清晰、内容很丰富、信息量大,能够学到很多东西,我想这方面还有点遗传基因吧。研究生阶段也在职业培训机构担任兼职讲师,硕士毕业(2006年)后在湖南移动、创智软件园上班,主要从事软件研发工作,还在NIIT(印度国家信息技术学院)、创智新程教育技术有限公司(原创智集团教育事业部)担任高级讲师,同时也担任几所高校的企业兼职教师,之后读博并在中南大学软件学院任教。从2001年开始都从事与IT教育相关的工作,之前是兼职,现在是全职。对于软件研发、软件职业教育、软件高校教育我都有涉足,正是这种特殊的工作经历,让我对IT人才的培养有更深刻的理解和认识。
在有过几年一线开发经验后,我发现自己最大的长处还是在于将一些知识用较为通俗的语言和实例传递给他人,这也是一名好教师,特别是IT相关教师应该具备的能力,因此,我陆续拒绝了很多国内外知名企业的Offer和多家猎头公司的邀约,在经过慎重思考后我已逐步将工作重心转移到软件教育上。另外我觉得中国的软件教育需要一批有过企业背景,也具有较好表达能力和感染力的老师,我自认为还是具备这些条件的。看到我的学生们能够进入一些一流的软件公司、做出一些优秀的软件,所带给我的成就和愉悦感,远比自己收获这些要大。
企业工作背景对我的教学工作非常有帮助,在教学过程中,我会结合一些企业真实项目实例来进行知识讲解,会结合我之前的一些工作经历讲授一些书本之外的知识,例如在讲解软件需求工程时,我会告诉学生我曾参与开发的一些项目是如何来获取需求的、对于不同的需求源我们做了哪些分析和考虑、采用了哪些需求获取手段、如何记录需求以及如何确认需求,跟学生讲我们当时遇到了哪些问题以及最后怎么解决的,还会穿插一些真实的小故事,让大家的学习过程不那么枯燥无味,而是充满好奇感和趣味性。正因为之前参与过那么多实际的项目,亲身经历过很多软件的成功与失败,也充分体会到过程的快乐与艰辛,才让我现在能够自信满满地站在讲台上,与学生们分享我的知识与经验。
为了不脱离企业,我现在仍然会经常与企业进行交流,我一直认为,一旦脱离企业实际需求,IT教育不可能培养出真正优秀实用的人才。一方面,我会在工作之余参与一些企业项目的开发工作,主要以顾问的形式参与需求分析、系统架构设计、技术评审等,尽自己的能力给一些企业提供指导,例如教企业如何实施Scrum、如何使用UML构建软件模型、如何对源代码进行优化和重构等。在提供指导的同时,我又可以将这些知识和经验传递给在校学生,让学生知道现在企业正在做什么、他们是怎么做的、遇到了什么困难、应该具备哪些知识才能胜任这些工作,我觉得这是一个良性循环,学生们学习了这些知识和技能之后在实际开发中就能够用上,而且学习过程本身也很好地切合了企业的需求,在一定程度上解决高校人才培养和企业实际需求脱节的问题。当然,我现在所做的还是一个探索性的工作,也希望这项工作对于我国高校软件人才的培养有一定的参考价值。另一方面,我也在分析企业的一些需求,例如,有些企业员工因为工作压力大、项目进度紧等原因,无法静下心来学习一些新知识,于是我会将这些知识整理好,用较短的时间和较为通俗生动的方式传授给他们,我觉得这也是IT教育很重要的一个组成部分,教育是贯穿一生的,活到老,学到老。
与企业交流让我了解到企业对人才的需求,能够为我的教学工作提供更多真实案例,同时我也能够为企业开发人员提供一些指导和培训,此外,我个人的知识和经验也在不断累积,有助于我更好地从事软件教育工作。
CSDN:现行的软件人才培养体制有哪些缺陷?你认为该如何改进?
刘伟:关于现行软件人才培养体制,我个人也有一些自己的意见和建议,下面我谈几点自己的看法吧。
· 培养目标问题。做科学家好还是工程师好?研究型大学是不是不应该培养工程师?研究型大学如何培养工程师?现在很多985和211高校的口号都是建设国内一流的研究型大学,老师们都全力去做科研,而工程类专业,特别是软件工程又是以培养工程应用型人才为主,如果老师们都将精力用于做科研了,谁来培养工程师?怎么培养工程师?而且没有实际的项目开发经验,只是照本宣科,停留在纯理论的教学中,如何能够培养出合格的工程师?我个人觉得工科大学本科教育(工程类专业)应该还是以面向应用工程型为主,如果希望当科学家,应该是在硕士,特别是博士教育中。随着工程硕士规模的扩大,很多人硕士毕业之后还是当工程师。如果有志于当科学家的同学,应该从本科就做好准备,打下良好研究基础,学好数学、英语和一些基础理论课程,最好能够把算法、计算理论、编译原理、形式化方法、数据挖掘、计算机图形学等知识学好,为做科学研究打下基础,如果有机会的话,可以提前和老师们一起参与一下科研项目的研究,了解一下科研的流程,说不定还能够做出点啥科研成果。而对于大部分想做工程师的同学而言,软件开发类课程就非常重要了,无论是编程、分析设计、架构还是测试,以及数据库、操作系统、数据结构等基础课都是成为一名优秀工程师的基础,如果有机会,做做实际的应用型项目(不是停留在课程设计或者项目实训等模拟项目上),那帮助就更大了。在大学阶段有一个比较明确的职业规划,对于同学们的成长确实有很有帮助,虽然这个做起来有点难度。
· 课程体系的建设问题。这也是软件工程教育的一个核心问题,它对应于一个怎么做的问题,软件工程专业的课程体系要兼顾多方面,因此一套完善合理的课程体系需要不断调整和优化,这样难免会有一些同学会成小白鼠,但是这是人才培养道路上必经的一个过程,还望做过小白鼠、正在做小白鼠和即将成为小白鼠的同学们理解。一套科学合理的软件工程课程体系,我认为至少要考虑如下两个方面:用人企业的需求(分析用人企业所需的技能和技术,结合企业的招聘启事,请企业专家参与评审课程体系,包括课程的培养目标、先后顺序、教学大纲和实验安排等)、人才的长远发展(我们不能只是培养码农,需要培养出能够具有更好发展前景的软件人才,因此除了编码类课程,还需要开设需求、分析、设计、架构、测试和项目管理等课程)。对于教育工作者,人才是产品,用人单位是客户。我们都希望能够培养出高质量的产品,满足客户的需求,做到三方共赢!
· 教学与实践课时分配的问题。软件工程是一个重实践的专业,必须要多实践,重点培养实践能力,许多高校都在压缩教学课时,大量增加实践课时。但是现在有一个很重要的问题是很多学校的教学资源不足,不可能所有的课都在机房上。大家经常会提到一个“做中学”的问题,对于软件人才的培养,确实需要增加实践课时,我个人觉得如果网速足够快并提供合适的在线项目开发和管理平台,有些练习和项目完全可以在宿舍完成。教学课时没有必要开设那么多,我始终还是坚信那句话:大学教育更多是培养大家学习能力,而不是学习知识本身。
· 师资问题。作为软件工程专业的教师,必须不断更新自己的知识体系,需要至少在某一个或几个领域与最新技术接轨,每年应该也要实时充电,让学生们能够在学校就学到一些前沿知识。对于有些知识的学习,老师的理解能力和消化能力还是要强于学生,毕竟对行业和专业的了解更为全面和系统。我个人的观点是,作为从事软件工程一线教学的教师,应该有目的地提升自己的实际项目开发能力,不断学习新知识,“一门课上到退休”对我们来说并不适用,也不应该是这样。作为一名软件教育从业者,我要求学生要努力学习,学会自学新知识与技能,同时我也需要不断学习,只有这样,我自己也才能实现成为一流IT教育工作者的梦想。
· 高校与企业联合办学的问题。与企业联合办学是目前很多高校在培养软件人才时采用的一个重要的手段,这样一方面可以让学生学到一些企业实用技术,另一方面可以缓解师资不足的问题。学生也特别希望能够有一些企业老师来讲课,学习到更多实用的技术和经验。我想我的课之所有学生们喜欢,跟我之前有过几年项目开发经验有很大关系。但是在校企联合办学过程中,地域因素也不得不考虑,例如在北京,如果要找一流的测试专家给某个高校上软件测试的一些课程,可以找中国软件评测中心或国家信息中心软件评测中心,但是有很多二线、三线城市的高校就没有这么便利的企业教学资源,应该建立一个校企合作的平台。我觉得校企合作是一个对三方有利的方式,对于学生和学校的好处不用多说,对于企业而言,可以从中选拔优秀的潜在员工,还可以宣传自己的企业和产品,同时带来新的赢利点。很多国内外知名软件公司就成立了专门的校企合作部门,已经嵌入到师资培训、学生培养、实习生安排和社团活动等多个环节,并取得了不错的效果。
当然,还包括不同层次的学校人才培养的差异性问题、软件工程硕士培养问题、不同方向(开发、测试、互联网营销等)的软件人才培养问题等。每一个问题都可以作为一个专题讨论,恕本人才疏学浅,也不妄加评论了,以后有机会再跟大家进一步交流。以上很多观点都来自我2012年12月份写的一篇博文《如何培养软件工程人才——参加“第一届高等学校软件工程人才培养高峰论坛”有感》,欢迎大家一起交流和讨论。
CSDN:设计模式适用于哪些领域?它体现了什么?能给软件开发带来哪些帮助?
刘伟:设计模式主要应用于面向对象软件设计领域,对于面向对象编程也有很好的指导意义。很多人都是通过对设计模式的学习和掌握才真正理解面向对象的。我在给一些软件企业做内训时,接触过很多具有多年开发经验的Java或C#程序员,他们一直采用面向对象语言在从事软件开发,但是基本上还是按照传统的结构化编程方式,不理解抽象类和接口有什么作用,不明白什么时候该用类继承什么时候该用对象关联,通过对设计模式的学习,他们才真正领悟到面向对象的魅力,更好地从事面向对象设计与编码工作。
设计模式来源于众多专家的经验和智慧,是从许多优秀的软件系统中总结出的成功的、目的是为了更好地实现可维护性复用的设计方案。设计模式是在特定环境下为解决某一通用软件设计问题提供的一套定制的解决方案,该方案描述了对象和类之间的相互作用,它是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。我们知道,随着软件寿命的延长和软件规模的扩大,如何更好地维护软件和实现软件的复用是现在软件界面临的一个很重要的问题,越来越多的企业将越来越多的时间、精力和金钱花费在软件维护上,从某种意义上来说,设计模式的使用可以避免我们做一些重复性的设计工作,有助于提高设计和开发效率。此外设计模式提供了一套标准的设计词汇,方便开发人员之间交流,例如学过设计模式的人都知道适配器是怎么回事,都知道桥接模式应用于什么样的环境,都知道单例类如何实现,也都知道迭代器是干啥的。除此之外,设计模式对于提高系统的可重用性、可扩展性以及设计方案的文档化都具有重要的意义,对于面向对象的初学者而言,提高学习和掌握一些设计模式的知识有助于更好地理解面向对象的三大特性:封装、继承和多态,同时还能够帮助大家更好地阅读和理解现有类库和框架的源码,降低学习成本。
CSDN:可以说设计模式是程序员必备技能之一,你认为如何才能更好地学习设计模式(比如如何才能更好地抓住设计思想和本质)?
刘伟:“如果想体验一下运用模式的感觉,那么最好的方法就是运用它们”。设计模式的学习关键是深入理解和多加实践,简单说就是多思考、多实践。每一个设计模式都有自己的适用场景,也都有自己的优缺点,因此在使用设计模式的时候要做一些权衡,例如有的设计模式可以提高系统的可扩展性,但是可能会增加系统中类的个数或者影响性能;滥用模式不如不用模式,需要合理使用设计模式。
最好的学习方法是首先系统学习一下设计模式,掌握这些模式的一些典型应用,例如在现有类库或者框架中的应用情况,关键还是要多加实践,要尝试在项目开发中多使用设计模式,有些设计模式还是很容易用上的,例如工厂方法模式、适配器模式、策略模式等。我建议大家的一种模式学习流程如下:先通过一些生活实例引入某种设计模式,然后学习该模式的动机、定义和结构,再学习一两个使用该模式的典型实例,接下来学习该模式的一些扩展知识以及模式的优缺点和适用场景,最后再学会在项目开发中应用所学模式。我相信,通过这样一个学习流程,应该还是可以较为系统地掌握一个设计模式的。在我最新出版的《设计模式的艺术——软件开发人员内功修炼之道》一书的第一章中有一个专门的小节介绍了我个人对于设计模式及其学习的一些观点,大家感兴趣的话可以看看。
关于这本书我在这里多讲两句,《设计模式的艺术——软件开发人员内功修炼之道》一书是我多年设计模式使用和推广经验的一个结晶。这本书结合30多个应用实例分析和讲解每一个设计模式,力求让大家通过最通俗易懂的方式来学习设计模式,让大家在项目实例的引导下学会选择和合理运用设计模式,快速提升开发功力,真正理解和掌握每一个设计模式,在软件开发的内功修炼之道上迈出坚实的一步。在书中内嵌了适量的基于项目实例的思考题和练习题,内容全面,语言通俗,深入浅出,难度适中,可满足不同层次读者的需求。我相信这本书将会帮助大家更好地理解和掌握设计模式,我在CSDN技术博客(http://blog.csdn.net/LoveLion)中开辟了专门的讨论区,学习之路,与你同行,希望大家能够多沟通,多交流。
CSDN:设计模式确实是个好东西,但很多人为了设计模式而设计模式,因此有人提议达到一定高度后请忘记设计模式,对此你是怎么看的?
刘伟:我很赞成这个看法,就像我经常和学生说的,真正的模式高手就像一些武林高手一样:手中无剑,心中有剑。设计模式的“上乘”境界就是:手中无模式,心中有模式。模式使用的最高境界是你已经不知道某个具体设计模式的定义和结构,但你会灵活自如地选择一种设计方案(也许就是某个设计模式)来解决某个问题,设计模式已经成为你开发技能的一部分,能够手到擒来,“内功”与“招式”已浑然一体,要达到这个境界并不是看完某本书或者开发一两个项目就能够实现的,它需要不断沉淀与积累,因此对模式的学习不要急于求成。
在学习过程中我得重复那句话,千万不要滥用模式。每个模式都有自己的适用场景,不能为了使用模式而使用模式,滥用模式不如不用模式,因为滥用的结果不仅得不到“艺术品”一样的软件,还很有可能是一堆垃圾代码。
当你真正系统理解和掌握了每一个模式之后,设计模式已不复存在,它已经完完全全与你的开发技能融为一体,你可以将它遗忘。
CSDN:设计模式和语言之间存在必然关系吗?有人说设计模式用C#和Java语言最好,对此你有什么观点?
刘伟:设计模式和语言之间还是存在一定的关系,现在通常我们所指的设计模式都是一种狭义的设计模式,大部分都源于GoF 23种经典模式,这些模式都是基于面向对象编程语言的,对于目前比较热门的另一类函数式编程(FP)语言并不适用,例如Haskell和Miranda等。但是对于所有的面向对象编程语言以及支持面向对象编程的语言来说设计模式都是通用的,不同的语言只是提供不同语法风格的实现,同一种模式的涵义和实现思路都是一致的。
由于在.netFramework和JDK类库中使用了大量的设计模式,而且C#和Java都是纯面向对象语言,它们都提供了较为方便的反射机制,同时也拥有大量的开源框架,在C#和Java相关社区讨论设计模式也会更多一些,相关的书和资料也很多,但这些都只能说明使用这两种语言来实现设计模式相对更为方便,并不代表只能用它们来实现。近年来,越来越多的基于其他语言的设计模式书籍问世,足以说明在其他各种面向对象语言中都可以很好地运用设计模式来设计软件。
总之,如果你正在从事面向对象开发或正准备从事面向对象开发,无论你是使用Java、C#、Object-C、VB.net、Smalltalk等纯面向对象编程语言,还是使用C++、PHP、Delphi、JavaScript等可支持面向对象编程的语言,如果你一点设计模式也不懂,可以毫不夸张地说:你out了。
CSDN:万物都有个度,设计模式并非万能,你能谈谈设计模式的一些局限吗?另外设计模式能改变软件质量吗?
刘伟:没有完美的模式,每一种设计模式都存在优缺点,就像我前面所说,有些设计模式可以提高系统的可扩展性,但是可能会增加系统中类的个数或者影响性能,例如工厂模式、策略模式、观察者模式等;有些设计模式可能连最基本的开闭原则都不满足,但是它们在特定情况下为某一软件设计问题提供了解决方案,例如单例模式、外观模式等。严格来说,每一种设计模式都存在自己的局限,因此在使用的时候需要我们做出一些权衡和折中,合理选择设计模式,慎重考虑是否需要使用设计模式。软件工程大师John Vlissides说过:模式从不保证任何东西,它不能保证你一定能够做出可复用的软件,提高你的生产率,更不能保证世界和平。模式并不能替代人来完成软件系统的创造,它们只不过会给那些缺乏经验但却具备才能和创造力的人带来希望。
对于设计模式是否能够改变软件质量,我只能说从某些角度或者一定程度上还是可以的。但是它不能确保,如果滥用模式,不仅不会改善软件质量,还会严重降低软件质量,导致设计方案和代码发生混乱。国外(加拿大蒙特利尔大学)有学者从程序员的主观角度做了一些研究,我把他们的研究成果翻译成中文也发布在CSDN博客中了,结果引起了大家“激烈”的讨论,因为不同的设计模式的出发点不一样,因此对于软件质量的影响也不一样。
软件质量是一个比较宽泛的概念,具体来说包括很多质量属性,例如正确性、健壮性、扩展性、可复用性、可维护性、安全性和可靠性等。有些设计模式可以改善某一个或一些质量属性,但是又会导致另一些质量属性受影响。例如单例模式,它可以节省系统资源,但是它的可维护性和可扩展性并不好;再如工厂模式,虽然它避免了创建代码的蔓延,扩展性也很好,但是它增加了系统的复杂度,一定程度上也影响了程序的性能。因此,对于设计模式是否能够改变软件质量,这句话没有明确的答案,选择设计模式的重点在于我们更加关注哪个或哪些质量属性,是可扩展性、可复用性还是性能,这需要我们做一些权衡和折中,大部分基于面向对象的设计模式重点考虑的还是可复用性和可维护性。因此,大部分设计模式可以改善系统的可复用性和可维护性,这句话是没有问题的,但是对于其他质量属性的改善,我们就只能逐个模式进行分析了。
目前,我带领一个研究小组正在从事有关设计模式对软件质量影响的定量研究,简单来说就是通过一些实实在在的数字(度量因子),而不是主观的评价来说明设计模式对软件质量的影响,我们在研究过程中选取了一些经典的软件度量因子,包括CK、LK、MOOD等,这项工作目前已经取得了一定的进展,我们发现有些模式确实能够很显著地改变一些软件度量因子,今后我会在博客中发布一些我们的研究成果。关于这项工作,我们最终还是希望能够融入到一套自动重构系统中,能够为用户选择是否重构提供一些理论依据。
CSDN:有人表示,设计模式并不难学,难的是如何成功应用,在这方面你有心得分享吗?
刘伟:首先这句话我很赞同。我也经常跟学生或者学员们说,设计模式一旦找到学习的感觉和有效的方式,学习起来并不难,而且学习的过程会很有趣,也很有成就感。但是很多人学完了并不知道怎么在项目中运用设计模式,对于学习,我想强调的是,每一个模式我们至少应该掌握如下几点:这个设计模式的意图是什么,它要解决一个什么问题,什么时候可以使用它;它是如何解决的,掌握它的结构图,记住它的关键代码;能够想到至少两个它的应用实例,一个生活中的,一个软件中的;这个模式的优缺点是什么,在使用时要注意什么。当你能够回答上述所有问题时,恭喜你,你了解一个设计模式了。
注意,我用的是“了解”,并不代表你掌握这个模式了,如果不懂如何使用一个设计模式,而只是学过,能够说出它的用途,绘制它的结构,充其量也只能说你了解这个模式,严格一点说:不会在开发中灵活运用一个模式基本上等于没学。设计模式是“内功心法”,它还是要与“实战招式”相结合才能够相得益彰。学习设计模式的最终目的还是在于应用,如果要做到熟练运用每一个设计模式,我的建议如下:
· 深入理解每一个模式的动机,除了熟悉它的标准结构,还要熟悉它的一些常用的变化,以及一些常用的模式联用机制,例如有的设计模式可以简化、有的经常会和别的设计模式一起使用。在学习设计模式之前,一定要深入理解面向对象设计原则,包括SOLID、合成复用原则以及迪米特法则,因为绝大多数设计模式都体现了一种或多种面向对象设计原则,理解面向对象设计原则是学好用好设计模式的基础。除此之外,还要熟悉设计模式的适用场景,知道什么时候用以及什么时候不用这个模式,这个很关键,千万不能滥用模式。
· 除了一些生活示例外,一定要看看已有软件中的模式应用实例,例如对于Java开发人员而言,我觉得一个很好的学习模式的方式是结合JDK一起学习,JDK类库中应用了所有的设计模式,例如Java IO中用到了装饰模式,AWT/Swing中用到了组合模式、抽象工厂模式,Runtime等类用到了单例模式,事件处理中用到了观察者模式,内置了迭代器等,而且很多现有的开源类库中也用到了很多设计模式,例如JUnit、Struts、Spring等框架中就用到了很多设计模式,如在JUnit中使用了组合模式、模板方法模式等设计模式。除此之外,还可以下载一些明确提到使用了设计模式的软件的源代码看看,看看别人是如何使用设计模式的。
· 学会思考和对比使用模式之前和使用模式之后,软件设计和代码结构是如何发生改变的。通过这种思考和对比加深对模式的理解,以便在实际开发中更好地运用设计模式,同时也学会使用设计模式来对代码进行重构。我在《设计模式的艺术——软件开发人员内功修炼之道》一书中也引入了大量真实的实例来讲解设计模式,目的也是希望读者能够真正懂得如何在项目开发中运用设计模式,例如通过重用算法库来讲解适配器模式、设计跨平台图像浏览系统来讲解桥接模式、设计界面皮肤库来讲解冲向工厂模式等,并提供了大量练习来强化和巩固所学知识。有效的学习也是提高应用能力的一个途径。
· 多交流,和别人讨论和分享设计方案。我个人提倡和鼓励分享和讨论,也乐于分享和讨论,当遇到设计和模式应用问题时,可以把问题拿出来,与同事讨论,或者通过网络与网友讨论,我相信通过这个讨论过程你一定会受益匪浅。而且我建议当你有了一个好的设计方案时,不妨把它分享出来,让大家学习学习,你的经验将给别人带来帮助,这何尝不是一种美德呢。如果对模式使用很纠结很困惑的朋友,可以通过我的CSDN技术博客和我交流,也许我能帮到你。
CSDN:软件架构设计需要考虑的因素有哪些?如何才能做好架构设计?
刘伟:软件架构是软件工程一个很重要的分支,随着软件规模的扩大和软件寿命的延长,软件架构也越发重要。就像建筑领域,盖一个狗窝不需要进行分析与设计,但是如果是要盖一座万人体育场或者摩天大楼,那一定会离不开设计师。软件工程与之同理,好的架构能够决定软件的成败。软件架构不只是简单的分层或者划分模块,它包括更多的内容,例如需求确认、系统分解、架构风格的选择(B/S还是C/S)、技术选型(Java还是.net,Oracle还是MySQL等)、物理架构设计、数据架构设计、逻辑架构设计等等,通常架构师还要参与包设计、核心模块设计以及类设计等概要设计和详细设计工作。正因为软件架构设计涉及的内容相当多,因此需要考虑的因素也很多。
软件架构设计需要考虑的问题包括需求、成本、进度、团队人员的素质等等,我觉得首先应该考虑的是需求。俗话说:需求进、架构出。如果没有深入分析需求,往往难以得到好的架构,在分析需求时除了功能需求外,还要考虑一些非功能需求,包括是否要与第三方系统兼容、是否要考虑到数据迁移、是否要考虑到平台的多样性等等,非功能需要中很多因素也会影响到架构,例如安全性、性能等等,因此好的架构师应该尽量多参与需求分析,听听客户的想法,需求是影响架构最大的因素,架构风格的选择、技术选型等都与需求息息相关。除此之外,成本、进度以及人员素质等对架构的影响也很大,因此在架构设计的时候也需要充分考虑。没有完美的设计方案,无论是SAAM还是ATAM等架构评估方法,其实都是一种折中的方法,无法使每一个因素都达到最优,所以我们要全盘考虑,具体问题具体分析,找出当前应用最合适的一种设计方案。
对于如何做好架构设计,我个人建议如下:
· 深入理解需求:错误的需求必定导致错误的架构,因此一定要深入理解需求,作为架构师,一个很重要的职责是进一步确认和细化需求,例如了解当前系统是否需要和别的系统交互,通过什么接口来交互,在交互的时候数据如何发送和接收等等,了解得越细致对于架构设计帮助越大,因此,一定要做好需求工作。
· 全面考虑非功能因素:很多软件项目的失败并不是因为功能的缺失或者错误,而是因为一些非功能因素考虑不周全,例如服务器的性能、最多容许的并发用户数、安全性等等。作为一名好的架构师,应该认识到当前系统最重要、最核心的质量属性是哪些,例如有些项目的核心是安全性,此时可能需要牺牲性能来提高安全性,这就需要和客户进行交流,做一些平衡,通盘考虑。可能还要专门就安全性来做专门的设计,例如在我曾经参与过的项目中,光一个安全性就需要做好几个层次的设计,有代码级的、数据库级的、功能级的、系统级的,但性能是肯定会要下降的。因此,在进行架构设计时要学会取舍,当然取舍的前提是已得到客户的认可。优秀的架构师会引导客户接受合理的架构方案,以确保系统能够真正投入使用。
· 构造合理的系统模型:建筑设计师都擅长绘图,同样作为一门工程学科,软件工程也需要绘制一些图,越大的项目需要的图越多,要求也越严格。这些图又可以称为模型,好的架构设计一定要有一套完整、合理的模型,包括数据模型(概念数据模型、逻辑数据模型和物理数据模型),结构模型(类图、包图、组件图和部署图等),动态模型(状态图、活动图和顺序图等)等,要熟练使用UML等技术来构造系统模型,这些图形也有助于进一步理解和验证架构的合理性,例如使用类图描述系统的类结构、使用包图描述系统的分层结构、使用组件图描述系统物理文件的联系以及与第三方库之间的交互、使用状态图描述核心业务对象的状态及其转换、使用活动图描述核心业务流程等等。在现代软件开发中,越来越多的软件公司都意识到建模的重要性,我也给一些软件公司做过UML建模技术的培训,发现很多公司在学完UML之后就能够将这些技术很好地用于实际的项目开发,特别是软件架构的设计,这与之前直接根据需求文档写代码,越写越盲目,最后还可能出现项目失败的情况相比,已经有了很大的改善。我目前也正在写一本UML相关的书,预计2013年能够发行,希望能够给广大开发人员提供帮助。
· 保证架构的稳定性和可扩展性:好的架构应该是稳定而又易于扩展的,因此在进行架构设计时要充分考虑这些因素,使用设计模式的目的实际上就是在一定程度上来保持架构的稳定性和可扩展性。因此,对于一名优秀的架构师而言,软件需求、软件质量属性、UML、设计模式等知识都是必须要具备的。
CSDN:设计模式在架构设计中作用大吗?如何在架构设计中使用设计模式?
刘伟:设计模式在架构设计中还是挺重要,几乎每一本关于架构设计的书都会提到设计模式,甚至还会精讲几个常用的设计模式。就像前面我所说设计模式可以在一定程度上提高架构设计的稳定性和可扩展性,不会因为一点点小的改动就导致架构方案发生大的改变。合理使用设计模式,可以在提高代码质量的同时提升软件架构设计的质量。
在架构设计中使用设计模式时,也需要充分理解需求,才能正确选择和使用设计模式。大部分设计模式都提供了抽象层,目的是让系统更易于扩展。通常的做法是要学会分析出系统的扩展点,对于一些可变的扩展点,需要提供一个稳定的抽象层,然后通过具体层来实现具体的业务功能。比如:如果在需求分析和架构设计时发现某个业务功能将来可能会进行改进,例如加密算法,此时可以提供一个抽象的加密类,而将具体的加密类作为其子类,所有的依赖和关联都基于抽象加密类,引入配置文件,将具体加密类类名存储在配置文件中,然后利用反射和注入的方式将具体加密类对象提供给其他业务类使用。当需要更换加密算法时,只需增加一个新的具体加密类并修改配置文件即可,原有设计方案和代码都无须进行修改,系统符合开闭原则,具有良好的可扩展性,这就是策略模式的一个应用。
对于需求分析和架构设计中的一些具体问题,有些设计模式已经提供了很好的解决方案,例如当你需要经常调用一些第三方接口时可以考虑使用适配器模式,当你需要为多个外部子接口提供一个一致的入口时可以使用外观模式,等等。每一个设计模式都相当于“三十六计”中的一计,一不小心就在你的项目开发中用上了,当你遇到此类问题时就可以游刃有余地解决,这就是设计模式的魅力所在吧。
如果你的项目涉及到更多底层的东西,例如事件处理、缓存服务、类工厂、消息队列等等,特别是你要开发的是一个API或者框架,那么所涉及到的设计模式就更多了,大家有兴趣可以看看《应用框架的设计与实现-.net平台》和《软件框架设计的艺术》这两本书,里面就提到了不少设计模式在框架设计中的应用。虽然框架不等于架构,但是框架的架构设计需要更加灵活,想想谁会去用那些僵硬固化、不易于扩展的框架呢?
总的来说,当你对需求足够了解,已深入学习和理解各种设计模式,并能够准确寻找出扩展点或一些设计模式的适用场景时,你就可以合理使用相应的设计模式了。我相信,有效地使用设计模式一定会提高你架构设计的质量,解决你在架构设计中遇到的一些困难。
CSDN:能不能谈下重构有哪些意义?另外重构与设计模式间有什么关系?
刘伟:说得专业一点,重构就是在不改变软件现有功能的基础上,通过调整程序代码改善软件的质量、性能,使其程序的设计模式和架构更趋合理,提高软件的扩展性和维护性。这是重构的定义,简单来说就是不改变软件的功能,优化软件设计和代码,让软件更易于扩展和维护,当然也包括易于复用。软件工程大师Martin Fowler等人总结出了一些常用的重构技术,将其写成了一本面向对象领域的经典著作——《重构:改善既有代码的设计》。
软件开发大师Robert C.Martin在《程序员的职业素养》(The Clean Coder)一书中提到要勇于整理代码,通过整理代码让当它们更易于理解,更易于修改,也更易于扩展。这样代码的bug也会随之减少,不应该让代码劣化而视若不见。我觉得Bob大叔说的很好,这就是重构的意义。因此我常常跟学生们说,如果想成为一名合格的面向对象程序员,设计模式和重构是你必须要学的两项基本技能。
重构和设计模式之间的关系也很紧密,在MartinFowler的《重构》一书中也提到了好几种使用设计模式来进行重构的手段。另外,还有一本书,名为《重构与模式》(Refactoring toPatterns,个人感觉翻译为“面向模式的重构”或者“重构到模式”更合适),作者是JoshuaKerievsky,这本书获得过第15届Jolt生产效率大奖。在这本书中提到了27种以模式为导向的重构,包括在设计中实现模式、趋向模式和去除模式。很客观地对一些设计模式进行了评价,既告诉我们如何通过设计模式来提高代码质量,又告诉我们如何在代码中去除一些不合理的模式应用。该书大部分重构方案还是实现和趋向模式的,例如将创建知识搬移到Factory、将聚集操作搬移到Visitor、用Strategy替换条件逻辑、用State替换状态改变条件语句等。每一种重构方案都提供了相应的实例,让你做一次重构的操练。当然,在读这本书之前,我建议大家还是先把设计模式好好学一下,否则里面有一些重构方案就没有办法理解了,毕竟这本书不是专门讲设计模式的。建议大家在学完设计模式之后再看这本书,既能够学到如何在实际项目中运用设计模式,又能学到如何在不同情况下选择设计模式,这本书也是我研究团队成员必读书籍之一。
CSDN:那在什么情况下才需要考虑重构?衡量是否需要重构的标准是什么?重构的难题有哪些?
刘伟:在Robert C.Martin新近出版的《程序员的职业素养》(The Clean Coder)一书中提到:当你发现修改软件不像你预想的那样简单时,你便应该改进设计,使后续修改变简单。让软件保持固定不变是危险的,如果一直不重构代码,等到最后不得不重构时,将会发现代码已经很僵化。因此,要将重构变成一种习惯,每一次在阅读代码或者检入代码时都要考虑对代码进行改善与优化。如果你希望自己的代码灵活可变,那就应该时常修改它!我觉得这就是对重构时机最好的回答。如果你发现代码太僵硬,不易于扩展(你可以扩展一下试试),特别是那些在需求文档或者客户嘴里多次提到的需要改变的地方,一定要尽量灵活,当缺少这种灵活性时,你应该马上重构。我个人觉得,衡量是否需要重构的标准就是现有设计方案或者代码是否不易扩展,复用性是否存在问题,将来是否会增加维护成本。为了将来,应该重构。
很多人明知设计方案或者代码有问题,却不愿意去重构,这里面也存在很多原因,我觉得很多人之所有害怕重构,最大的问题在于担心重构之后会引入新的问题,难于测试。关于这一点,Robert C. Martin和我的想法不谋而合,尽量实现测试的自动化。由于重构主要还是代码级的,对应的测试也主要是单元测试,单元测试的自动化程度在所有的测试中应该是最高的,因此只要我们有一套完整的测试用例,重构之后可以对应修改少许的测试代码就可以马上实施回归测试了。我个人觉得这个问题还是可以解决的,但是对于测试代码不完善或者根本没有测试代码的项目,实现起来恐怕会比较麻烦。因此,培养良好的开发习惯很重要,建议大家有空看看测试驱动开发(TDD)方面的书,我相信对大家,特别是一线开发人员还是挺有帮助的。
当然也有人觉得重构麻烦,不愿意重构,觉得功能都没有问题了,没有必要再进行重构,指不定重构之后功能还会出问题。有这种想法的人缺乏一种专业精神,我个人觉得要想成为一名专业的程序员,应该掌握一些常用的重构技巧并经常在项目开发中使用它们,可能开始会比较麻烦一点,但是当重构成为你开发技能一部分,就像写for、if语句一样很平常,你就不会觉得这是一种麻烦。在现在主流的IDE,例如Eclipse、VisualStudio中,重构都已成为菜单的一部分,可见业界对于重构已达成共识,很多人把重构当做写注释一样,是一个很平常的提高代码质量的操作而已。
还有一个影响重构的因素是项目进度、成本等受到制约。明明知道要重构,但是项目工期很短,没有时间和精力来重构,各种赶工很常见,后果有时候也很严重,代码质量很差,在维护和二次开发时大量代码可能需要推倒重写。对于这个问题,只能尽量去跟客户或者领导们交涉了,建议适当延长时间或者缩减功能。