
软件技术方案设计原则.docx
8页软件技术方案设计原则软件开发是一项高强度的脑力劳动过程,到目前为止尚 没有办法使软件开发完全机械化,只能靠人脑去设计,也无 法保证一个软件完全没有错误同硬件产品相比,一方面是 同功能的软件产品不再需要一个生产过程,只需要拷贝就行 了,而硬件产品却需要重复生产 ;另一方面,软件产品功能 需求的变化远比硬件产品多这就造成软件产品和硬件产品 在成型之后面临的问题是不同的,软件产品主要是应付用户 功能需求的变化和扩展,硬件产品主要着眼于如何大规模地 生产硬件产品的大规模生产可以交给机器去执行,而软件 产品的需求变化还是要靠人脑去完成以下是软件技术方案 设计原则,欢迎阅读如何快速开发出符合用户功能需求的软件,如何保证开 发出的软件尽可能少地出现错误,如何使开发出的软件能够 容易地适应用户功能需求的变化和扩展,是所有软件开发人 员追求的目标;我想,也正是为了实现上述目标,才有了软 件设计原则和设计模式软件开发是起始于面向过程的,为什么会这样呢 ?我想 是因为面向过程地解决问题更直接,软件本身就是一个解决 问题的过程;面向过程的最大问题就是不容易把问题进行分 解,再大的问题都要在一个过程里面解决,从第一步直到最 后一步;最多是把一个大过程分解成几个顺序执行的小过 程,很考验人的逻辑推理能力,开发出的软件不容易维护、 重用和扩展。
面象对象的方法没那么直接,需要有一个抽象 的过程,要把问题抽象成一个个对象,每个对象解决一个小 问题,不同对象的组合就可解决不同的大问题 ;而且把对象 跟日常的事物联系起来,产生了属性、事件、方法这样的概 念,增加了对象的直观性设计原则和设计模式都是针对面向对象的设计方法而 提出来的,如果在软件开发中还完全采用面向过程的方法, 是无所谓设计原则和设计模式的在软件开发中,面向过程 是起步,是基础,没什么好研究的了 ;面向对象才是深入, 是王道,需要不断地去总结方法 ;下面的软件设计都是指面 向对象的设计方法根据前人总结的经验,在软件开发中,遵循一定的设计 原则,灵活地采用一些设计模式,可以提高软件的易维护性、 可扩展性以及重用的机率关于这方面最权威的著作恐怕就 是 Robert C. Martin 写的敏捷软件开发一书了;关于这本书, 个人阅读的理解如下:1、单一职责原则(SRP): —个类只实现一个功能;换一 种说法,一个类只能有一个引起它变化的原因 ;在软件工程 中有一个要求,叫做高内聚 ;一个类只实现一个功能,无凝 内聚度是最高的了 ;这一原则可以使一个类更好地被重用 ; 当然,“一个功能”是相对的,在某种情况下,MODEM功能 是一个单一功能,而在另一种情况下,可能就要把modem功 能再分解成多个小功能;2、 开放封闭原则(OCP):开放是指一个类能够扩展功能, 封闭是指这个类对于功能修改是封闭的,也就是说不能修改 其已有的代码和功能;要实现这一目标,关键是抽象;在客户 类中只使用抽象基类,在应用中子类继承基类,并按实际需 要扩展基类的功能;按更通俗的说法就是:接口不能改变, 功能可以扩展;3、 子类替换原则(LSP):就是一个子类在任何情况下, 都能替换掉它的基类;这是面向对象设计方法中实现继承和 多态必须遵循的一条基本原则,显然也是开放封闭原则能够 实现的基础;如何实现这一原则呢?那就是子类必须要有比 基类相同或更弱的前置条件,相同或更强的后置条件;前置 条件就是调用一个方法之前必须满足的条件,后置条件就是 一个方法执行之满足的条件 ;为了更清楚地说明这一个问 题,见下面的函数表达式:Y = F(X);F是一个函数,X是一个整型的输入参数,Y是一个整型 的返回值,如果F要求X>0,返回值Y>1,贝X>0和Y>1就 分别是F的前置条件和后置条件;如果F是基类A中的一个 函数,B是A的一个子类,并扩展了 F的功能,则B类中F 的前置条件必须跟A类中的相同或更弱,也就B类中的F必 须至少能接受X>0,如果能同时接收XI,这是一个X>0更强 的条件;B类中的F必须保证返回值Y>1,当然如果能保证 Y>10 更好,但不能使返回值 Y1 的条件;不满足子类替换原则 最直接的后果就是使应用程序产生BUG;必须说明的是,在实 际中是很难完全遵循子类替换原则的,必须作合理的假设, 在这个假设的前提下遵循子类替换原则,这就是所谓契约设 计;4、依赖倒置原则(DIP):就是上层模块不能依赖于下层 模块,两者都应该依赖抽象 ;抽象不能依赖细节,细节应该 依赖于抽象;对这一原则要灵活看待,因为这一原则和当前 开发中常用组件开发方式看起来是相矛盾的 ;首先明确定义 一下上层模块和下层模块,所谓上层模块是调用别人的模 块,也可称之为客户模块,下层模块是被别人调用的模块, 也可称之为服务模块 ;显然这是一个相对的概念,因为一个 模块很可能同时即调用别的模块,又被另外的模块调用 ;依 赖倒置原则告诉我们客户模块和服务模块不能互相依赖,而 只能依赖于一个抽象的基类 ;另外这个抽象基类的接口是由 客户模块决定,而不是由服务模块决定 ;也就是说客户模块 需要什么,服务模块就提供什么,而不是服务模块提供什么, 客户模块就使用什么;这颇有点当前企业信奉的一个原则: 客户就是上帝,客户需要什么,我们就提供什么 ;而我们当 前常用的组件编程中,每一个组件都是一个被别人调用的具 体类,显然是属于细节和服务模块,我们调用组件,实际上 就依赖了这些组件的模块;根据依赖倒置原则是不是就不能 调用这些组件呢?当然不能这么呆板,在设计中还有另一条 原则,稳定依赖是没有害处的;说到底,这些组件也是根据 客户需求制定出来的,只不过是已经固化了的需求;而且这 些组件经过了严密测试,是稳定的,依赖它们没有害处;依 赖倒置原则是针对我们自己的设计来说的;当然,如果我们 自己设计的某一个服务类经过了严格的测试和大量的使用, 都已经验证没有问题,也可以作为一个通用的组件,别人可 以调用它,依赖它,没有问题;5、接口隔离原则(ISP):这一原则好象是单一职责原则 的升级版,接口隔离原则强调的是当一个服务类需要被即有 共同功能需求又有不同功能需求的客户类使用时,不能在服 务类中加进它的客户不需要的方法,比如在服务类A的客户 中,B类客户需要F方法,而C类客户则不需要F方法,这 时不能简单地把F方法加到服务类A中以满足B类客户的需 求,而应分离接口;比如另设计一个服务类D,其中包含F方 法,并把共用的功能委托给A实现,这样B客户可以使用D, 而C客户继续使用A;对这一原则我有所保留的是:如果F方 法对C类没有影响,直接加到A类中也无防,而且这种情况 是很普遍;6、共同封闭原则:这是针对包的;一个包对应用一个程 序文件,包含一到多个类,这些类具有共同的封闭性,要么 是都不能修改,要么是只能由同一原因引起修改;7、共同重用原则:也是针对包的;不同类的通用性也是 不一样的,通用性最高的就是在所有项目中都可使用,比如 我们用到的集成开发工具中的组件 ;有些类可能包含些行业 特征,只能这一行业类的软件中使用,有些类包含了某一个 项目的特征,就可能在该项目中使用 ;但是一个包中的所有 类的通用性都应该是一样的,这样才能保证包的重用度最大 化;1、 策略模式(STRATEGY):在一个拥有通用算法的具体 类中,把一些调用的方法委托给一个接口类实现,通过接口 的不同实现,扩展不同的功能 ;策略模式能够重用通用具体 类,又易于扩展功能;2、 工厂模式(FACTORY):在一个工厂类中,传入不同的 参数,可生成不同的类(相同的接口,不同的实现);工厂模 式易于扩展功能;3、 封装模式(FAADE):对一个具有复杂接口的类(或API 函数)进行封装,并提供几个简单的接口供外部调用;封装模 式可以隔离复杂的接口,并使其使用变得简单;4、 命令模式(COMMAND):上层模块要操作一组COMMAND 对象,这些COMMAND对象都具有同样的方法(不同的实现), 上层模块在操作 COMMAND 对象时,只需要调用它们的方法, 而不用关心方法的实现 ;在某些情况下,这种模式会极大地 简化系统;5、 组合模式(COMPOSITE):当A调用B, 一对一的关系, 需要改变为A调用多个B(或B的子类对象)时,不更改A的 代码(比如在 A 中创建一个 B 或 B 的子类对象的列表,再依 次从列表中取出对象调用),而是从B继续一个子类C,在C 类中创建B或B的子类对象的列表,重写C类中相应的方法 为依次调用列表中对象的方法,从而用A和C 一对一的关系 代替A和B之间一对多的关系,并保持A的代码不用更改;6、 观察者模式(OBSERVER):在被观察者中提供注册接 口 (Register)用于注册观察者,所有注册的观察者都放入一 个列表中,在观察者中提供观察接口(Update),用于接收被 观察者发出的通知;当被观察者发生变化时,依次调用列表 中的观察者的观察接口 (Update) , 观察者在观察接口 (Update)中,对感兴趣的被观察者变化进行处理;7、 代理模式(PROXY):主要用于代理数据库操作,可以 实现数据库操作和业务操作的代码分离;实现模式如下:应用程序调用一个接口 A,B 和 C 都实现 A 中的所有接 口,其中B是知晓数据库的代现,利用一个数据库类D进行 数据库操作,然后委托相应的方法给C;代理模式使用不多, 主要是在B类中把方法再委托给C,在大多数情况下都没有 必要;8、适配器模式(ADAPTER):在一个稳定的架构中,增加 一个外部组件,但该组件的接口不符合架构的规范,这时就 可创建一个适配器类对外部组件进行封装,适配器的接口符 合架构的规范(这样才能纳入架构),相应的方法委托给外部 组件实现;这样就可把外部组件纳入到已有的架构中;另外, 也可能是需要提供一个具有不同接口的组件给另外的客户 端使用,同时又要把该组合件的功能纳入到已有的架构中, 通过一个适配器把组件纳入架构,另外的客户端直接使用组 件;最后说明:使用设计模式是有代价的,可能需要增加新 的类,编写额外的代码,增加复杂度 ;优势就是,可以使系 统更适应于功能需求的变化,包括功能修改和扩展,隔离变 化等;可以提高代码的重用率;所以对于设计模式,不能生搬 硬套,而应是顺势而为。
