`
zr615zr
  • 浏览: 13533 次
最近访客 更多访客>>
社区版块
存档分类
最新评论

【 零 】 Java 随 笔 ( 2 月 9 日 更 新 ---- 贪 吃 蛇 雏 形 )

 
阅读更多

【 零 】 Java 随 笔 ( 2 月 9 日 更 新 ---- 贪 吃 蛇 雏 形 )
2009年08月18日
          07年的7,8月份从这个论坛开始了我的Java学习之路!历时一年多了,自我感觉良好!在之后的半年时间里,想做一个全面的知识的巩固!所以开此帖,算作是对自己的一个总结,也希望对初学者有些帮助!
  首先先说几个问题,算作预备篇吧!
  1.论坛里有不少帖子是关于如何快速学好Java的。
  相信大部分的回帖都是多动手,多啃书。学什么都没有捷径的。不过也不是绝对的,你得知道你学Java的目的是什么?J2ee,J2me还是桌面?(目前已经有一些Java桌 面,游戏相关的公司了)。如果你的目的是J2ee,我想就没有必要死磕Swing,多线程了吧?当然如果你有时间还是可以钻研的。不过也不是一点不看,Swing里面有对匿名内部类的使用,可以巩固你前面的知识。还有监听器的使用,对你以后理解观察者模式会有帮助。所以我的建议是,这两部分你只要能看懂就行了,不需要会写多复杂的程序。
  你可以以一种循环渐进的方式学习,第一次以很快的速度浏览Java的基础语法,像面向对象的概念,类,对象以及关键字等这些基本的语法。对这些语法有一个大体的了解。这个时期你可以不需要书籍,或者一本很薄的基本书籍,多上网找资料(下面说。)。在有了一定的了解后,开始找一本专门的书籍阅读。可以是Thinking in Java,或者Agile Java这类书籍。这一时期可能是最最痛苦的时期。我想很少有没有扎实的语言基础的人能够轻松阅读完Thinking in Java的。这一关过了,你就可以算是掌握了J2se了。这里特别说一下Agile Java,这也是一本讲Java基础的书籍,不过它很特别。从一开始就给你灌注TDD思想。可能有利有弊,弊端就是你一开始就要学两个思想,面向对象和TDD的开发方式。利,就是,从一开始你就能养成一个很好的开发习惯。我最近入手了Agile Java,就是要学习TDD开发方式。
  再后面,你应该有了自己的学习习惯了。肯定不会再问怎么学习这,怎么学习那的问题了。
  2.不知道现在还有没有人使用JDK1.4。
  我刚学习的时候,就是从JKD1.5开始的。当时就看见不少人还在使用1.4,我是很奇怪,为什么不用新版本?1.4和1.5的差别还是比较大的。
  我自己的电脑,反正只要一有新的稳定版本就会安装上去的。我个人是推荐不要使用太老的JDK,新的JDK肯定有变化,既然你在学习,为什么还要学习老的呢?
  3.关于开发工具
  相信每个初学者都问过或想过这个问题,我该使用什么开发工具?我当时也问过。对于刚开始,没有第二个选择,我是建议,cmd+文本编辑工具。除了让你熟悉命令外,还要多注意cmd里面输出的内容,多留意当出现错误的时候,具体的错误内容,能够根据错误信息找到错误的地方,并修改。我很疑惑的是,J2ee论坛里面还有不少人帖一个空指针异常,然后问是什么错误。。。。
  在你学习玩Thinking in Java的前一部分知识之前,建议还是多悄悄命令行。你认为你熟悉了以后,可以再使用IDE。具体使用什么IDE,看自己的喜好,别人用的推荐的,并不一定适合你。最好你把各个主流的IDE都试一遍,找到最适合自己的。目前主流的应该就是eclipse,netbeans,Intellij IDEA以及Myeclipse.我想用的比较多的是myeclipse.虽说是eclipse的一个插件,其实已经可以单独出来成一个开发工具了。你都去试一试,看看自己哪个用着最舒服就用哪个。我最后选择的是Intellij IDEA。当然了,IDE说到底都只是开发工具而已。不能依赖工具,你得主导工具才行。别没了工具你什么都搞不了。
  4.学会Google和论坛的使用
  不要一有问题,就上网问人,多Google,你会理解得更好。论坛也不是你有问题就来求解的地方。论坛论坛,是讨论的地方。我自己很少在论坛提问,我上论坛一般都是看别人的帖子,看看自己能否解答,不能的再自己Google下,看看能不能找到结果。
  先预备这么多吧!知识的学习和过滤
  知识学得越多,当然没有坏处。但也要看你有没有时间和精力去学习!如果你时间很充裕,没有问题。随便你怎么钻研。但是,有时候懂得放弃,也是一种提高学习效率的方法。相信在高中或初中时代,老师们都教过大家,考试的时候如果遇到不会的题目,可以先跳过去,等做完了再去思考。这里也是同样的道理,J2EE主要是数据库方面以及web方面的知识。一个Swing或applet程序不会写了,就在那里绞尽脑汁。何必呢?说实话,写Swing程序的确很有成就感!我也是喜欢桌面多于J2ee。但是说句很俗的话,money和兴趣之间如果只能选一样,你会选择哪个?既然你喜欢,可以抽空研究。没必要死磕。还严重的影响了自己的学习热情。
  提到了Applet,感觉目前学校的知识很过时,很难想像现在学校还在教Applet,早被Sun遗弃的东东了。大家常上网的,肯定或多或少的听到大学无用论的风声。我是中立态度,有用没用,反正都上了,而且大部分的原因还是在自己。不过有一个可以肯定的,大学的知识和社会有脱节现象。可能这是导致大学无用论的原因。还有授课方式,我精神抖擞的去上课,听了5分钟就开始打瞌睡,我有原因,老师对着课本读就没有原因吗?我学习Java完全是自学的。学校里学的工科!所以对学校里面的Java教学情况不了解。但是根据我的大学生涯以及论坛所见。相信和我的猜想不会出入太多。至少学校里面都会学习数据结构,算法之类的。然后可能是C语言,接着Java,还有软件工程。对于一些Java专业的,可能还会学习SSH。对这些课程本没有什么可异议的。但是我认为学校的一概而论的教学方式并不可取。
  就数据结构而言,Java对数据结构的依赖很小很小。昨天没事和头闲聊,谈论当年头的开发环境。从打孔,到16k内存,到9寸软盘,到10m硬盘。如果那个时候没有数据结构,没办法节省哪怕1k的空间,程序都没法跑起来。这些概念相信对大家都是不可想象的。16k内存现在能做什么?可以说什么都做不了!这就是数据结构存在的意义。也不是说数据结构现在就一点用都没有,对于写出良好的程序还是有帮助的。但是,数据结构的确是一门很枯燥很乏味的课程。如果你感觉它很有意思,那说明你已经基本掌握了它,否则你肯定会觉得很郁闷。这个感觉是能弄懂多少就弄懂多少,都弄懂了最好。
  对于C和Java,都属于C系列的语言,都是语言而已,没什么好坏。如果学校里面还在讲这jdk1.4,applet,那真的是很无语的。对于Java里面的知识点,哪些重点学,哪些了解的,这时候可以参考Thinking in Java。Thinking in Java第三版我基本看完了,第四版入手的书籍,没怎么看
  
  , 第四版和第三版差距很大,加入了很多的内容,该重点讲的重点讲了,该略讲的也略讲了。开始看不懂也可以作为一个知识点的参考。
  软件工程我是完全没有概念,因为完全没接触过。
  SSH,很古老,很流行的名词。学个Java的人,在简历里面基本都会写熟悉或精通SSH。什么叫熟悉精通?学习这三个框架不是为了熟练运用,而是要学习它的思想,为以后能迅速的学习其他的框架。Java领域框架实在太多了。但是机制其实都大同小异。Struts基于MVC模式,Webwork,Struts2,Spring MVC也同样是基于MVC的,你能迅速的掌握并应用到项目里面吗?对于MVC模式又有多少的理解?熟悉了jsp+servlet的MVC模式,转到Struts上会有多大的难度呢?再说Spring,如果你基础够扎实,对设计模式有一点的了解。IOC,AOP有那么复杂吗?Hibernate说白了不就是读读配置文件吗?Hibernate的难点应该是在优化上!
  对这些内容的过滤就是多google,哪些是流行,哪些是重点,Google都能告诉你,当然对Google的内容自己也需要过滤。像对于目前流行的内容,学校没有教!自己感兴趣或者认为将来可能用到的可以看看,同样没必要死命的钻。
  真正工作了你会发现,学习的东西太多了。需要学什么,完全看公司用什么。像我一直没用SSH,因为公司不用。抽空自己学习的SSH。
  所以说到底还是一句废话,基础很重要!需要学习的知识点及学习程度
  以Thinking in Java第四版为参考。
  第一章:对象导论
  对Java的一个整体的讲述,可以跳过。
  第二到第十三章除去第11章 + 第16章
  Java的基础,非常重要。这一段内容如果你学习得很扎实,那么之后的学习就会比较轻松了。这些内容都是很基础的东西。你都得搞熟练了。
  里面的讲解很到位,像static,final关键字什么的都有比较详细的讲解。理解了这些,对单例模式为什么会这么写,简直就是理所当然的。
  第11章以及后面的第17章是讲解集合类的
  集合类可以说是Java常用类里面最重要的类了。里面的内容你也是需要熟练的掌握,大致有哪些方法你都应该熟悉。可以说没有哪个项目里面能离开集合类的。
  第14章  RTTI及反射
  反射也是非常重要的一部分。不过开始学习的时候可以不用深入这个,到你学习框架的时候再深入学习这一部分。到时侯你会发现,框架里面大量的使用了反射。
  第15章  泛型
  泛型,同第14章,开始的时候你只需要知道泛型在集合类里面的应用。以后有时间了可以学习泛型类。
  第18章  IO
  这是仅此于集合类的,很重要的一个Java类库。你也需要尽量的学好。
  第19和20章  枚举和注解
  这两章以及泛型是JDK5的新特性。较之泛型可能应用比较少一点。枚举你需要熟悉,在一些应用上还是很有帮助的。对于注解,你要知道如何的使用注解。因为现在大部分的框架都支持注解了。至于自己写注解,看你兴趣吧。
  第21章  多线程
  Thinking in Java里面花了150多页讲解多线程,开头也说了,即使你学完了这一章你也不能算是精通多线程。可见多线程这潭水有多深了。这一章你至少要知道多线程的概念,能看懂以及会写基本的多线程的程序。
  第22章  Swing
  Swing,Thinking in Java对Swing部分讲解很范,可见Swing在J2ee的比重了。在这一章里面复习内部类,学习监听器即可。
  Thinking in Java里面没有提到两个东西,一个是xml文件的读写,一个是JDBC。这两个东西也是J2ee里面比较重要的。XML文件在框架的配置文件里面大行其道。没有JDBC,你就没办法连接数据库。这两个内容也需要熟练掌握。关于Ant和Maven
  在你学习的过程中,或是听说,或是网上看到,或是书上提到。你应该会看到Ant和Maven这两个字眼。相对而言还是Ant出现的频率比较大一点。怎么也是出现得比较早的一个构建工具了。你肯定会看到他们说Ant,Maven怎么怎么的好,怎么怎么的方便。于是冲动一下,便把这东东down下来试试,满怀激动的心情来试用这两个工具。不过,我想大部分人都会以失望告终。为什么?Ant,Maven提供的功能,可以说就是主流IDE所提供的功能。相对于IDE友好的界面,你会选择命令行式的Ant以及Maven吗?大部分人应该不会吧?
  那Ant和Maven到底优势在哪里?如果你看过开源项目的话,会发现开源项目不是用Ant构建的就是用Maven构建的。开源项目会选择这个,肯定是因为对开发有利才使用的。不可能使用一个影响开发效率的工具吧?我对Ant和Maven2(Maven有Maven1和Maven2版本,Maven2版本较新,且比Maven1简化很多,替代Maven1的)都使用过,我也是不喜欢用啦,所以没有深入。只说说自己的看法。
  首先,Ant和Maven都是和平台无关的.Ant不会建立自己的项目目录结构,完全由自己编写build.xml来操作。Maven2会提供一套公认比较好的目录结构给你,当然了,你也可以自己定制。这样就带来了一个优点,可以跨IDE。假如你用eclipse新建了一个项目,那目录结构就是eclipse的目录结构,如果你想要用netbeans来打开,那么对不起,你需要做不少额外的活,来让它在netbeans下面跑起来,反之亦然。而如果你使用Ant或Maven来创建项目,只要IDE上装了相应的插件(主流IDE都提供了Ant和Maven的很好的插件),项目可以用任意的IDE打开,且立刻运行。
  接着,其实这个和第一点类似,如果你在协同开发的话,如果使用IDE,那么需要使用相同的IDE才能协同开发(也不是说不能使用不同的IDE,见上面,两个不同的IDE间项目的导入需要额外的设置才行),当然了,如果你们都喜欢同一个IDE那就好办了,如果你们喜欢的IDE不同的,他喜欢eclipse,你喜欢netbeans,那就需要一个人妥协了。在讨厌的环境下开发,心情肯定不好吧?使用Ant或Maven就可以解决这个矛盾,装上插件,你爱用什么IDE就用什么IDE。
  还有,就是关于规范开发的问题。我好像没在IDE里面找到生成Junit测试报告的选项。Ant和Maven都可以生成测试报告。当然了,你可能就没有按照规范开发的模式来开发。我至今做了两个项目,都没有按规范开发来
  
  ,新年开始,按规范来。 (关于规范开发和敏捷开发下次说。)
  关于学习成本,Ant学习成本其实很低,基本可以说就是边学边写,学完就写完build.xml文件。Maven学习成本比ant高一点,上手可能要半天,熟练使用就需要多摸摸了。这两个东东你可以看,可以不看。在项目组里面如果要使用这两个东东,只需要一个人熟练掌握Ant或Maven就行了。其他人只要用就可以了。如果你想做那个人,你就学吧,成本也不大。如果不是,那么可以无视之。。。。       规范开发,敏捷开发(TDD)
  看过一定量的书后,规范开发这个词肯定就烙在了你的心里。因为书里面或多或少的都提到了规范开发,像类名首写字母要大写,驼峰命名法之类的。这些只是规范开发里面最最基本的。你应该都做到了。这只能算是规范代码。这只是规范开发里面的一部分。
  你写测试了吗?你有文档吗(初学应该都不写文档的,写文档的可能不是初学的,不是初学的也不一定写文档。。。。)?文档先不提,这个到工作了肯定要写的,至于怎么写,看公司的规章制度。感觉国内小公司对文档的要求都不是很严格,很多情况都是开发完了补文档。
  对于测试,我建议还是学习写的好,一个程序员需要对自己所写的代码负责任。现在的IDE都提供了JUnit很不错的支持,写测试也不是那么难的。可能开始会认为,写测试是浪费时间,多写了那么多的代码。所以这里推荐TDD开发模式(我也正在摸索中,但是我现在已经发觉了TDD的确对代码的编写很有帮助。)
  敏捷开发现在也是很火热,像ROR,GOG这类一栈式开发框架目前很受推崇,为什么?效率第一,如果你能快速的拿出一个稳定的版本,不但公司对你有好印象,公司也能提前打入市场。而敏捷开发里面就推荐TDD开发方式。TDD---Test Drive Development,测试驱动开发(这种开发模式不仅针对Java的,对ROR,GOG同样的适用,你只要看看相应的文章你就了解了)。TDD是经过实践的,和设计模式一样。不过需要你思想的改变。就像你学习面向对象思想那样,从过程化转变为对象化。
  对于下面的需求,想想你是怎么开发的。
  一人开着一辆小轿车。左转,然后刹车(我随便写的,这是驾驶的过程)。
  如果根据面向对象的思想,可以分析,上面至少要有两个对象,一个是人Person,一个是车Car,人驾驶车,先左转再刹车,说明了车有两个方法,一个是左转turnLeft,一个是刹车stop.
  如果用TDD的方式呢,其实是在上面加了一层,或者是反过来想。一人开着一辆小轿车,那我需要两个测试类,一个是PersonTest,一个是CarTest,由于Person能驾驶车,所以我需要一个testDrive方法测试drive是否正确,而Car能左转和刹车,所以我需要testTurnLeft和testStop方法测试对应的方法是否正确。
  看下具体的流程,看看你是否适应这种开发方式,我现在开始喜欢这样了,感觉能给你一个很清晰的思路来开发和设计。
  首先编写一个CarTest类,类里面添加testTurnLeft和testStop方法,编写相应的测试代码,测试失败。必然的,因为你没有Car这个类,所以就添加Car类,并编写相应的代码,写完继续测试,测试失败继续改,直至测试绿条(成功)!
  接着编写一个PersonTest类,在类里面编写testDrive方法,接着new Person,new Car,person.driver(car)。测试,会出现错误,这是肯定的,因为你没有Person类,所以这时候就新增Person类,在Person类里面添加相应的drive方法,再重复上面的工作。
  乍看,感觉很麻烦,但是这种循序渐进的方式会给你一个很明了的思路和不断重构的建议,我认为这种开发能更好更快的理解设计模式。
  这种方式建议你试试看,不要因为看似麻烦而拒绝。想想你开发结束后,如果需要修改将是很简单的事情,因为你的测试完备,修改后,只需要运行测试,通过就说明修改成功,即使测试失败你也能根据测试快速的定位到错误上去。
  这么说,大家可能没概念,建议阅读Agile Java,这也是一本讲授J2se的书籍,但是完全是以TDD的方式讲解的,一开始就带你进入规范开发。无论你是否讨厌这种方式,你先试一试,以TDD方式编写你以前写过的代码,看你是不是突然有一种有很清晰的思路的感觉?开发流程
  这里算是对之前的一些内容的总结吧!使用TDD方式开发一个袋集合!顺便分析下开发的流程!
  需求:实现一个袋集合,非线性集合。有添加元素,添加所有元素,随机删除元素,删除元素,合并元素,确定是否包含特定元素,能判断是否为空,能获取元素个数的功能。
  需求分析:从需求可以看出,这里就是需要一个类,包含了增删元素等等方法,并且类里面的各个元素之间又可以说是没有任何关系的,就像实际的袋子一样,放进去以后,如果要取出来,各个元素之间的几率是相等的。
  设计:我们可以设计一个类,里面肯定要有个容器来存放这些元素,这里采用数组,取名叫ArrayBag吧。再提供相应的方法add,addAll,removeRandom,remove,union,contains,isEmpty,size,equals,toString(最后两个方法是必要的吧)
  详细设计:上面的设计就已经够了,需求很简单。
  那么开始着手编码。首先来写add方法,add方法是给ArrayBag类里面添加一个元素,那么怎么测试这个方法呢?既然添加了元素,那么size就肯定不是0了吧?其实从size方法开始比较好,先创建一个TestBag类,开始测试size方法,代码如下。 
  
  程序代码:
  import org.junit.*;
  import static org.junit.Assert.*;
  public class TestArrayBag {
  ArrayBag bag;
  @Before
  public void init(){
  bag = new ArrayBag();
  }
  @Test
  public void testSize(){
  assertEquals(0,bag.size());
  }
  }这里采用的是junit4.4来测试的,如果使用的junit3,写法可以参考里面的cookbook。
  这里判断bag.size()是否为0.测试必然失败,因为你没有ArrayBag类以及size方法。如果使用的eclipse,鼠标点击到错误上,按ctrl+1,如果是netbeans或者intellij idea按alt+enter,都会有提示的操作的。这里就是创建ArrayBag类以及一个size方法。 
  
  程序代码:
  public class ArrayBag {
  public int size() {
  return 0;
  }
  }这下测试通过了吧?你发现怎么会返回0呢?别急,继续增量开发。测试add方法。
  
  
  程序代码:
  @Test
  public void testAdd(){
  bag.add("1");
  assertEquals(1,bag.size());
  }代码不全部再写一遍了,就两个类,测试的代码都放测试类里面,其他放ArrayBag类里面就是咯!这里给bag添加一个元素,然后测试bag的size是否为1,虽然你肯定知道不是,还是继续。给bag添加add方法,因为使用的是数组来存储元素的,你会想到需要ArrayBag里面有个数组的属性。另外,要加的元素加到哪里?肯定是数组的末尾了,怎么知道数组的末尾呢?使用一个属性来存储就可以了,而size方法就可以直接返回这个属性就行了,修改ArrayBag代码如下
  
  
  程序代码:
  public class ArrayBag {
  private Object[] objs = new Object[100];
  private int index = 0;
  public void add(Object obj){
  objs[index] = obj;
  index++;
  }
  public int size() {
  return index;
  }
  }测试通过了吧?
  其他的方法都很类似的,我想没必要写了吧?你自己可以试试写写看!会有不同的体验的
  
  
  TDD的一个特点,我感觉到的就是,它是从需求出发的,像需求里面有添加元素的功能,怎么添加?add(),添加后会出现什么效果?size会+1!并且它会保证你的代码会很符合需求,因为你测试和你的需求是相同的。
  PS:放假咯,年后继续
  
  关于开源
  
关于开源
  学完J2SE基础,就可以看看开源项目了。其实从开始学习Java就已经在和开源打交道了,用的eclipse和netbeans都是开源项目,只不过你没有看它的源码而已。有兴趣的可以到它的官方网站上下载源码来读读看,你能很顺畅的读下来,你的J2SE水平已经很不错了。
  对于开源,可能刚开始接触的时候有些误解!开源!=免费!开源开源,就是给你源代码。乍看之下,都开源了,还怎么收费?有不少开源还是需要收费的,大部分的收入来源都是来自文档以及培训之类的,不像商业软件是主卖产品!开源里面还有很多的开源协议,有完全免费的,还有就是像ExtJS比较鸡肋的协议,如果你使用Extjs不是为了商业用途,那么对你是完全免费的,但是如果是商业用途,那么就需要收费了。开源之所以能流行,主要是因为自己可以修改源代码,来满足自己的需求,高手们也能自由的加入到开发中去,完善项目等等。如果你对开源感兴趣,你可以到开源网站上找找你感兴趣的项目去参与(当然你得有能力了!英语也要过关才行!)。
  这里介绍一个相对简单点的开源项目----JEdit,官方网站是www.jedit.org,可以从那里找到软件的下载,以及源码的下载。这里下载源码。你可以先安装一个jedit,看看什么样,功能相对eclipse和netbeans肯定简单很多,它是定位在programe editor的,所以和这些IDE是有差距的,但是它是可完全定制的,可以换肤,可以添加和卸载插件,支持很多种的语言,另外值得说的一点,jedit的速度很不错,好像网络上有人就速度上夸过jedit,大意好像是,只要你做的够多,Swing也是可以很快的。
  对于开源,最重要的莫过于文档了,JEdit是完全开源的,所以对应的文档都很全,包括api,user guide等。
  下载好了源码,你应该就遇到问题了,因为你解压后会发现目录结构和eclipse和netbeans项目的目录结构完全的不一样,而同时你会发现一个build.xml文件。这个项目是用ant构建的,如果你之前看过ant,那么部署就很简单了,用IDE加载这个项目,然后使用ant来运行,不需要任何的其他多余动作。这也印证了我之前说的ant的优点,和IDE无关性。而如果你没有使用ant,那么就需要一些步骤来调整以适应你的IDE了。说下需要的目录,按照自己的IDE来调整就行了。
  首先还是导入项目,其中源码目录是两个,org目录和com,只需要编译这两个目录下的类就可以了,还有需要将modes目录拷贝到编译目录里面还有根目录下的jedit.props文件也需要拷贝过去。这样,就可以运行了。(这个开源项目的部署还是相对比较简单的,我最郁闷的是部署jdom的论坛,部署了n次,硬是没完全部署好,郁闷得要死,最终放弃了。)
  开源文档可以在你安装的程序里面找到,或者从官网上下载。其中有个user guider文档,从jedit的使用一直到为jedit写插件,都有说到,不过是英文的,需要你的一些耐心来阅读,大部分的开源文档都是英文的,而且一般的最新技术文档也都是英文的,所以你的口语可以不行,阅读能力还是需要一点的。
  阅读文档,能够方便我们阅读源码。举个例子,在文档里有How Plugins are Loaded这一节,如果你看了这一节,你就会了解到plugin的加载顺序。设计模式----单例
  设计模式个人认为不是看书学出来的,要体会设计模式需要不少的代码量,不断的重构。借用鲁迅先生的话,”代码里本没有设计模式,重构的多了,也就有设计模式了“。
  单例模式应该算是设计模式里面代码量最小的一个设计模式了,就一个类。但是其实要单例,还是很复杂的,这是我以前写的blog(本来当初想把23种设计模式都记录下来的,谁知道零头都没搞定
  
   ,新年开始努力补全
  
   )
  http://ivan-pig.javaeye.com/blog/148829
  主要看看回复,你就会知道其实单例很复杂,牵扯到的东西很多且都比较底层。
  这里以TDD方式来创建单例,不深入,主要是体会设计模式。
  单例,顾名思义,就是只产生一个实例。那么我们开始从测试写起。 
  
  程序代码:
  import org.junit.*;
  import static org.junit.Assert.*;
  public class SingletonTest {
  @Test
  public void testSingleton(){
  Singleton s1 = new Singleton();
  Singleton s2 = new Singleton();
  assertTrue(s1 == s2);
  }这里创建了两个Singleton实例,然后比较这两个实例是否是同一个实例(==和equals的区别应该都知道吧。)。当然了,错误一堆是肯定的,就这个测试开始创建Singleton类。
  
  
  程序代码:
  public class Singleton {
  }没有错误了。开始测试,肯定是失败的!new了两个实例,会相等才奇怪。从这里就可以看出,new是肯定行不通的,所以不能够让用户来new对象,怎样才能不让用户new对象?构造方法私有化。重构Singleton。
  
  
  程序代码:
  public class Singleton {
  private Singleton(){}
  }立刻的,测试类就报错了,无法new对象了。那怎么才能获得实例呢?只能使用Singleton自己的方法了。这里使用newInstance()方法,重构test类。
  
  
  程序代码:
  import org.junit.*;
  import static org.junit.Assert.*;
  public class SingletonTest {
  @Test
  public void testSingleton(){
  Singleton s1 = Singleton.newInstance();
  Singleton s2 = Singleton.newInstance();
  assertTrue(s1 == s2);
  }
  }针对测试,重构Singleton。如果使用的是IDE,重构是很方便的,eclipse按ctrl+1,netbeans和intellij idea按alt+enter就会有提示,再一个回车就搞定了。
  
  
  程序代码:
  public class Singleton {
  private Singleton(){}
  public static Singleton newInstance() {
  return new Singleton();
  }
  }这里依然是new了一个Singleton返回,因为创建实例的唯一方法就是new了。
  测试,还是报错。原因很明显,目前为止,只不过是把new放到了Singleton类内部而已,实际效果和第一版本没有任何区别。
  怎样才能让Singleton只产生一个实例,这里就是对static关键字的理解是否透彻了。很明显,需要一个static的变量来存储对象,重构Singleton。 
  
  程序代码:
  public class Singleton {
  static Singleton singleton;
  private Singleton(){}
  public static Singleton newInstance() {
  return singleton;
  }
  }在哪里实例化呢?从这个问题,就出现了懒汉式和饿汉式两种单例了!
  懒汉式 
  
  程序代码:
  public class Singleton {
  static Singleton singleton;
  private Singleton(){}
  public static Singleton newInstance() {
  if(singleton!=null){
  singleton = new Singleton();
  }
  return singleton;
  }
  }饿汉式
  
  
  程序代码:
  public class Singleton {
  static Singleton singleton = new Singleton();;
  private Singleton(){}
  public static Singleton newInstance() {
  return singleton;
  }
  }测试看看,是否看见了久违的绿条?
  这样一步步的重构得出的设计模式,是否比你从书上读到的映像更深刻呢?TDD,以一小步一小步的方式不断的重构,还保证了你代码的正确性。以及设计模式的自然得到
  
  
  比设计模式更重要----设计原则
  学习设计模式的时候可能会看到这句话,设计模式对于面向对象开发相当于数据结构对于面向过程开发那么重要。不过,有一个比设计模式更重要的东西,那就是设计原则。对于设计模式,设计原则可能提得比较少,不过是很重要的东东。
  下面是DavidHooker提出的7个软件开发原则:
  1.第一原则:存在的理由(Pattern: TheReason)
  一个软件系统存在的理由就是:为它的用户提供价值。你所有的决定都取决于这一点。在指定一个系统需求,在写下一段系统功能,在决定硬件平台和开发过程之前,问你自己一个问题,“这样做会为系统增加价值吗?“,如果答案是”yes”,做。如果是”No”,不做。这个原则是其他原则的原则。
  2.第二原则(能简单就简单,愚蠢!)KISS (Pattern: KeepItSimple)
  软件设计不是一个轻描淡写的过程。在做任何一个设计时,你必须考虑很多因素。所有设计应当尽可能简单,但是不要再比这简单了。这样产生的系统才是可以理解和容易维护的。这并不是说很多由意义的特性,因为这种简单性也要被抛弃。确实很多更优雅的设计往往更简单,但简单并不意味着“quick and dirty."。事实上,简单是通过许多思考和一次一次的反复修改才达到的。这些努力的汇报就是更容易维护,代码错误更少。 (看看是否违反)
  3.第三原则 :保持远见(Pattern: MaintainTheVision)
  清晰的远见是一个软件项目成功的基础。. 没有这样的远见,项目开发最后就变成天天为一个不好的设计做补丁。Brooks说过:
  概念的完整性是系统设计中最重要的问题。
  Stroustrup 也说:
  有一个干净的内部结构识构建一个可理解、可辨识、可维护
  、可测试系统的基础。
  Booch则总结道:
  只有当你对系统的体系由一个清晰的感觉,才可能去发现通用的抽象和机制。开发这种通用性最终导致系统更简单,因此更小,更可靠
  如果你不断地复制、粘贴、修改代码,最终你将陷入一个大泥潭(the Big Mud),你永远不可能对系统有一个清晰的认识。
  4.第四原则:你制造的,别人会消费 (Pattern: WhatYouProduceTheyConsume)
  软件系统不是在真空中使用的。其他人会使用、维护、文档你的系统。这依赖于对你系统的理解。所以,你设计、实现的东西应当能够让别人理解。要记住,你写的代码并非只给计算机看,你要时时记住,代码还要给人看。(Kent Beck)
  如果到处泛滥似是而非的代码,别人如何能够辨别这些代码的相似和不同,如何去理解这些代码之间具有何种关系。
  5.第五原则:对将来开放( Pattern BuildForTodayDesignForTomorrow)
  一个成功的软件有很长的生命期。你必须能够使得软件能够适应这样和那样的变化。所以,一开始就不要软件设计到死角上去。请总是问一下自己“如果这样,那么。。?“这个问题,你要考虑到各种各样的可能性,而不光光是图省事。复制,粘贴一下即可。
  6.第六原则:为重用做好计划
  软件模式是重用计划的一种。不断重复的代码显然不是这样的计划。
  (See CommentsOnSix)
  7.第七原则:思考!
  在采取任何动作之前首先做一个清晰、完整的考虑,这样才能产生更好的结果。如果你考虑了,但还是产生错误的结果,那么这种努力也是值得的。在你学习或研究类似的问题时,更容易理解和掌握。设计原则续
  对于上面所提的设计原则,有比较规范的定义,可能或多或少都听说过。
  1.单一职责原则:就一个类而言,应该只有一个引起它变化的原因。
  举个例子,如果开发一个计算器小程序,开始肯定有不少人一个类就搞定了。这个类就违反了单一职责原则,因为这个类不但有计算逻辑,还用来显示了。所以除了计算外,显示也会引起这个类的变化。如果现在想改成控制台程序呢?容易改吗?
  2.开-闭原则:软件实体(类,方法等),应该可以扩展,但不能修改。
  这句话应该比较熟悉,“对修改关闭,对扩展开放”。具体是什么意思呢?还是上面的计算器,如果还是那个类,想改成控制台程序。那么就需要修改类了,这就违背开闭原则了。理想状态是添加一个新的功能(这里就是控制台输出),替换掉可视化即可,稍微的修改是可以容忍的或者是必须的。
  在说一个情况,如果你写了一个程序,给另一个人用,没办法扩展,于是那个人就修改你的代码,然后过了几天你想到一个新功能,想拿回来修改,发现面目全非了,你什么感受。倒过来,别人给你的代码你只能修改他的代码才能用,你又是什么感受?
  3.依赖倒转原则:
  (1)高层模块不应该依赖底层模块。两个都应该依赖抽象。
  (2)抽象不应该依赖细节。细节应该依赖抽象。
  (2)就类似于学OO时长听到的一句话,面向接口编程。其实(1)也是这个意思,还是上面那个计算器,假设你和另一个人协作开发,一人写前台,一人写计算逻辑。该怎么协作呢?你写算法逻辑,还要那人等你写完了再写前台吗?这时可以定义一套接口,你实现接口,而他调用接口就可以了,而不必管你实现了没有。
  4.里氏替换原则:子类应该能替换掉它的父类。
  乍看之下,这好像是理所当然的。举个例子就明白了。
  例如,你有一个Bird类,如下所示。 
  
  程序代码:
  public class Bird{
  public void fly(){
  System.out.println("飞翔");
  }
  public void sound(){
  System.out.println("鸣叫");
  }
  }然后呢,你有一个燕子类继承了Bird类
  
  
  程序代码:
  public class Swallow extends Bird{
  ........
  }接着呢!调用
  
  
  程序代码:
  public class Test{
  public static void main(String [] args){
  Bird bird = new Swallow();
  bird.fly();
  bird.sound();
  }
  }这个代码很明了吧?如果现在有个鸽子类,需要怎么样呢?Bird类不需要改,Swallow也不需要改,这就对修改关闭了,添加一个鸽子类,继承Bird类,然后在main方法里面将new Swallow()替换掉即可。这就是对扩展开放了。而如果这时候,你需要添加一个企鹅类,你还能这么添加吗?肯定就不能了。为什么呢?因为企鹅虽然是鸟类,但是不能继承Bird类,因为企鹅不会fly()。既然不会fly,那么在Test类里面,它就不能替换掉Bird类了,如果替换掉了,那么fly()就没有了。这个就是里氏替换原则。
  5.迪米特法则:如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果一个类需要调用另一个类的某个方法,可以通过第三者转发这个调用。
  这个原则简单来说就是类要强内聚松耦合。类与类之间耦合越松,越容易复用。其实这个原则和依赖倒转原则很类似。
  知道了这些原则,写程序的时候,多思考思考,不断的向这个方向重构,那么设计模式就自然而然的出现在你眼前了
  
  贪吃蛇雏形
  
贪吃蛇雏形
  以一个贪吃蛇游戏,来依据设计原则不断的重构!
  贪吃蛇文档
  需求1:
  开发贪吃蛇游戏。
  开始时,蛇在左上方,停止。按下上下左右任意键,开始移动。使用上下左右键来控制蛇的方向,随机的出现食物,蛇吃到食物后身体变长。蛇不能碰到墙壁也不能碰到自己,碰到墙壁或自己,游戏结束。
  第一版设计:
  一个Snake类,有turnUp,turnRight,turnLeft,turnDown,startRun,eatFood,dead方法。
  一个SnakeWin类,用来做界面显示。
  一个Food类,有create和isEated方法。
  一个GameMain类,游戏的启动类。
  对于上面的需求,首先GameMain类创建一个SnakeWin,并显示出来,在SnakeWin左上方画出一条蛇以及一个食物。按下上下左右任意键,调用Snake的startRun方法,当吃到食物时调用eatFood方法,且调用Food的create方法,再创建一个食物.如果吃到自己或者撞墙,调用dead方法,Game Over!
  开发:
  Food类相对简单,所以,先来开发Food,Food怎么写呢?Food可以直接当成界面上画的一个小矩形而已。想想Swing里面,矩形怎么画的?长度,宽度,x和y轴就可以了。那长度和宽度肯定是定的,需求是随机生成Food,那么x,y就是随机数了,这样就很好设计了吧!很明显,Food就是个POJO!那么就不需要测试类了。代码如下。
  public class Food {
  private int width = 20;
  private int height = 20;
  private int x;
  private int y;
  public int getWidth() {
  return width;
  }
  public void setWidth(int width) {
  this.width = width;
  }
  public int getHeight() {
  return height;
  }
  public void setHeight(int height) {
  this.height = height;
  }
  public int getX() {
  return x;
  }
  public void setX(int x) {
  this.x = x;
  }
  public int getY() {
  return y;
  }
  public void setY(int y) {
  this.y = y;
  }
  }
  动画效果的实现其实就是不断的擦出和重画,Snake的移动就是不断的擦出末尾,添加头部。可以想到,用一个List来表示Snake,所以Snake里面就有一个List属性,表示蛇身。eatFood不过就是List里面添加一个元素罢了。
  首先编写Snake的测试类。SnakeTest.,知道了Snake里面有个List,先测试eatFood方法。代码如下。
  @Test
  public void testEatFood() {
  snake.eatFood(food);
  assertEquals(5,snake.getBody());
  }
  snake吃food,判断长度是否加长了。初始List设为4.开始编写Snake的eatFood方法。
  public class Snake {
  private LinkedList body = new LinkedList();
  public List getBody() {
  return body;
  }
  public Snake() {
  for (int i = 0; i < 4; i++) {
  body.add(new Food());
  }
  }
  public void eatFood(Food food) {
  body.add(food);
  }
  }
  蛇身List里面存放Food,默认有四个,贪吃蛇嘛,太贪吃了,蛇身也是食物。测试OK.
  接着,开始写startRun方法,开始说了,动画就是不断的擦出和重画,那么这个startRun怎么擦除和重画呢?肯定都玩过贪吃蛇吧?想象一下,蛇的运动其实就是把尾巴擦除,在蛇头的前面再加一个就可以了,向左移动就是在蛇头的左边加一个,向下移动就是在蛇头的下面加一个,其余类似。那么就来实现吧!这个方法可能不好测试,因为它就是将List的尾去掉,再在List前面加一个而已。先直接写startRun方法。
  public void startRun(){
  body.removeLast();
  body.addFirst(new Food());
  }
  眼尖的肯定发现这里有问题了。所有的Food都是直接new Food(),而从Food类可以看出来x,y始终为0,这肯定不能组成蛇,先粗略的修改一下。
  public Snake() {
  for (int i = 0; i < 4; i++) {
  Food food = new Food();
  food.setX(20*(i+1));
  food.setY(20);
  body.add(new Food());
  }
  }
  接着是startRun里面的body.addFirst(new Food());这个就有问题了,你知道这个new Food()是在第一个的上面?左边?下面?还是右边呢?这就要看,当前是在向哪个方向运动了!
  那么就来看看四个turn方法怎么设计?这里其实就是个条件语句,四个turn就是四个条件,满足哪一个条件就在哪里添加!可以想到,这里的四个turn方法就是设置一个属性的四个值罢了。这里就是枚举起作用的时候了(当然了,你可以直接用数字之类的啦!)看下枚举!
  public enum Turn {
  UP,LEFT,RIGHT,DOWN 
  }
  很简单,也很清晰明了吧!那么四个trun方法就知道怎么写了吧?
  private Turn direction = Turn.RIGHT;
  public void turnUp(){
  direction = Turn.UP;
  }
  public void turnDown(){
  direction = Turn.DOWN;
  }
  public void turnLeft(){
  direction = Turn.LEFT;
  }
  public void turnRight(){
  direction = Turn.RIGHT;
  }
  这个,不需要测试吧!现在知道了方向了,那么在那里添加那个Food也显而易见了吧?
  public void startRun(){
  body.removeLast();
  Food food = new Food();
  Food head = body.getFirst();
  if(direction == Turn.DOWN){
  food.setX(head.getX());
  food.setY(head.getY() + 20);
  }else if(direction == Turn.LEFT){
  food.setX(head.getX() + 20);
  food.setY(head.getY());
  }else if(direction == Turn.RIGHT){
  food.setX(head.getX() - 20);
  food.setY(head.getY());
  }else if(direction == Turn.UP){
  food.setX(head.getX());
  food.setY(head.getY() - 20);
  }
  body.addFirst(food);
  }
  搞定。Snake和Food基本搞定了,dead方法待会再说,先看到效果才让人兴奋吧!
  开始SnakeWin类了,这个应该很简单吧?界面而已。这里不详细说了,Swing不是重点!
  界面没有设计,就一个JPanel而已!有兴趣的自己设计吧!接下来,开始对代码进行重构和发现Bug,修改Bug!                                         [PS:转载]
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics