程序员的持续学习之道

原文出处: ShareCore (@Justin_Programer)

 

我其实是一个悲观主义者,从开始成为一名程序员的那一天开始,我就对自己的职业充满着担忧。早期,我担忧的是我刚进入这一行,落后身边从事这一行多年的人太多,跟他们比,我一开始就处于“不公平“的起跑线上;二是当我稍微深入这行后,发现技术的道路充满了歧路和死胡同,稍不留神,就将落入再也跟不上技术进步的节奏,身边也充斥着类似的示例。

好在在这条路上,我找到了一个克服担忧的良方:学习,坚持不懈的学习!

人类最大的恐惧莫过于对未知的恐惧,而学习,是克服未知的一个最有效途径。那作为一个程序员,该如何才能做到坚持不懈的学习呢?我想主要有以下几点:

1. 不抗拒变化,跟踪变化

这里说的变化,更多指的是技术的变化。新技术永远层出不穷,如果你抗拒变化,或惧怕变化,在心里优势上就落后了一大截。很多程序员的技术道路越走越窄,当一门新技术来临时,他们从不去了解或研究,他们要么认为这是”换汤不换药“的”技术幌子“,不值得去研究,要么等着别人去研究,自己被动的接受别人的结论与成果。诚然,等待别人的研究结果也可行,但是,当身边没有人去研究呢?当研究的人不愿意提供给你他的研究结果呢?当别人的结论和结果是错误的呢?

当然,业界的新技术层出不穷,要去跟踪每一项新技术的变化也是不可能的,我的建议是尽量掌握基础的技术,越是基础的技术越是恒定。如计算机的体系架构,TCP,HTTP,各类编程范式,OOP,MVC架构等,都是好多年来没有发生过变化的技术了。许多新技术也是建立在他们上面,当你了解了这些基础的技术,建立在他们之上的新技术也就能很快掌握了,并能迅速而准确地对这些新技术作出“价值判断”。

技术的变化,其实也并不全是指业界的新技术来临,更多指的是,超出自己当前技术能力外,主动研究和选择的“新技术” ,主动丢弃老的套路和技术,不固步自封。见过很多的程序员,他们写了很多年的代码了,技术能力还停留在数据库层写一个Sql(存储过程)+前端调用上。为什么如此呢?因为自开始写代码起,这样简单粗暴的方式就可以解决他们面对的问题了。所以,当碰到新的问题,他们就继续沿用着这种方式,而从不去看看业界是不是有更合适的方案来解决。长此以往,他们的技术能力也就不会有任何的变化,工作十年,只是将第一年所学的技术重复十年而已

2. 书宜杂读,业宜精钻

建筑学家梁思成赴美留学,其父梁启超告诫说:你该挤出一部分时间学些常识性东西,特别是文学或人文科学,稍稍多用点工夫就能有大的收获。我深怕你因所学太专一,把多彩的生活弄得平平淡淡,生活过于单调,则生厌倦心理,厌倦一生即成苦恼之事……书宜杂读,业宜精钻。

学习本身应该是一件很快乐的事情,但如果只是一味地沉浸于专业学习之中,难免会将本来快乐的事情弄成平淡和单调,更可怕的是,由此产生厌倦心理,放弃学习。

所以,可以尽量跟随自己的兴趣,去更广泛地读书。历史,文学,心理学,哲学等等各方面的书都可以尝试去读,它们不仅丰富你的知识,更能让你在阅读中受到感动、教育和启迪。书读的更多更广泛,知道的事也就多,思路更加开阔,解决问题的能力也就能高于常人,从而反过来帮助到你专业能力的提升。

3. 投资团队,积极打造学习型团队

现代社会,难免的一点就是个人必须置身于群体之中,程序员更是如此。从群体心理学的角度来看,在群体里,个人的才智被削弱,异质性被同质性所吞没。由此,如果一个团队不爱学习,那么,其中的成员也很难坚持学习(个性和意志力特别强的人除外)。

如果你爱学习,请想办法让你的团队也变得爱学习,这样,你对学习的坚持将变得更加容易。或许你认为建立学习氛围,是团队领导的事情,跟自己无关。领导当然可以来做也需要来做这样的事情,但要明白的一点,学习这事,如果变成从上向下,就难免“政治化”了,容易失去它本身的意义。而从下往上,更能建立轻松和谐的学习环境。

