讲师:钟发然\r日期:2009年5月
设计模式基础
目录
设计中的坏味道(30)\r什么是设计模式(20)\r设计模式讲解(6种) (55)\r交流讨论 (15)\r
设计中的坏味道
我们的代码中、设计中,经常散发着各种坏味道,严重地影响着系统的各种品质\r\r代码/设计中各种各样的坏味道,往往代表着各种各种的设计缺陷
设计中的坏味道--僵化
僵化(Rigidity)\r 软件难以修改的趋势,哪怕是简单的修改。\r\r症状:每个改动令到依赖块一层层地顺次变动。\r\r影响:管理人员因而害怕让开发人员修补非关键性问题,因为\r 不知何时能修改完,影响有多大。
设计中的坏味道--僵化
一个例子\r 假设EAS的环境信息不是使用上下文(context)来传递,那么每当新增一个信息(例如context增加某个新增组织类型信息),所有使用到的代码被迫做出修改,以支持使用新的信息。
设计中的坏味道--脆弱
脆弱(Fragility)\r软件的每次修改都在多处破损,而且往往出现在与变动区域概念上不相关的地方\r\r\r症状:每次修补都使软件更糟糕,带来更多的问题。\r\r影响:每次管理人员批准进行修补时,都担心带来新的崩\r 溃。\r
设计中的坏味道--脆弱
现在的补丁,往往出现这种情况。这种味道,代表着这个模块的设计肯定存在问题,可能是:\r依赖不该依赖的包,导致不相关的模块修改时也会引起本模块有问题\r划分不合理,由于资源上的原因,或者历史上的缘故,应该独立分开的类或者模块,交杂混在一起,彼此相互影响\r依赖于具体实现,而不是依赖于定义出来的抽象\r\r\r消除此种坏味道,对于测试人员也是很大的解脱。\r
设计中的坏味道--固化
固化(Immobility)\r无法重用项目中其他部分的功能\r\r\r症状:发现存在自己想要的功能,但该功能太依赖于其所\r 处的环境,发现要分离的工作量与风险太大而放弃\r 重用。\r\r影响:只能简单的粘贴代码来代替重用。\r
设计中的坏味道--固化
对于公共服务(例如BOS、框架或common模块等)存在而不适合用的,应主动沟通,以及提出需求\r\r\r自己维护的模块,尽可能的修复此坏味道,切记一定不能复制粘贴了事,重复代码是非常严重的问题
设计中的坏味道--粘滞
粘滞(Viscosity)\r软件粘滞:面临一个变更,不止一种实现方式。往往选择省时省事的方式,而不管对于设计的影响。\r\r环境粘滞:担心会波及他人,可能会选择只是波及自己但可能是不良的方式\r\r症状:错事易做,正事难为。\r\r影响:软件的可维护性因各种“捷径”修补,而渐渐退化。\r
设计中的坏味道--不透明性
不透明性(Opacity)\r一个模块难以理解\r\r\r症状:随着软件的不断发展,代码变得越来越难以理解。\r\r影响:当维护者不再熟悉原来的代码时,发现要再修改代\r 码很困难了。\r
设计中的坏味道--不透明性
应对办法:\r遵循代码规范,把代码携程清晰易懂的统一风格,把不透明性保持最低\r\r作者多从读者的角度去阅读、重构代码,使之易懂\r\r定期进行代码走读与评审\r
目录
设计中的坏味道(30)\r什么是设计模式(20)\r设计模式讲解(6种) (55)\r交流讨论 (15)\r
什么是设计模式
最初产生于建筑行业 \r\r设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结\r\r设计模式提供了一种共享经验的方式,可以使团体受益和避免不断的重复发明。 \r
什么是设计模式
设计模式是特定场景的通用解决设计方案\r通用\r被反复使用,或者已被成功实践证实过的\r可以复用,提高质量与效率\r\r特定场景\r设计模式是无穷无尽的,不同场景或者同一场景不同动机都会产生不同的设计模式\r可复用但勿套用,必须理解设计模式的使用场景与目标\r\r\r
最初产生于建筑行业
什么是设计模式
设计模式的四个基本要素\r模式名称\r\r问题\r\r解决方案\r\r效果\r
最初产生于建筑行业
什么是设计模式
OO领域中的设计模式服从于面向对象原则\rOCP原则\rOCP(The Open--Closed Principle)定义:\rA、软件应对扩展开放,对修改封闭。\r\r B、OCP是面向对象设计的核心原则,往往以其作为设计的指\r 导总则来考虑,然后应用其他原则以及各种设计模式来\r 满足它。\r
最初产生于建筑行业
什么是设计模式
三大基本面向对象设计原则:\r A、针对接口编程,而不是针对实现编程 ;\r B、优先使用对象组合,而不是类继承;\r C、封装变化点 。\r\r\r模式是特定场景下的通用解决方案;OOD原则是模式应用的指导原则。\r\r\r
最初产生于建筑行业
目录
设计中的坏味道(30)\r什么是设计模式(20)\r设计模式讲解(6种) (55)\r交流讨论 (15)\r
设计模式讲解--工厂方法模式(1)
概述\r 在软件系统中,经常面临着“某个对象”的创建工作,由于需求的变化,这个对象的具体实现经常面临着剧烈的变化,但是它却拥有比较稳定的接口。如何应对这种变化?提供一种封装机制来隔离出“这个易变对象”的变化,从而保持系统中“其它依赖该对象的对象”不随着需求的改变而改变?Factory Method模式!\r意图\r 定义一个用于创建对象的接口,让子类决定实例化哪一个类。\r适用性\r当一个类不知道它所必须创建的对象的时候;\r当一个类希望由它的子类来指定它创建的对象时;\r当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候;
设计模式讲解--工厂方法模式(2)
结构图
示例
设计模式讲解--工厂方法模式(3)
效果\r用工厂方法在一个类的内部创建对象通常比直接创建对象更灵活。\r\rFactory Method模式通过面向对象的手法,将所要创建的具体对象的创建工作延迟到了子类,从而提供了一种扩展的策略,较好的解决了这种紧耦合的关系。\r\r\r实现要点\r Factory Method模式的两种情况:一是Creator类是一个抽象类且它不提供它所声明的工厂方法的实现;二是Creator是一个具体的类且它提供一个工厂方法的缺省实现。\r工厂方法是可以带参数的。\r工厂的作用并不仅仅只是创建一个对象,它还可以做对象的初始化,参数的设置等\r
设计模式讲解--单例模式(1)
概述\r Singleton模式要求一个类有且仅有一个实例,并且提供了一个全局的访问点。从另一个角度来说, Singleton模式其实也是一种职责型模式。 \r意图\r 保证一个类仅有一个实例,并提供一个访问它的全局访问点。 \r适用性\r当类只有一个实例,而且客户可以从一个众所周知的访问点访问它时;\r当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
设计模式讲解--单例模式(2)
结构图
示例
设计模式讲解--单例模式(3)
效果\r对唯一实例的受控访问。\r是对全局变量的一种改进。\r允许可变数目的实例\r\r\r实现要点\rSingleton模式是限制而不是改进类的创建 \rSingleton类中的实例构造器可以设置为Protected以允许子类派生 \r理解和扩展Singleton模式的核心是“如何控制用户使用new对一个类的构造器的任意调用” \r
设计模式讲解--适配器模式(1)
概述\r 由于应用环境的变化,常常需要将“一些现存的对象”放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的。那么如何应对这种“迁移的变化”?如何既能利用现有对象的良好实现,同时又能满足新的应用环境所要求的接口? \r意图\r 将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。\r适用性\r系统需要使用现有的类,而此类的接口不符合系统的要求;\r想要建立一个可以重复使用的类,用于一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。这些源类不一定有很复杂的接口;\r(对象适配器)在设计里需要改变多个已有的子类的接口,如果使用类适配器就要针对每一个子类做一个适配器,这不实际;
设计模式讲解--适配器模式(2)
结构图
示例
设计模式讲解--适配器模式(3)
效果\r对于类适配器:\r1.用一个具体的Adapter类对Adaptee和Taget进行匹配。结果是当我们想要匹配一个类以及所有它的子类时,类Adapter将不能胜任工作。\r2.使得Adapter可以重定义Adaptee的部分行为,因为Adapter是Adaptee的一个子类。\r3.仅仅引入了一个对象,并不需要额外的指针一间接得到Adaptee.\r对于对象适配器:\r1.允许一个Adapter与多个Adaptee,即Adaptee本身以及它的所有子类(如果有子类的话)同时工作。Adapter也可以一次给所有的Adaptee添加功能。\r2.使得重定义Adaptee的行为比较困难。这就需要生成Adaptee的子类并且使得Adapter引用这个子类而不是引用Adaptee本身。 \r实现要点\rAdapter模式主要应用于“希望复用一些现存的类,但是接口又与复用环境要求不一致的情况”,在遗留代码复用、类库迁移等方面非常有用;\rAdapter模式有对象适配器和类适配器两种形式的实现结构,但是类适配器采用“多继承”的实现方式,带来了不良的高耦合,所以一般不推荐使用。对象适配器采用“对象组合”的方式,更符合松耦合精神;\rAdapter模式的实现可以非常的灵活,不必拘泥于GOF23中定义的两种结构。例如,完全可以将Adapter模式中的“现存对象”作为新的接口方法参数,来达到适配的目的;\rAdapter模式本身要求我们尽可能地使用“面向接口的编程”风格,这样才能在后期很方便的适配;
设计模式讲解--外观模式(1)
概述\r 在软件开发系统中,客户程序经常会与复杂系统的内部子系统之间产生耦合,而导致客户程序随着子系统的变化而变化。那么如何简化客户程序与子系统之间的交互接口?如何将复杂系统的内部子系统与客户程序之间的依赖解耦?\r意图\r 为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。\r适用性\r为一个复杂子系统提供一个简单接口;\r提高子系统的独立性;\r在层次化结构中,可以使用Facade模式定义系统中每一层的入口;
设计模式讲解--外观模式(2)
结构图
Fa?ade模式注重的是简化接口,它更多的时候是从架构的层次去看整个系统,而并非单个类的层次
设计模式讲解--外观模式(3)
效果\r对客户屏蔽子系统组件,因而减少了客户处理的对象的数据并使得子系统使用起来更加方便;\rFa?ade模式实现了子系统与客户之间的松耦合关系,而子系统内部的功能组件往往是紧耦合的。松耦合关系使得子系统的组件变化不会影响到它的客户;\r如果应用需要,它并不限制它们使用子系统类。因此你可以在系统易用性与通用性之间选择;\r实现要点\r降低客户-子系统之间耦合度;\r公共子系统与私有子系统类;(fa?ade类是公共接口的一部分,但不是唯一的部分,子系统的其他部分也有公共的)
设计模式讲解--观察者模式(1)
概述\r 在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系” ——一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。\r意图\r 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。\r适用性\r当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用;\r当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变;\r当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。
设计模式讲解--观察者模式(2)
结构图
示例
设计模式讲解--观察者模式(3)
效果\r观察者模式在被观察者和观察者之间建立一个抽象的耦合。被观察者角色所知道的只是一个具体观察者列表,每一个具体观察者都符合一个抽象观察者的接口。被观察者并不认识任何一个具体观察者,它只知道它们都有一个共同的接口;\r观察者模式支持广播通讯。被观察者会向所有的登记过的观察者发出通知。 \r观察者模式缺点\r如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间;\r如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃。在使用观察者模式是要特别注意这一点;\r虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎么发生变化的,仅仅知道发生了什么变化而已;\r实现要点\r使用面向对象的抽象,Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达到松耦合;\r目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播。观察者自己决定是否需要订阅通知。目标对象对此一无所知;\r在C#中的Event。委托充当了抽象的Observer接口,而提供事件的对象充当了目标对象,委托是比抽象Observer接口更为松耦合的设计。
设计模式讲解--策略模式(1)
概述\r 策略模式是对算法的包装,是把使用算法的责任和算法本身分隔开,委派给不同的对象管理。策略模式通常把一个系列的算法包装到一系列的策略类里面,做为一个抽象策略类的子类。\r意图\r 定义一系列算法,吧它们一个个封装起来,并使它们可相互替换。本模式使得算法可以独立于使用它的调用者而变化。\r适用性\r许多相关的类仅仅是行为有异。策略提供了一种用多个行为中的一个行为来配置一个类的方法;\r需要使用一个算法的不同变体。如:可能会定义一些反映不同的空间/时间权衡的算法。当这些变体实现为一个算法的类层次时,可以使用策略模式;\r算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法有关的数据结构;\r一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入他们各自的Strategy类中以代替这些条件语句。\r
设计模式讲解--策略模式(2)
结构图
示例
设计模式讲解--策略模式(3)
效果\r定义了一系列可供重用的算法或行为,使用继承有助于析取出这些算法的公共功能;\r一个替代继承的方法。将算法封装在独立的Strategy类中使得你可以独立于其调用者改变它,使之易于切换、易于理解和扩展;\r消除了一些条件语句。含有许多条件语句的代码通常意味着需要使用策略模式;\r可以提供相同行为的不同实现;\r但使用者需要选择合适的策略这需要知道具体实现问题(不能隐藏实现)。因此只有这些不同行为变体与客户相关时,才使用Strategy模式;\r增加了Strategy和Context之间的通信开销;\r增加了对象数目,增加维护;\r实现要点\r定义Strategy和Context接口,必须使Concrete能够有效的访问它锁需要的数据,这需要Context将数据放在参数中传递,或者直接将Context做为参数传递;\r(c )将Strategy做为一个模板参数,可以将一个Strategy和Context静态地绑定在一起,提高效率;\r
交流讨论
Q