首先来说一下软件工程,软件工程就是把软件的开发工程化,在给定成本、进度的前提下,开发出具有可修改性、有效性、可靠性、可理解性、可维护性、可重用性、可适应性、可移植性并且满足用户需求的软件产品。软件工程主要有一下内容:需求、设计、编码、测试。其过程中需要产生以下一些文档:可行性研究报告、软件需求说明书、数据库设计说明书、概要设计说明书、详细设计说明书、用户手册、测试分析报告等。这些文档贯穿软件开发的整个过程。
那么这些文档里都是些什么内容呢?要想系统的结构表达的更清楚是不是图表更有表现力呢?所以UML图(统一建模语言)就成为了文档的得力助手,画UML图的工具有很多,Rational Rose是一个应用比较广泛而且功能比较强大的工具。UML有九种图,分别从不同的侧面不同的粒度描述系统的结构流程。
1. 用例图描述角色以及角色与用例之间的连接关系,说明的是谁要使用系统,以及他们使用该系统可以做些什么,主要用在需求说明书中来表明系统需要实现的主要功能;
2. 包图描述了系统的整体架构,每一个包是一个程序集,用在概要设计说明书中;
3. 类图描述系统中的类以及各个类之间的静态关系视图,也用在概要设计说明书中表示各个类之间的调用关系;
4. 活动图描述每个用例进行的活动以及活动之间的关系,主要用在概要设计说明书中用来表示整个系统的运行流程;
5. 状态图描述类的对象所有可能的状态,以及事件发生时状态的转移条件,是对类图的补充;
6. 交互图包括协作图和顺序图,这两种图都是表达的对象与对象之间的交互,顺序图表现的是消息如何在对象之间被发送和接收的,主要强调时间和顺序,而协作图显示对象间的动态合作关系,主要强调上下级之间的关系,交互图用在详细设计说明书中;
7. 图描述代码构件的物理结构以及各种构件之间的依赖关系;
8. 部署图用来建模系统的物理部署。例如计算机和设备,以及它们是如何连接的;
系统架构就是系统的骨骼,如果骨骼没有设计好,做出来的软件也会是畸形,怎样的架构才是合理的,怎样的架构才能造就一个健康的软件,那么健康的软件是什么样的?首先要满足用户需求,其次要有可修改性、灵活性、可维护性,也就是当用户需求改变时,尽量少更改已经封装好的东西,而且还要达到目的,这样的要求传统的面向过程的编码方式是很难实现的,而MVC三层架构就能实现这一切,V代表界面层,C代表业务逻辑层,M代表数据访问层。这个三层架构只是宏观意义上的三层,其实根据系统架构的需要可以分为更多层。除了这三层外还有一层是实体层,实体层对应着数据库中的表,每一张表映射为一个实体,下面介绍一下三层架构具体是什么样的:
1. 界面层只负责与用户交互,用户输入信息,在界面进行基本验证(是否为空、是否是数字等)将数据传到业务逻辑层,经过业务逻辑层处理后返回给界面层信息,界面将信息显示给用户。
2. 业务逻辑层负责接受界面的数据,进行业务处理(包括一些逻辑判断,计算等),需要数据库中的数据就调用数据访问层的方法,业务处理后给界面返回数据。
3. 数据访问层主要是一些操作数据库的类,查出的数据返回到业务逻辑层。
4. 实体层中每一个实体对应着数据库中的每一张表,实体类作为参数在三层之间传递。
下面以添加用户为例:
界面层(UI):当用户按下添加按钮后,首先检查输入框中的数据是否合法,然后将数据赋值给用户实体中的每个字段,调用B层的添加用户方法,将用户实体作为参数传递。
业务逻辑层(Bll):首先判断界面传进来的用户实体是否已经存在(调用D层操作用户表中的检查用户方法),如果已经存在则抛出异常,如果不存在则向用户表中插入该用户实体(调用D层的操作用户表中的插入方法)。
数据访问层(Dal):对数据库的操作,与数据库的连接字符串放在app.configer文件中,方便更换。其中的两个方法分别是检查数据库中是否存在某个用户,想数据库中用户表中插入一条记录。
实体层:表中的字段都是私有的属性,他们的值的读写是通过属性过程来完成的。
三层架构的基本形式在怎样才能开发出好的软件(二)中已经讲过了,为了提高程序可维护性、可扩展性、可复用性、灵活性,可以在其中加入设计模式,设计模式有23种,这些设计模式可以分为三大类:创建型模式、结构型模式、行为型模式。下面就分别介绍一下这些设计模式的基本结构、使用的好处以及使用场合
创建型模式有抽象工厂模式、建造者模式、工厂方法、原型模式、单例模式。
抽象工厂模式:
这个设计模式客户端只与抽象工厂以及抽象产品打交道,而与具体的实现是隔离的,主要用在可能变更的地方,比如更换数据库。当需要不同类型的产品的话直接添加一个工厂和产生的产品即可。
建造者模式:
这个设计模式主要用于构造一个产品时,使所有的产品都有一些必须的部件,抽象建造者中定义了抽象的建造方法,具体的建造者继承抽象建造者时就必须实现抽象建造者中的所有组装方法,由于建造者隐藏了产品是如何组装的,所有如果想要改变一个产品的内部组装,只需要再定义一个具体建造者就可以了。
工厂方法模式:
工厂方法模式定义了一个用于创建对象的接口(抽象工厂类),让子类决定实例化那个类,它使一个类的实例化延迟到其子类(具体工厂)。它与抽象工厂模式的区别是:抽象工厂模式中的具体工厂用于生产一个品牌的所有产品,而工厂方法模式中的具体工厂用于生产具有相同功能的一类产品。
原型模式:
当创建多个类似的对象时就可以用原型模式,原型模式的关键点就在于Clone()方法,它使得相同的对象或类似的对象可以直接Clone,对于与原来对象不同的属性可以重新定义,但是大体上还是不会变的,如果更改的很多的话就要考虑是不是这个设计模式用的不恰当。用这个设计模式隐藏了对象的创建细节,而且不用重新初始化对象,对性能又是一个大的提高。
单例模式:
这个模式是我认为最简单的一个模式,之所以这么说是因为它简单到都不用画图来表示(开玩笑啦~~),这个类只有一点:就是保证一个类只有一个实例,并且提供一个访问它的全局访问点。那怎样才能做到这一点呢?办法就是让类自身保存它的唯一实例,这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。也就是说这个类中的构造方法设置为私有,不让外界利用new创建该类的实例,然后编写一个静态方法,这个方法保证这个类只有一个实例(如果实例不存在就创建一个实例,然后就返回实例)。
怎样才能开发出好的软件(三)已经介绍了创建型模式,这一节就看一下结构型模式。
首先结构型模式有:适配器模式、桥接模式、组合模式、装饰模式、外观模式、享元模式、代理模式。
下面一一介绍:
适配器模式:
这个设计模式是将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。这种情况一般出现在开发后期或者维护阶段,在设计阶段还是需要把接口设计的一致的,当然使用第三方控件的时候也会考虑使用适配器模式。
桥接模式:
桥接模式是将抽象部分与它的实现部分分离,使它们都可以独立的变化。它的优点是:把多种方式的实现独立出来,让它们各自的变化,每种实现的变化都不会影响其他实现,从而达到应对变化的目的。当我们需要多角度去分类实现对象是只用继承会造成大量的类增加,不能满足开放-封闭原则时就要考虑使用桥接模式了。
组合模式:
它将对象组合成树形结构以表示“部分-整体”的层次结构。使得用户对单个对象和组合对象的使用具有一致性。简单来说就是使用了组合模式你可以让多个公司任意隶属组合,比如我首先设立一个总公司,总公司下设立财务部、人力资源部,还有北京分公司、上海分公司,北京分公司和上海分公司下面还设立财务部和人力资源部,就是这样的一层一层的隶属关系。这样说来财务部和人力资源部都算是叶节点,具体公司算是枝节点,具体公司下可以再设置枝节点或叶节点。那什么时候使用组合模式呢?当你发现需求只用是体现部分与整体层次的结构时,以及希望用户可以忽略组合对象与单个对象的不同,统一的使用组合模式结构中的所有对象时,就应该考虑使用组合模式了。比如:ASP.NET中的TreeView控件就是组合模式的应用。
装饰模式:
装饰模式可以动态的给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更灵活。说白了装饰模式就是一层一层包装的过程,每个装饰对象只需要知道怎么包装,至于先包装哪一层是不需要知道的。它主要用在对已有功能动态的添加更多功能。
外观模式:
外观模式为子系统中的一组接口提供一个一致的界面,它定义了一个高层接口,这个接口使得这一子系统更加容易使用。通俗讲就是复杂的子系统提供出一个简单的接口,具体子系统内部是怎么操作的根本不需要外界知道,这样就减少了外界与这个子系统的依赖。在开发初期阶段要有意识的将两个层分离,在层与层之间建立外观。在维护一个遗留的大系统时,新的开发需求必须要依赖他,此时可以为新系统开发一个外观类,来提供设计粗糙或高度复杂的遗留代码的比较清晰的接口,让新系统与外观对象交互,外观对象与遗留代码交互所有的复杂工作。
享元模式:
享元模式是运用共享技术有效的支持大量细粒度的对象。简单来说,实例化的对象越多占用的内存也就越多,当存在大量重复的对象时,那就是资源的极大浪费,使机器性能减慢,享元模式就解决了这样的问题,让相同的对象共享同一个对象,这样就可以节约大量内存。
代理模式:
代理模式是为其他对象提供一种代理以控制对这个对象的访问。通俗讲就好像是秘书与老板,老板需要什么只需要告诉秘书,秘书负责去办老板的事,这样真正与外界打交道的是秘书,而不是老板,这样就起到了一定的隔离作用。那一般都用在什么场合呢?首先可用在远程代理,也就是为一个对象在不同的地址空间提供局部代表,这样可以隐藏一个对象存在于不同地址空间的事实。第二种应用是虚拟代理,是根据需要创建开销很大的对象,通过它来存放实例化很长时间的真实对象(比如说打开一个很大的HTML网页,里面可能有很多文字和图片,文字可以很快就能看到,但是图片要一张一张的下载,那些未打开的图片框就是通过虚拟代理来替代了真实的图片,此时代理存储了真实图的路径和尺寸,这样可以优化下载)。第三种是安全代理,用来控制真实对象访问时的权限,一般用于对象有不同访问权限的时候。还有一种是只能索引,当调用真实对象的时候,代理处理另外一些事。
行为型模式:观察者模式、模板方法模式、命令模式、状态模式、职责链模式、解释器模式、中介者模式、访问者模式、策略模式、备忘录模式、迭代器模式。
观察者模式:
观察者模式定义了一种一对多的依赖关系,让多个观察者同时监听某一个主题对象。这个主题对象在发生变化时,会通知所有观察者,使他们能够自动更新自己。当一个对象改变需要同时改变其他对象,而且不知道具体有多少对象有待改变时就应该考虑使用观察者模式了。其实观察者模式的工作就是解耦,让耦合的双方都依赖于抽象,而不是依赖于具体,从而使他们各自的变化不会影响另一边的变化。
模板方法模式:
模板方法模式定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。其实就是通过把不变的行为搬到超类中,去除子类中的重复代码。何时使用他就显而易见了,当不变的喝可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现,这时候我们就可以用模板方法模式,将这些重复的行为搬到一个单一的地方,也就是超类中,这样就提高了代码的复用率。
命令模式:
命令模式就是将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。它将调用操作的对象与知道如何实现该操作的对象解耦(服务员与烤肉串者),这就意味着可以在这两者之间处理很多事,比如发送者发送完请求就完事了,具体怎么做是我的事,我可以在不同的时刻指定、排列和执行请求。还可以在实施操作前将状态存储起来,以便支持取消/重做的操作。还可以记录整个操作日志,以便以后可以在系统出问题是查找原因或恢复重做。当然这就意味着支持事务,要么所有的命令全部执行成功,要么恢复到什么也没执行的状态。敏捷开发的原则告诉我们,不要为了代码添加基于猜测的、实际不需要的功能。如果不清楚一个系统是否需要命令模式,一般就不要着急去实现它,事实上,在需要的时候通过重构实现这个模式并不困难,只有在真正需要撤销/恢复操作等功能时,把原来的代码重构为命令模式才有意义。
状态模式:
状态模式是当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。它主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况,把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。当然,如果这个状态判断很简单,就没有必要用状态模式了。当一个对象的行为取决于他的状态,并且它必须在运行时刻根据状态改变他的行为时就可以考虑使用状态模式了。状态模式的好处就是他将与特定状态有关的行为放到一个ConcreteState中,所以通过定义新的子类就可以很方便的增加新的状态和转换了。有了状态模式就省略了判断状态转换的条件了,其实也不是省略了,只不过是把这个判断的条件都转移到ConcreteState中了。
职责链模式:
职责链模式就是使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系,将这个对象连成一条链,并沿着这条链传递该请求,知道有一个对象处理它为止。每一个处理者类都有一个设置下一个后继者的方法和一个处理请求的方法,这两个方法就保证了一个处理请求链存在的可能。这种模式使得接收者和发送者都没有对方的明确信息,并且链中的对象也不知道链的结构,它们只需要保持一个指向后继者的引用,从而大大降低了耦合度,并且可以随时改变或者增加处理一个请求的结构,增强了灵活性。但是需要注意的是有可能一个请求到了链的末端都没有得到处理,所以事先要考虑全面。
剩下的几个行为型设计模式留到后面再说
解释器模式:
解释器模式就是给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。用了解释器模式就意味着可以很容易的改变和扩展文法。如果一种特定类型的问题发生的频率足够高,那么就值得将该问题的各个实例表述为一个简单语言中的句子,这种情况下就可以构造一个解释器,解释器通过解释这些句子来解决问题。当然解释器模式为文法中的每一条规则至少定义了一个类,因此可能导致文法难以管理和维护。所以当文法非常复杂的时候可以使用其他的技术来处理,如语法分析程序或编译器生成器。
中介者模式:
中介者模式就是用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式的相互引用,从而使其耦合松散,而且可以独立的改变它们之间的交互。也就是具体同事类之间不直接通信,而是通过中介者来处理,中介者负责控制和协调一组对象间的交互,以使组中的对象不再相互显示的引用。这个模式很容易在系统中应用,也很容易在系统中误用。当系统出现了‘多对多’交互复杂的对象群时,不要急于使用中介者模式,而要先反思你的系统在设计上是不是合理。所以这个模式一般应用于一组对象以定义良好但是复杂的方式进行通信的场合。
访问者模式:
访问者模式表示一个作用于某对象结构中的个元素的操作,它使你可以在不改变个元素的类的前提下定义作用于这些元素的新操作。也就是说它将数据结构与作用于这个数据结构上的操作分离开,使得操作集合可以相对自由的演化,它的优点也就是增加新的操作很容易,因为增加一个新的操作就意味着增加一个新的访问者,访问者模式将有关的行为集中到一个访问者对象中。这个模式的缺点是增加新的数据结构比较困难,所以用这个设计模式的前提是系统的数据结构相对稳定。
策略模式:
策略模式定义了算法家族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化不会影响到使用算法的客户。策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,他可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。其实策略模式就是封装了变化点。在实践中,我们可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式来处理了。
备忘录模式:
备忘录模式是在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可将该对象恢复到原先保存的状态。 看名字就能知道什么时候用它了,当一个系统功能比较复杂,需要维护或者记录属性历史的时候。命令模式也有类似的撤销作用,如果在某个系统中使用命令模式时,需要实现命令的撤销功能,就可以使用备忘录模式来存储可撤销操作的状态。
迭代器模式:
迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。迭代器模式分离了集合对象的遍历行为,抽象出一个迭代器来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明的访问集合内部的数据。当你需要访问一个聚集对象,而且不管这些对象时什么都需要遍历的时候,或者对聚集有多种遍历方式的时候,就应该考虑使用迭代器模式了。