一些软件设计原则

出自:http://blog.jobbole.com/685/

导读:酷壳网的陈皓给大家介绍了软件设计的一些原则,作者认为一个好的程序员通常由其操作技能、知识水平,经验层力和能力四个方面组成。软件设计的这些原则,每一个程序员都应该了解。相信大家从中能够从中学了解到一些设计原理方面的知识。
文章内容如下:

以前本站向大家介绍过一些软件开发的原则,比如优质代码的十诫和Unix传奇(下篇)中所以说的UNIX的设计原则。相信大家从中能够从中学了解到 一些设计原理方面的知识,正如我在《再谈“我是怎么招聘程序员”》中所说的,一个好的程序员通常由其操作技能、知识水平,经验层力和能力四个方面组成。在这 里想和大家说说设计中的一些原则,我认为这些东西属于长期经验总结出来的知识。这些原则,每一个程序员都应该了解。但是请不要教条主义,在使用的时候还是 要多多考虑实际情况。

下面这些原则,不单单只是软件开发,可以推广到其它生产活动中,甚至我们的生活中

Don’t Repeat Yourself(DRY)

DRY是一个最简单的法则,也是最容易被理解的。但它也可能是最难被应用的(因为要做到这样,我们需要在泛型设计上做相当的努力,这并不是一件容易 的事)。它意味着,当我们在两个或多个地方的时候发现一些相似的代码的时候,我们需要把他们的共性抽象出来形一个唯一的新方法,并且改变现有的地方的代码 让他们以一些合适的参数调用这个新的方法。

参考http://en.wikipedia.org/wiki/Don%27t_repeat_yourself

Keep It Simple, Stupid (KISS)

KISS原则在设计上可能最被推崇的,在家装设计,界面设计,操作设计上,复杂的东西越来越被众人所BS了,而简单的东西越来越被人所认可,比如这些UI的设计和我们中国网页(尤其是新浪的网页) 者是负面的例子。“宜家”(IKEA)简约、效率的家居设计、生产思路;“微软”(Microsoft)“所见即所得”的理念;“谷歌”(Google) 简约、直接的商业风格,无一例外的遵循了“kiss”原则,也正是“kiss”原则,成就了这些看似神奇的商业经典。而苹果公司的iPhone/iPad 将这个原则实践到了极至。

把一个事情搞复杂是一件简单的事,但要把一个复杂的事变简单,这是一件复杂的事。

参考http://en.wikipedia.org/wiki/KISS_principle

Program to an interface, not an implementation

这是设计模式中最根本的哲学,注重接口,而不是实现,依赖接口,而不是实现。接口是抽象是稳定的,实现则是多种多样的。以后面我们会面向对象的SOLID原则中会提到我们的依赖倒置原则,就是这个原则的的另一种样子。还有一条原则叫Composition over inheritance(喜欢组合而不是继承),这两条是那23个经典设计模式中的设计原则。

Command-Query Separation (CQS)–命令-查询分离原则

查询:当一个方法返回一个值来回应一个问题的时候,它就具有查询的性质;
命令:当一个方法要改变对象的状态的时候,它就具有命令的性质;

通常,一个方法可能是纯的Command模式或者是纯的Query模式,或者是两者的混合体。在设计接口时,如果可能,应该尽量使接口单一化,保证 方法的行为严格的是命令或者是查询,这样查询方法不会改变对象的状态,没有副作用,而会改变对象的状态的方法不可能有返回值。也就是说:如果我们要问一个 问题,那么就不应该影响到它的答案。实际应用,要视具体情况而定,语义的清晰性和使用的简单性之间需要权衡。将Command和Query功能合并入一个 方法,方便了客户的使用,但是,降低了清晰性,而且,可能不便于基于断言的程序设计并且需要一个变量来保存查询结果。

在系统设计中,很多系统也是以这样原则设计的,查询的功能和命令功能的系统分离,这样有则于系统性能,也有利于系统的安全性。

参考http://en.wikipedia.org/wiki/Command-query_separation

You Ain’t Gonna Need It(YAGNI)

这个原则简而言之为——只考虑和设计必须的功能,避免过度设计。只实现目前需要的功能,在以后您需要更多功能时,可以再进行添加。
如无必要,勿增复杂性。

软件开发先是一场沟通博弈。

