技术开发 频道

从设计原则谈软件开发

【IT168分析评论】几天一直在研究着一些设计模式,作为初学者,我想很多人可能都会有这样的感觉,就是很多设计模式看上去都大同小异。那是因为我们并没有过多的项目经验,因此并不能想象到何处应该应用何种设计模式。

      我在网上看过很多人有这样一种观念,就是说设计模式不要学,尤其是学生不要学。其实我不赞同这种说法,因为我一直认为理论和实践应该是共同进步的。也许我的这个观点不具有特别的权威性。但是有一点是一定的,我们可以不懂设计模式,但我们一定要熟悉设计原则。正是抱着这个想法,因此,我想从初学者的角度,把设计模式的几个设计原则系统地来说一下。
 
      首先,我们要先明白为什么要应用设计模式。有一种这样的说法,没有学过设计模式,你就永远都无法理解,甚至说根本就不会理解什么是面向对象,为什么要面向对象。我愿意对面向对象的优点用这一句话来总结,设计模式(或者说是面向对象)的根本目的是为了重用和应对变化。在这里,《大话设计模式》上的活字印刷的例子非常经典地解释了面向对象的优点。我来大概概括一下这个例子:
 
      曹操雅兴作诗一首:喝酒唱歌,人生真爽。于是记录师就刻了一晚上的字把这八个字给记录了下来。第二天,曹操改变注意把诗改成对酒唱歌,人生真爽。于是记录师不得不把这八个字都毁掉,然后重刻了八个字....最后,曹操反复修改改成对酒当歌,人生几何。期间,记录师只能不停地毁掉,重做...耗费的人力劳力之大,我们可以想象。但是我们想象活字印刷,记录师完全可以把每个字来刻,即使修改了,记录师也只需要新做几个字与原来的字组合到一起(应对变化),而且被取代的字将来也可以得到应用(重用性)。
 
      这是现实中的例子,我们再想想程序中的例子。我相信每个程序员都应该听过这样一个概念叫组件。每个公司都有着他的组件库。一家想要发展下去的公司一定要有了一个庞大而且健壮的组件库。我们举一个最常见的例子,数据访问组件。我们写代码时,尤其是开发一个网站时,我们可以发现,我们写的很多代码都是重复代码,其实说白了,就是在不停地和数据库打交道,进行着增删改查的操作。于是,很多公司就把这部分给提取了出来,做成专门的数据访问组件,然后程序员只需要访问这个数据访问组件,一般来说是向这个数据访问组件的方法中传入SQL语句或者是存储结构名称,再到最近可能传入LINQ语句,然后由这个数据访问组件和数据库之间来打交道,这样就节省了很多人力和物力。其实,这也就是我们通常所说的分层。我们来总结这个组件的作用,其实也就无非是为了重用和今后的应对变化。所以一切设计模式和设计原则也是应对着这两点来展开的。
 
      因此,在应用设计模式上有两个极端。第一种就是不会用设计模式,这点的人很多。就是根本就不知道用设计模式,做一个项目,就是堆积代码,用asp.net来举例子,就是把所有的代码都写到.aspx.cs中,这样的缺点我不说大家也知道,就是非常不容易修改,而且他写出的代码,重用性非常差。第二种就是过分地应用设计模式,大家也许都觉得设计模式应用地越多,这样的项目不是越好么?其实不是这样的,还是我刚才说的那句话,设计的目的是为了重用和应对变化。如果你设计的部分根本不会发生变化,而且根本没有重用性,这样的设计不仅使整个项目变得庞大,同时也会影响程序的效率。
 
      这点也是网上有些观点说学生不适合学设计模式的原因,因为作为学生,没有企业实际的项目经验,根本无法很好地找出变化点和重用点,所以极易造成设计过度或者是该设计的地方没有应用合适的模式。但是,设计原则无论在何处都是应该遵守的。
 
      由于时间原因,今天只来介绍一下设计原则中的第一个:单一职责原则(SRP)。
 
      从名字上就可以看出个大概意思。就是说每个类只有一个职责。这里的职责,我们要把它理解为,产生变化的原因。把这两句话综合起来,就是GOF的话:每个类,只应该有一个能够引起他变化的原因。
 
     我们仍然从面向对象的两个角度来分析单一职责原则的目的。首先是重用,我们应该可以想到,一个类的功能越多,那么这个类的可重用性必然越差。举例子,一个做蛋糕的机器,比如我们把和面,定型,加温,冷却,全都放到同一个机器中,那么这个机器只可以做出一种蛋糕。但是如果我们把这些功能都分到不同的机器中,有的机器和面,有的机器定型...这样排列组合,我们应该可以想到。比如说先和面,再加温,定型,冷却,就可以做出一种新的蛋糕。甚至可以做出各种各样不是蛋糕的东西。然后我们想应对变化,我们想在这个新的机器中加入掺杂鸡蛋的功能,如果是一台功能完全的机器,我们只能把这台机器拆掉,然后重构。成本可想而知。如果功能分开呢,我不再多说.....
 
      软件也是一样,有些人可能对我的应对变化这点产生质疑。说蛋糕机只能拆掉重做,但是一个类只需要添些代码就可以啦!这个问题我们留一下,下次说到OCP的时候大家就明白了。
 
     从SRP,我来简单的扩展一个设计模式。其实设计模式,就是用各种模式来达到设计原则的目的罢了。我要说的是迭代器模式。
 
     关于迭代,我相信每个学过高级语言的人都不会陌生,无论是JAVA还是.net。java中的for(:)和.net中的foreach(in)都是迭代器模式的一种封装体现。
 
      今天只是简单地说下,以后再来仔细讲下这个,引用GOF的解释。迭代器模式提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。
我们来看下迭代器模式的结构图:
 
 
这里具体的英文解释我就不说了,我来简单说一下两个接口的作用:
1.       IList是一个集合类的接口,我在里面只写了一个方法,就是获取某特定集合的Iterator。
2.       IIterator的作用是规定了迭代器的一些必要方法。
 
    这里,讲Iterator和List分开就是一个单一职责原则的体现。List只是一个容器,至于我们要对这个容器进行什么操作,不应该属于这个类所管辖的内容。因此,我们就把这个功能分出了一个新的迭代器的类来完成迭代的工作。比如说现在我们是对这个List类来顺序迭代。可是有一天,我突然想对List逆序迭代了,那么我们只需要增加一个逆序迭代器的类,就可以了。这就单一职责原则的易于变化。另外,比如现在我已经写好的顺序迭代的类,明天,我又写了一个Arraylist的容器类,同样要顺序迭代,那么我们完全可以重复应用顺序迭代器这个类,这就是单一职责原则的可重用性。
 
    谈到这里,接近尾声,最后再来提一下,我之前写过的接口,接口的设计一定要遵循的就是单一职责原则。我之前说,接口不能设计太大,也不能设计太小。我们用这句话再来对单一职责原则做下总结。单一职责,不是说我们把职责分得越细越好。而是要时刻记住,我们应用设计模式的目的,往具体了说,分类的目的不能为了分类而分类,类的划分是为了封装,而分类的基础是抽象。这里的职责,指的不是有几个功能,而是有几个能够引发类发生变化的原因。
 
    好了,今天就写到这,不足之处,希望大家多多指教。谢谢。
0
相关文章