1、面向对象分析与设计,高级设计原则与设计模式,内容提要,高级设计原则 设计模式 AOP编程,面向对象设计原则-OCP,开放封闭原则(OCP) 类模块应该是可扩展的,但是不可修改 Closed for Modification; Open for Extension (对扩展开放,对更改封闭) OCP说明了软件设计应该尽可能地使架构稳定而又容易满足不同的需求。,如何在OO中引入OCP?,把对实体的依赖改为对抽象的依赖 .,OCP举例1-收音机,收听节目时需要打开收音机电源,对准电台频率和进行音量调节。 但是对于不同的收音机,实现这三个步骤的细节往往有所不同。 不太可能针对每种不同类型的收音机通过一
2、个收音机类来实现(通过重载)这些不同的操作方式。,OCP举例1-收音机(续),定义一个收音机接口,提供开机、关机、增加频率、降低频率、增加音量、降低音量六个抽象方法。 不同的收音机继承并实现这六个抽象方法。 新增收音机类型不会影响其它原有的收音机类型,收音机类型扩展极为方便。 已存在的收音机类型在修改其操作方法时也不会影响到其它类型的收音机。,OCP举例1-收音机(续),OCP举例2-赛车,OCP举例2-赛车(续),OCP总结,OCP是OO设计原则中高层次的原则,其余的原则对OCP提供了不同程度的支持。 为了实现OCP,我们会自觉或者不自觉地用到其它原则或是诸如Bridge、Decorator
3、等设计模式。 对于一个应用系统而言,实现OCP并不是设计目的,我们所希望的只是一个稳定的架构。所以对OCP的追求也应该适可而止,不要陷入过渡设计。,面向对象设计原则-SRP,Single Responsibility Principle 单一职责原则 所谓单一职责,就是一个设计元素只做一件事 “单一职责”就是要在设计中为每种职责设计一个类,彼此保持正交,互不干涉。 (二重奏),SRP举例-调制解调器,SRP总结,违反SRP通常是由于过于“真实”地设计了一个类所造成的。 因此,解决办法是往更高一层进行抽象化提取,将对某个具体类的依赖改变为对一组接口或抽象类的依赖。,面向对象设计原则-LSP,Li
4、skov于1987年提出了一个关于继承的原则“Inheritance should ensure that any property proved about supertype objects also holds for subtype objects.” “继承必须确保超类所拥有的性质在子类中仍然成立。” 也就是说,当一个子类的实例应该能够替换任何其超类的实例时,它们之间才具有is-A关系。,LSP原则实质,一个对象是一组状态和一系列行为的组合体。状态是对象的内在特性,行为是对象的外在特性。 LSP所表述的就是在同一个继承体系中的对象应该有共同的行为特征。 这一点上,表明了OO的继承与日
5、常生活中的继承的本质区别。,LSP举例,如何确保对象的行为?,每个方法调用之前,该方法应该校验传入参数的正确性,只有正确才能执行该方法,否则认为调用方违反契约,不予执行。这称为前置条件(Pre-condition)。 一旦通过前置条件的校验,方法必须执行,并且必须确保执行结果符合契约,这称之为后置条件(Post-condition)。 对象本身有一套对自身状态进行校验的检查条件,以确保该对象的本质不发生改变,这称之为不变式(Invariant)。,如何满足LSP?,当存在继承关系时,子类中方法的前置条件必须与超类中被覆盖的方法的前置条件相同或者更宽松; 子类中方法的后置条件必须与超类中被覆盖的
6、方法的后置条件相同或者更为严格。,OO语言特性,继承并且覆盖超类方法的时候,子类中的方法的可见性必须等于或者大于超类中的方法的可见性,子类中的方法所抛出的受检异常只能是超类中对应方法所抛出的受检异常的子类。 public class SuperClass public void methodA() throws IOException public class SubClassA extends SuperClass /this overriding is illegal. private void methodA() throws Exception public class SubClas
7、sB extends SuperClass /this overriding is OK. public void methodA() throws FileNotFoundException ,JAVA语言,从Java5开始,子类中的方法的返回值也可以是对应的超类方法的返回值的子类。这叫做“协变”(Covariant) public class SuperClass public Number caculate() return null; public class SubClass extends SuperClass /only compiles in Java 5 or later.
8、public Integer caculate() return null; ,LSP总结,严格按照接口和虚拟类的语法规范来做就能很好遵循此原则 如果有两个具体类A和B之间的关系违反了LSP,可以采用以下重构方案 创建一个新的抽象类C,作为两个具体类的超类,将A和B共同的行为移动到C中,从而解决A和B行为不完全一致的问题。 从B到A的继承关系改写为委派关系。,面向对象设计原则-DIP,依赖倒置原则 (针对接口编程,不要针对实现编程 ) 高层模块不应该依赖于低层模块。二者都应该依赖于抽象。 抽象不应该依赖于细节。细节应该依赖于抽象 针对接口编程:使用接口和抽象类进行变量的类型声明、参量的类型声明
9、,方法的返还类型声明,以及数据类型的转换 不要针对实现编程:不应当使用具体类进行变量的类型声明、参量的类型声明,方法的返还类型声明,以及数据类型的转换,DIP举例,DIP总结,DIP虽然强大,但却不易实现,因为依赖倒转的缘故,对象的创建很可能要使用对象工厂,以避免对具体类的直接引用,此原则的使用将导致大量的类文件。给维护带来不必要的麻烦。所以,正确的做法是只对程序中频繁变化的部分进行依赖倒置。,面向对象设计原则-ISP,接口隔离原则 不要强迫客户依赖于它们不用的方法。 一个类对另外一个类的依赖性应当是建立在最小的接口上的。如果客户端只需要某一些方法的话,那么就应当向客户端提供这些需要的方法,而
10、不要提供不需要的方法。提供接口意味着向客户端作出承诺,过多的承诺会给系统的维护造成不必要的负担。 使用多个专门的接口比使用单一的接口要好。,ISP的实质,如果你拥有一个针对多个客户的类,为每一个客户创建特定业务接口,然后使该客户类继承多个特定业务接口将比直接加载客户所需所有方法有效。,ISP举例,三个基本面向对象设计原则, 针对接口编程,而不是针对实现编程 优先使用对象组合,而不是类继承 封装变化点,设计模式定义,设计模式的广义定义: 设计模式概念是由建筑设计师Christopher Alexander提出:“每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,
11、你就能一次又一次地使用该方案而不必做重复劳动。” 设计模式的狭义定义: 可以简单的认为:设计模式就是解决某个特定的面向对象软件问题的特定方法。,框架和设计模式的区别,框架可以认为是一个适用于某个领域的软件包。这个软件包提供了相应领域的各个问题的解决方法 设计模式针对面向对象的问题域;框架针对特定业务的问题域 设计模式在碰到具体问题后,才能产生代码;框架已经可以用代码表示。 设计模式是比框架更小的体系结构元素 ,框架中可以包括多个设计模式。,为什么要用设计模式?,作为程序员都知道良好程序的一个基本标准:高聚合,低耦合。面向对象语言比结构化语言要复杂的多,不良或者没有充分考虑的设计将会导致软件重新
12、设计和开发。然而实际的设计过程中,设计人员更多的考虑如何解决业务问题,对于软件内部结构考虑较少;设计模式则补充了这个缺陷,它主要考虑如何减少对象之间的依赖性,降低耦合程度,使得系统更易于扩展,提高了对象可复用性。因此,设计人员正确的使用设计模式就可以优化系统内部的结构。,设计模式描述,模式名称 便于使用者在更高的抽象层次上进行设计并交流有关设计思想 问题描述 指明可以被应用的所必须的环境条件,解释了设计问题及其背景 解决方案 描述了设计方案的组成部分、它们之间的关系及各自的职责和协作方式 效果 描述应用设计模式后的结果及使用模式时应权衡的问题,设计模式的种类,创建型(Creational):解
13、决如何创建对象的问题。 结构型(Structural):解决如何正确的组合类或对象的问题。 行为型(Behavioral):解决类或对象之间如何交互和如何分配职责的问题。,创建型模式,使系统独立于对象的产生、组合和表示 将系统使用哪些具体的类的相关信息封装起来 隐蔽了这些具体类的实例是如何被创建和使用 的 创建型模式在创建“什么”、“怎样”被创建、由“谁”创建以及“何时”创建等方面具有很大的灵活性,创建型模式类别,抽象工厂(Abstract Factory) 提供创建相关的或相互依赖的一组对象的接口而无须指定具体的类 生成器(Builder) 将一个复杂对象的创建与表示分离,使得同样的构建过程
14、可以用于创建不同的表示 工厂方法(Factory Method) 定义一个用于创建对象的接口,由子类决定实例化哪一个类 原型(Prototype) 使用一个原型指定要创建的类的类型,通过复制这个原型得到新的对象 单件(Singleton) 保证一个类仅有一个实例,并提供一个全局性的访问点,工厂方法模式,实现代码,public interface Product public class ConcreteProduct implements Productpublic ConcreteProduct() public interface Creatorpublic Product factory
15、method(); public class ConcreteCretor implements Cretorpublic Product factorymethod() return new ConcreteProduct(); public class Client private static Creator creator;private static Product product;public static void main(String args)creator = new ConcreteCretor();product =creator.factorymethod(); ,
16、适用范围,一个类不知道它所必须创建的对象的类 一个类希望由其子类确定它创建的对象 类将创建对象的职责分配给多个帮助者子类中的一个,且将哪一个帮助者子类作为代表者这项信息局部化,结构型模式,涉及如何组合类和对象以构成更大的结构 一种方法是采用继承机制来组合接口或实现以形成更大的结构 另一种方法通过对象组合方式来形成 对象组合可以在运行时刻改变 继承机制为静态组合,创建型模式类别,适配器(Adapter) 将一个类的接口转换成用户希望的另一种接口 桥(Bridge) 将类的抽象部分与实现部分分离,使它们可以相互独立地变化 组合(Composite) 将对象组成树结构来表示局部和整体的层次关系,使单
17、个对象和组合对象的使用具有一致性 装饰(Decorator) 动态地给一个对象添加新功能 外观(Facade) 给一个子系统的所有接口提供一个统一接口,使子系统便于使用 轻量(Flyweight) 运用共享技术有效地支持大量细粒度对象 代理(Proxy) 为其他对象提供一个代理,以控制对该对象的访问,适配器模式,实现代码,public interface Targetpublic void request(); public class Adapteepublic void operation() public class Adapter ectends Adaptee implements
18、Targetpublic void request()operation(); public class Adapter implements Targetprivate Adaptee adaptee;public Adapter(Adaptee adaptee)this.adaptee=adaptee;public void request()adaptee.operation(); ,实现代码(续),public class Client private static Target target;public static void main(String args)target = n
19、ew Adapter(); public class Client private static Target target;private static Adaptee adaptee;public static void main(String args)target = new Adapter(adaptee); ,适用范围,希望使用一个已存在的类,但它的接口与希望的接口不匹配 要创建一个可复用的类,该类可以与其他不相关的类或不可预见的类协同工作 想使用一些已存在的类,但不能对每一个类子类化以匹配它们的接口,行为型模式,不仅描述对象或类的模式,还描述它们之间的通信模式,这些模式刻画了在运
20、行时难以跟踪的复杂的控制流 行为型模式使设计者的注意力从控制流转移到对象间的联系方式上,行为型模式类别,职责链(Chian of Responsibility) 命令(Command) 解释器(Interpreter) 迭代器(Iterator) 中介者(Mediator) 备忘录(Memento) 观察者(Observer) 状态(State) 策略(Strategy) 模板方法(Template Method) 访问者(Visitor),数据库操作超类,AOP是什么?,AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向方面编程。AOP实际是Go
21、F设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。 面向方面编程 (AOP) 是施乐公司帕洛阿尔托研究中心 (Xerox PARC) 在上世纪 90 年代发明的一种编程范式,实现举例,假设有在一个应用系统中,有一个共享的数据必须被并发同时访问,首先,将这个数据封装在数据对象中,称为Data Class,同时,将有多个访问类,专门用于在同一时刻访问这同一个数据对象。 abstract class Worker abstract void locked(); abstract void accessDataObject(); abstract
22、 void unlocked(); ,这种实现方式的缺点,accessDataObject()方法需要有“锁”状态之类的相关代码。 Java只提供了单继承,因此具体访问类只能继承这个父类,如果具体访问类还要继承其它父类,比如另外一个如Worker的父类,将无法方便实现。 重用被打折扣,具体访问类因为也包含“锁”状态之类的相关代码,只能被重用在相关有“锁”的场合,重用范围很窄。,分析它的特点,“锁”功能不是具体访问类的首要或主要功能,访问类主要功能是访问数据对象,例如读取数据或更改动作。 “锁”行为其实是和具体访问类的主要功能可以独立、区分开来的。 “锁”功能其实是这个系统的一个纵向切面,涉及许
23、多类、许多类的方法,应用,Authentication 权限 Caching 缓存 Context passing 内容传递 Error handling 错误处理 Lazy loading 懒加载 Debugging 调试 logging, tracing, profiling and monitoring 记录跟踪 优化 校准 Performance optimization 性能优化 Persistence 持久化 Resource pooling 资源池 Synchronization 同步 Transactions 事务,怎样才能设计“好的面向对象”,遵循一定的面向对象设计原则 熟悉一些典型的面向对象设计模式,软件开发的艺术,软件开发的全部艺术就是权衡:在简单与复杂之间权衡,在一种方案与另一种方案之间权衡。如果把每个问题、每个权衡的利弊都考虑得清清楚楚,恐怕开发一个应用程序的成本会高得惊人。所以,很多时候我们更依赖自己的审美眼光,用平静的心去设计一个赏心悦目的系统。”,