以前本站有一篇关于过度重构的文章,这个示例就是这个原则的反例。而,WebSphere的设计者就表示过他过度设计了这个产品。我们的程序员或是架构师在设计系统的时候,会考虑很多扩展性的东西,导致在架构与设计方面使用了大量折衷,最后导致项目失败。这是个令人感到讽刺的教训,因为本来希望尽可能延长项目的生命周期,结果反而缩短了生命周期。

参考http://en.wikipedia.org/wiki/You_Ain%27t_Gonna_Need_It

Law of Demeter–迪米特法则

迪米特法则(Law of Demeter),又称“最少知识原则”(Principle of Least Knowledge),其来源于1987年荷兰大学的一个叫做Demeter的项目。Craig Larman把Law of Demeter又称作“不要和陌生人说话”。在《程序员修炼之道》中讲LoD的那一章叫作“解耦合与迪米特法则”。关于迪米特法则有一些很形象的比喻:

如果你想让你的狗跑的话,你会对狗狗说还是对四条狗腿说?

如果你去店里买东西,你会把钱交给店员,还是会把钱包交给店员让他自己拿?

和狗的四肢说话?让店员自己从钱包里拿钱?这听起来有点荒唐,不过在我们的代码里这几乎是见怪不怪的事情了。

对于LoD,正式的表述如下:
对于对象‘O’中一个方法‘M’,M应该只能够访问以下对象中的方法:

  • 1.对象O;
  • 2.与O直接相关的Component Object;
  • 3.由方法M创建或者实例化的对象;
  • 4.作为方法M的参数的对象。

在《Clean Code(代码整洁之道)》一书中,有一段Apache framework中的一段违反了LoD的代码:

final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();

这么长的一串对其它对象的细节,以及细节的细节,细节的细节的细节……的调用,增加了耦合,使得代码结构复杂、僵化,难以扩展和维护。

在《重构》一书中的代码的环味道中有一种叫做“Feature Envy”(依恋情结),形象的描述了一种违反了LoC的情况。Feature Envy就是说一个对象对其它对象的内容更有兴趣,也就是说老是羡慕别的对象的成员、结构或者功能,大老远的调用人家的东西。这样的结构显然是不合理的。 我们的程序应该写得比较“害羞”。不能像前面例子中的那个不把自己当外人的店员一样,拿过客人的钱包自己把钱拿出来。“害羞”的程序只和自己最近的朋友交 谈。这种情况下应该调整程序的结构,让那个对象自己拥有它羡慕的feature,或者使用合理的设计模式(例如Facade和Mediator)。

参考http://en.wikipedia.org/wiki/Principle_of_Least_Knowledge

面向对象的S.O.L.I.D原则

一般来说这是面向对象的五大设计原则,但是,我觉得这些原则可适用于所有的软件开发。

Single Responsibility Principle (SRP)–职责单一原则

关于单一职责原则,其核心的思想是:一个类,只做一件事,并把这件事做好,其只有一个引起它变化的原因。单一职 责原则可以看作是低耦合、高内聚在面向对象原则上的引申,将职责定义为引起变化的原因,以提高内聚性来减少引起变化的原因。职责过多,可能引起它变化的原 因就越多,这将导致职责依赖,相互之间就产生影响,从而极大的损伤其内聚性和耦合度。单一职责,通常意味着单一的功能,因此不要为一个模块实现过多的功能 点,以保证实体只有一个引起它变化的原因。

Unix/Linux是这一原则的完美体现者。各个程序都独立负责一个单一的事。

Windows是这一原则的反面示例。几乎所有的程序都交织耦合在一起。

Open/Closed Principle (OCP)–开闭原则

关于开发封闭原则,其核心的思想是:模块是可扩展的,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的

对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。

对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对类进行任何修改。

对于面向对象来说,需要你依赖抽象,而不是实现,23个经典设计模式中的“策略模式”就是这个实现。对于非面向对象编程,一些API需要你传入一个 你可以扩展的函数,比如我们的C语言的qsort()允许你提供一个“比较器”,STL中的容器类的内存分配,ACE中的多线程的各种锁。对于软件方面, 浏览器的各种插件属于这个原则的实践。

Liskov substitution principle (LSP)–里氏代换原则

软件工程大师Robert C. Martin把里氏代换原则最终简化为一句话:“Subtypes must be substitutable for their base types”。也就是,子类必须能够替换成它们的基类。即:子类应该可以替换任何基类能够出现的地方,并且经过替换以后,代码还能正常工作。另外,不应该 在代码中出现if/else之类对子类类型进行判断的条件。里氏替换原则LSP是使代码符合开闭原则的一个重要保证。正是由于子类型的可替换性才使得父类 型的模块在无需修改的情况下就可以扩展。

这么说来,似乎有点教条化,我非常建议大家看看这个原则个两个最经典的案例——“正方形不是长方形”和“鸵鸟不是鸟”。通过这两个案例,你会明白 《墨子小取》中说的——“娣,美人也,爱娣,非爱美人也….盗,人也;恶盗,非恶人也。”——妹妹虽然是美人,但喜欢妹妹并不代表喜欢美人。盗贼是人,但 讨厌盗贼也并不代表就讨厌人类。这个原则让你考虑的不是语义上对象的间的关系,而是实际需求的环境

在很多情况下,在设计初期我们类之间的关系不是很明确,LSP则给了我们一个判断和设计类之间关系的基准:需不需要继承,以及怎样设计继承关系。

Interface Segregation Principle (ISP)–接口隔离原则

接口隔离原则意思是把功能实现在接口中,而不是类中,使用多个专门的接口比使用单一的总接口要好。

举个例子,我们对电脑有不同的使用方式,比如:写作,通讯,看电影,打游戏,上网,编程,计算,数据等,如果我们把这些功能都声明在电脑的抽类里 面,那么,我们的上网本,PC机,服务器,笔记本的实现类都要实现所有的这些接口,这就显得太复杂了。所以,我们可以把其这些功能接口隔离开来,比如:工 作学习接口,编程开发接口,上网娱乐接口,计算和数据服务接口,这样,我们的不同功能的电脑就可以有所选择地继承这些接口。

这个原则可以提升我们“搭积木式”的软件开发。对于设计来说,Java中的各种Event Listener和Adapter,对于软件开发来说,不同的用户权限有不同的功能,不同的版本有不同的功能,都是这个原则的应用。

Dependency Inversion Principle(DIP)–依赖倒置原则

高层模块不应该依赖于低层模块的实现,而是依赖于高层抽象。

举个例子,墙面的开关不应该依赖于电灯的开关实现,而是应该依赖于一个抽象的开关的标准接口,这样,当我们扩展程序的时候,我们的开关同样可以控制 其它不同的灯,甚至不同的电器。也就是说,电灯和其它电器继承并实现我们的标准开关接口,而我们的开关产商就可不需要关于其要控制什么样的设备,只需要关 心那个标准的开关标准。这就是依赖倒置原则。

这就好像浏览器并不依赖于后面的web服务器,其只依赖于HTTP协议。这个原则实在是太重要了,社会的分工化,标准化都是这个设计原则的体现。

参考http://en.wikipedia.org/wiki/Solid_(object-oriented_design)

Common Closure Principle(CCP)–共同封闭原则

一个包中所有的类应该对同一种类型的变化关闭。一个变化影响一个包,便影响了包中所有的类。一个更简短的说法是:一起修改的类,应该组合在一起(同 一个包里)。如果必须修改应用程序里的代码,我们希望所有的修改都发生在一个包里(修改关闭),而不是遍布在很多包里。CCP原则就是把因为某个同样的原 因而需要修改的所有类组合进一个包里。如果2个类从物理上或者从概念上联系得非常紧密,它们通常一起发生改变,那么它们应该属于同一个包。

CCP延伸了开闭原则(OCP)的“关闭”概念,当因为某个原因需要修改时,把需要修改的范围限制在一个最小范围内的包里。

参考http://c2.com/cgi/wiki?CommonClosurePrinciple

Common Reuse Principle (CRP)–共同重用原则

包的所有类被一起重用。如果你重用了其中的一个类,就重用全部。换个说法是,没有被一起重用的类不应该被组合在一起。CRP原则帮助我们决定哪些类 应该被放到同一个包里。依赖一个包就是依赖这个包所包含的一切。当一个包发生了改变,并发布新的版本,使用这个包的所有用户都必须在新的包环境下验证他们 的工作,即使被他们使用的部分没有发生任何改变。因为如果包中包含有未被使用的类,即使用户不关心该类是否改变,但用户还是不得不升级该包并对原来的功能 加以重新测试。

CCP则让系统的维护者受益。CCP让包尽可能大(CCP原则加入功能相关的类),CRP则让包尽可能小(CRP原则剔除不使用的类)。它们的出发点不一样,但不相互冲突。

参考http://c2.com/cgi/wiki?CommonReusePrinciple

Hollywood Principle–好莱坞原则

好莱坞原则就是一句话——“don’t call us,we’ll call you.”。意思是,好莱坞的经纪人们不希望你去联系他们,而是他们会在需要的时候来联系你。也就是说,所有的组件都是被动的,所有的组件初始化和调用都 由容器负责。组件处在一个容器当中,由容器负责管理。

简单的来讲,就是由容器控制程序之间的关系,而非传统实现中,由程序代码直接操控。这也就是所谓“控制反转”的概念所在:

  • 1.不创建对象,而是描述创建对象的方式。
  • 2.在代码中,对象与服务没有直接联系,而是容器负责将这些联系在一起。

控制权由应用代码中转到了外部容器,控制权的转移,是所谓反转。

好莱坞原则就是IoC(Inversion of Control)或DI(Dependency Injection)的基础原则。这个原则很像依赖倒置原则,依赖接口,而不是实例,但是这个原则要解决的是怎么把这个实例传入调用类中?你可能把其声明 成成员,你可以通过构造函数,你可以通过函数参数。但是IoC可以让你通过配置文件,一个由Service Container 读取的配置文件来产生实际配置的类。但是程序也有可能变得不易读了,程序的性能也有可能还会下降。

参考
http://en.wikipedia.org/wiki/Hollywood_Principle
http://en.wikipedia.org/wiki/Inversion_of_Control

High Cohesion & Low/Loose coupling & –高内聚,低耦合

这个原则是UNIX操作系统设计的经典原则,把模块间的耦合降到最低,而努力让一个模块做到精益求精。

  • 内聚:一个模块内各个元素彼此结合的紧密程度
  • 耦合:一个软件结构内不同模块之间互连程度的度量

内聚意味着重用和独立,耦合意味着多米诺效应牵一发动全身。

参考
http://en.wikipedia.org/wiki/Coupling_(computer_science)
http://en.wikipedia.org/wiki/Cohesion_(computer_science)

Convention over Configuration(CoC)–惯例优于配置原则

简单点说,就是将一些公认的配置方式和信息作为内部缺省的规则来使用。例如,Hibernate的映射文件,如果约定字段名和类属性一致的话,基本 上就可以不要这个配置文件了。你的应用只需要指定不convention的信息即可,从而减少了大量convention而又不得不花时间和精力啰里啰嗦 的东东。配置文件很多时候相当的影响开发效率。

Rails中很少有配置文件(但不是没有,数据库连接就是一个配置文件),Rails的fans号称期开发效率是java开发的10倍,估计就是这 个原因。Maven也使用了CoC原则,当你执行mvn -compile命令的时候,不需要指源文件放在什么地方,而编译以后的class文件放置在什么地方也没有指定,这就是CoC原则。

参考http://en.wikipedia.org/wiki/Convention_over_Configuration

Separation of Concerns (SoC)–关注点分离

SoC是计算机科学中最重要的努力目标之一。这个原则,就是在软件开发中,通过各种手段,将问题的各个关注点分开。如果一个问题能分解为独立且较小 的问题,就是相对较易解决的。问题太过于复杂,要解决问题需要关注的点太多,而程序员的能力是有限的,不能同时关注于问题的各个方面。正如程序员的记忆力 相对于计算机知识来说那么有限一样,程序员解决问题的能力相对于要解决的问题的复杂性也是一样的非常有限。在我们分析问题的时候,如果我们把所有的东西混 在一起讨论,那么就只会有一个结果——乱。

我记得在上一家公司有一个项目,讨论就讨论了1年多,项目本来不复杂,但是没有使用SoC,全部的东西混为一谈,再加上一堆程序员注入了各种不同的观点和想法,整个项目一下子就失控了。最后,本来一个1年的项目做了3年。

实现关注点分离的方法主要有两种,一种是标准化,另一种是抽象与包装。标准化就是制定一套标准,让使用者都遵守它,将人们的行为统一起来,这样使用 标准的人就不用担心别人会有很多种不同的实现,使自己的程序不能和别人的配合。Java EE就是一个标准的大集合。每个开发者只需要关注于标准本身和他所在做的事情就行了。就像是开发镙丝钉的人只专注于开发镙丝钉就行了,而不用关注镙帽是怎 么生产的,反正镙帽和镙丝钉按标来就一定能合得上。不断地把程序的某些部分抽像差包装起来,也是实现关注点分离的好方法。一旦一个函数被抽像出来并实现 了,那么使用函数的人就不用关心这个函数是如何实现的,同样的,一旦一个类被抽像并实现了,类的使用者也不用再关注于这个类的内部是如何实现的。诸如组 件,分层,面向服务,等等这些概念都是在不同的层次上做抽像和包装,以使得使用者不用关心它的内部实现细节。
说白了还是“高内聚,低耦合”。

参考http://sulong.me/archives/99

Design by Contract (DbC)–契约式设计

DbC的核心思想是对软件系统中的元素之间相互合作以及“责任”与“义务”的比喻。这种比喻从商业活动中“客户”与“供应商”达成“契约”而得来。例如:
供应商必须提供某种产品(责任),并且他有权期望客户已经付款(权利)。
客户必须付款(责任),并且有权得到产品(权利)。

契约双方必须履行那些对所有契约都有效的责任,如法律和规定等。

同样的,如果在程序设计中一个模块提供了某种功能,那么它要:
期望所有调用它的客户模块都保证一定的进入条件:这就是模块的先验条件(客户的义务和供应商的权利,这样它就不用去处理不满足先验条件的情况)。

保证退出时给出特定的属性:这就是模块的后验条件——(供应商的义务,显然也是客户的权利)。

在进入时假定,并在退出时保持一些特定的属性:不变式。

契约就是这些权利和义务的正式形式。我们可以用“三个问题”来总结DbC,并且作为设计者要经常问:

  • 它期望的是什么?
  • 它要保证的是什么?
  • 它要保持的是什么?

根据Bertrand Meyer氏提出的DBC概念的描述,对于类的一个方法,都有一个前提条件以及一个后续条件,前提条件说明方法接受什么样的参数数据等,只有前提条件得到 满足时,这个方法才能被调用;同时后续条件用来说明这个方法完成时的状态,如果一个方法的执行会导致这个方法的后续条件不成立,那么这个方法也不应该正常 返回。

现在把前提条件以及后续条件应用到继承子类中,子类方法应该满足:

  • 1.前提条件不强于基类.
  • 2.后续条件不弱于基类.

换句话说,通过基类的接口调用一个对象时,用户只知道基类前提条件以及后续条件。因此继承类不得要求用户提供比基类方法要求的更强的前提条件,亦 即,继承类方法必须接受任何基类方法能接受的任何条件(参数)。同样,继承类必须顺从基类的所有后续条件,亦即,继承类方法的行为和输出不得违反由基类建 立起来的任何约束,不能让用户对继承类方法的输出感到困惑。

这样,我们就有了基于契约的LSP,基于契约的LSP是LSP的一种强化。

参考http://en.wikipedia.org/wiki/Design_by_contract

Acyclic Dependencies Principle (ADP)–无环依赖原则

包之间的依赖结构必须是一个直接的无环图形,也就是说,在依赖结构中不允许出现环(循环依赖)。如果包的依赖形成了环状结构,怎么样打破这种循环依 赖呢?有2种方法可以打破这种循环依赖关系:第一种方法是创建新的包,如果A、B、C形成环路依赖,那么把这些共同类抽出来放在一个新的包D里。这样就把 C依赖A变成了C依赖D以及A依赖D,从而打破了循环依赖关系。第二种方法是使用DIP(依赖倒置原则)和ISP(接口分隔原则)设计原则。

无环依赖原则(ADP)为我们解决包之间的关系耦合问题。在设计模块时,不能有循环依赖。

参考http://c2.com/cgi/wiki?AcyclicDependenciesPrinciple

上面这些原则可能有些学院派,也可能太为理论,我在这里说的也比较模糊和简单,这里只是给大家一个概貌,如果想要了解更多的东西,大家可以多google一下。

不过这些原则看上去都不难,但是要用好却并不那么容易。要能把这些原则用得好用得精,而不教条,我的经验如下:(我以为这是一个理论到应用的过程)

  • 1.你可以先粗浅或是表面地知道这些原则。
  • 2.但不要急着马上就使用。
  • 3.在工作学习中观察和总结别人或自己的设计。
  • 4.再回过头来了回顾一下这些原则,相信你会有一些自己的心得。
  • 5.有适度地去实践一下。
  • 6.Goto第 3步。

好的程序设计原则

好的编程原则跟好的系统设计原则和技术实施原则有着密切的联系。下面的这些编程原则在过去的这些年里让我成为了一名优秀的程序员,我相信,这些原则对任何一个开发人员来说,都能让他的编程能力大幅度的提高,能让他开发出可维护性更强、缺陷更少的程序。

我不要自我重复 — 这也许是在编程开发这最最基本的一个信条,就是要告诉你不要出现重复的代码。我们很多的编程结构之所以存在,就是为了帮助我们消除重复(例如,循环语句,函数,类,等等)。一旦程序里开始有重复现象的出现(例如很长的表达式、一大堆的语句,但都是为了表达相同的概念),你就需要对代码进行一次新的提炼,抽象。
http://en.wikipedia.org/wiki/Don%27t_repeat_yourself

提炼原则 — 跟“不要自我重复原则”相关,这一原则是说“程序中任何一段具有功能性的代码在源代码文件中应该唯一的存在。”
http://en.wikipedia.org/wiki/Abstraction_principle_(programming)

保持简单 — 简单化(避免复杂)永远都应该是你的头等目标。简单的程序让你写起来容易,产生的bug更少,更容易维护修改。
http://en.wikipedia.org/wiki/KISS_principle

不要开发你目前用不到的功能 — 除非你真正需要用到它,否则不要轻易加上那些乱七八糟用不到的功能。
http://en.wikipedia.org/wiki/YAGNI

用最简单的方法让程序跑起来 — 在开发时有个非常好的问题你需要问问自己,“怎样才能最简单的让程序跑起来?”这能帮助我们在设计时让程序保持简单。
http://c2.com/xp/DoTheSimplestThingThatCouldPossiblyWork.html

不要让我动脑子 — 这实际上是Steve Krug 关于web界面操作的一本书的书名,但也适用于编程。主旨是,程序代码应该让人们花最小的努力就能读懂和理解。如果一段程序对于阅读者来说需要花费太多的努力才能理解,那它很可能需要进一步简化。
http://www.sensible.com/dmmt.html

开放/封闭原则 — 程序里的实体项(类,模块,函数等)应该对扩展行为开放,对修改行为关闭。换句话说,不要写允许别人修改的类,应该写能让人们扩展的类。
http://en.wikipedia.org/wiki/Open_Closed_Principle

为维护者写程序 — 任何值得你编写的程序在将来都是值得你去维护的,也许由你维护,也许由他人。在将来,当你不得不维护这些程序时,你对这些代码的记忆会基本上跟一个陌生人一样,所以,你最好还是当成一直在给别人写程序。一个有助于你记住这个原则的办法是“写程序时时刻记着,这个将来要维护你写的程序的人是一个有严重暴力倾向,并且知道你住在哪里的精神变态者”。
http://c2.com/cgi/wiki?CodeForTheMaintainer

最少意外原则 — 最少意外原则通常是使用在用户界面设计上,但这个原则同样适用于编写程序。程序代码应尽可能的不要让阅读者感到意外。也就是说应该遵循编码规范和常见习惯,按照公认的习惯方式进行组织和命名,不符常规的编程动作应该尽可能的避免。
http://en.wikipedia.org/wiki/Principle_of_least_astonishment

单一职责原则 — 一个代码组件(例如类或函数)应该只执行单一的预设的任务。
http://en.wikipedia.org/wiki/Single_responsibility_principle

最小化耦合关系 — 一个代码片段(代码块,函数,类等)应该最小化它对其它代码的依赖。这个目标通过尽可能少的使用共享变量来实现。“低耦合是一个计算机系统结构合理、设计优秀的标志,把它与高聚合特征联合起来,会对可读性和可维护性等重要目标的实现具有重要的意义。”
http://en.wikipedia.org/wiki/Coupling_(computer_programming)

最大化内聚性 — 具有相似功能的代码应该放在同一个代码组件里。
http://en.wikipedia.org/wiki/Cohesion_(computer_science)

隐藏实现细节 — 隐藏实现细节能最小化你在修改程序组件时产生的对那些使用这个组件的其它程序模块的影响。
http://en.wikipedia.org/wiki/Information_Hiding

笛米特法则(Law of Demeter) — 程序组件应该只跟它的直系亲属有关系(例如继承类,内包含的对象,通过参数入口传入的对象等。)
http://en.wikipedia.org/wiki/Law_of_Demeter

避免过早优化 — 只有当你的程序没有其它问题,只是比你预期的要慢时,你才能去考虑优化工作。只有当其它工作都做完后,你才能考虑优化问题,而且你只应该依据经验做法来优化。“对于小幅度的性能改进都不该考虑,要优化就应该是97%的性能提升:过早优化是一切罪恶的根源”—Donald Knuth。
http://en.wikipedia.org/wiki/Program_optimization

代码复用 — 这不是非常核心的原则,但它跟其它原则一样非常有价值。代码复用能提高程序的可靠性,节省你的开发时间。
http://en.wikipedia.org/wiki/Code_reuse

职责分离 — 不同领域的功能应该由完全不同的代码模块来管理,尽量减少这样的模块之间的重叠。 http://en.wikipedia.org/wiki/Separation_of_concerns

拥抱变化 — 这是Kent Beck的一本书的副标题,它也是极限编程和敏捷开发方法的基本信条之一。很多的其它原则都基于此观念:面对变化,欢迎变化。事实上,一些经典的软件工程原则,例如最小化耦合,就是为了让程序更容易面对变化。不论你是否采用了极限编程方法,这个原则对你的程序开发都有重要意义。http://www.amazon.com/gp/product/0321278658

[英文原文:The Principles of Good Programming ]
这和《代码大全》上的总结基本一致

SQL 增加自动增长id列

方法1:

在表设计器中手动创建列,选择标识,种子,增量。

方法2:

直接建表是创建生成

CREATE TABLE Tab_Test (ID int IDENTITY (1, 1), Name varchar(50))

方法3:

修改以有的表,增加一列

ALTER TABLE _TAB_Epm_Users ADD nID int IDENTITY (1, 1)

方法4:

用存储过程完成, 我没用过。自己查下。

方法5:

其他方法, 也可以变通完成。

比如:在插入数据时可以 先 MAX(nID)  原主键,然后+1后插入。

比如:已有一张表,我想在表里增加一个自动增长列,但是我想添进去的自动增长列按关键字order by排序,有简单易行的方法吗

——解决方案——————–
select NewID = identity(int,1,1), * into NewTable from OldTable order by 旧的数据的编号按关键字排序
——解决方案——————–
用2005的排序函数 row_number()
select row_number() over (order by id), * from sysobjects

C#中的’?号’及用法

1. 可空类型修饰符(?):

  引用类型可以使用空引用表示一个不存在的值,而值类型通常不能表示为空。
  例如:
    string str=null;是正确的。
    int i=null;编译器将报错。
    为了使值类型也可为空,可空类型出现了,可空类型使用可空类型修饰符?来表示,表现形式为T?。
  例:int?表示是可空的整形,DateTime?表示为可空的时间。
  T?其实是System.Nullable<T>(泛型结构)的缩写形式,也就意味着当你用到T?时编译器在编译时会把T?编译成System.Nullable<T>的形式,
  例如:int?,编译后便是System.Nullable<int>的形式。

2. 三元(运算符)表达式(?:):

语法为:条件表达式?表达式1:表达式2;
该操作首先求出条件表达式的值(bool类型),为true时调用表达式1,为flase时调用表达式2。
其逻辑为:"如果为真执行第一个,否则执行第二个。"

 例:
 test ? expression1 : expression2
 test 任何 Boolean 表达式。
 expression1 test 为 true 时返回的表达式。可能是逗点表达式。
 expression2 test 为 false 时返回的表达式。可能是逗点表达式。

 例如:
 string prm1="4"; string prm2="5";
 string prm3 = prm1==prm2?"yes":"no" // 此时prm3值为"no".

3. 空合并运算符(??):

空合并运算符 (null coalescing operator) ??
  用于定义可空类型和引用类型的默认值。如果此运算符的左操作数不为 null,则此运算符将返回左操作数;否则返回右操作数。
  例:a??b 如果 a 为非空,则 a ?? b 的结果为 a;否则结果为 b 。

空合并运算符为右结合运算符,即操作时从右向左进行组合的。
  例:“a??b??c”的形式按“a??(b??c)”计算。