《大话设计模式》自学笔记.docx
- 文档编号:25702729
- 上传时间:2023-06-11
- 格式:DOCX
- 页数:35
- 大小:501.46KB
《大话设计模式》自学笔记.docx
《《大话设计模式》自学笔记.docx》由会员分享,可在线阅读,更多相关《《大话设计模式》自学笔记.docx(35页珍藏版)》请在冰豆网上搜索。
《大话设计模式》自学笔记
设计模式
《重构与模式》经典之语:
如果想成为一名更优秀的软件设计师,了解优秀软件设计的演变过程比学习优秀设计本身更有价值,因为设计的演变过程中蕴藏着大智慧。
1)简单工厂模式
图表1简单工厂模式结构图
简单工厂模式是众多模式的基础。
2)策略模式
面向对象的编程,并不是类越多越好,类的划分是为了封装,但分类的基础是抽象,具有相同属性和功能的对象的抽象集合才是类。
策略模式(Strategy):
它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。
算法本身只是一种策略,最重要的是这些算法是随时都可能互相替换的,这就是变化点,而封装变化点是面向对象的一种很重要的思维方式。
图表2策略模式(Strategy)结构图
●Strategy类,定义所有支持的算法的公共接口。
●ConcreteStrategy,封装了具体的算法或行为,继承于Strategy。
●Context,用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用。
策略模式的优点:
1)它是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。
2)它的Strategy类层次为Context定义了一系列的可供重用的算法或行为。
继承有助于析取出这些算法中的公共功能。
3)它简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。
4)当不同的行为堆砌在一个类中时,很难避免使用条件语句来选择合适的行为。
将这些行为封装在一个个独立的Strategy类中,可以在使用这些行为的类中消除条件语句。
策略模式的使用:
它本身是用来封装算法的,但在实践中,可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。
3)单一职责原则
单一职责原则(SRP),就一个类而言,应该仅有一个引起它变化的原因。
如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。
这种耦合会导致脆弱的设计,当变化发生时,设计会遭到意想不到的破坏。
软件设计真正要做的许多内容,就是发现职责并把那些职责互相分离。
判断是否应该分离出类来:
如果你能够想到多于一个的动机去改变一个类,那么这个类就具有多于一个的职责,就应该考虑类的职责分离。
4)开放-封闭原则
开放-封闭原则,即软件实体(类、模块、函数等等)应该可以扩展,但是不可修改。
●对于扩展是开放的(Openforextension);
●对于更改是封闭的(Closedformodification)。
开放-封闭原则的意思,是说设计的时候,时刻要考虑,尽量让这个类是足够好,写好了就不要去修改了,如果新需求来,增加一些类就完事了,原来的代码能不动则不动。
它使我们的设计面对需求的改变时可以保持相对稳定,从而系统可以在第一个版本以后不断推出新的版本。
无论模块是多么的“封闭”,都会存在一些无法对之封闭的变化,设计人员必须对于他设计的模块应该对哪些变化封闭做出选择,程序员必须先猜测出最有可能发生的变化种类,然后构造抽象来隔离这些变化。
具体做法是,可以在发生小变化时,及早去想办法应对发生更大变化的可能,即等到变化发生时立即采取行动。
当我们最初编写代码时,假设变化不会发生;当变化发生时,我们就创建抽象来隔离以后发生的同类变化。
并不是什么时候应对变化都是容易的,我们希望的是在开发工作展开不久就知道可能发生的变化,查明可能发生的变化所等待的时间越长,要创建正确的抽象就越困难。
开放-封闭原则是面向对象设计的核心所在,遵循这个原则可以带来面向对象技术所声称的巨大好处,即可维护、可扩展、可复用、灵活性好。
开发人员应该仅对程序中呈现出频繁变化的那些部分做出抽象,然而,对于应用程序中的每个部分都刻意地进行抽象同样不是一个好主意,拒绝不成熟的抽象和抽象本身一样重要。
5)依赖倒转原则
依赖倒转原则(或译为依赖倒置原则),是指抽象不应该依赖细节,细节应该依赖于抽象,即要针对接口编程,不要对实现编程。
●高层模块不应该依赖低层模块,两个都应该依赖抽象;
●抽象不应该依赖细节,细节应该依赖抽象。
图表3依赖倒转原则示意图
里氏代换原则(BarbaraLiskov女士于1988年发表的,具体数学定义比较复杂),大意是一个软件实体如果使用的是一个父类的话,那么一定适用于其子类,而且它察觉不出父类对象和子类对象的区别。
即在软件里面,把父类都替换成它的子类,程序的行为没有变化。
里氏代换原则(LSP):
子类型必须能够替换掉它们的父类型。
使得继承复用成为了可能,只有当子类可以替换掉父类,软件单位的功能不受到影响时,父类才能真正被复用,而子类也能够在父类的基础上增加新的行为。
正是由于子类型的可替代性才使得使用父类类型的模块在无需修改的情况下就可以扩展。
高层次模块不应该依赖低层模块,两个都应该依赖抽象。
依赖倒转其实就是谁也不要依靠谁,除了约定的接口。
依赖倒转可以说是面向对象设计的标志,用哪种语言来编写程序不重要,如果编写时考虑的都是如何针对抽象编程而不是针对细节编程,即程序中所有的依赖关系都是终止于抽象类或者接口,那就是面向对象的设计,反之那就是过程化的设计了。
6)装饰模式
装饰模式(Decorator),动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。
把所需的功能按正确的顺序串联起来进行控制。
图表4装饰模式(Decorator)结构图
●Component定义一个对象接口,可以给这些镀锡动态地添加职责。
●ConcreteComponent是定义了一个具体的对象,也可以给这个对象添加一些职责。
●Decorator,装饰抽象类,继承了Component,从外类来扩展Component类的功能,但对于Component来说,是无需知道Decorator的存在的。
●ConcreteDecorator是具体的装饰对象,起到给Component添加职责的功能。
装饰模式利用SetComponent来对对象进行包装。
每个装饰对象的实现和如何使用这个对象分离开,每个装饰对象只关心自己的功能,不需要关心如何被添加到对象链当中。
如果只有一个ConcreteComponent类而没有抽象的Component类,那么Decorator类可以是ConcreteComponent的一个子类。
同样道理,如果只有一个ConcreteDecorator类,那么就没有必要建立一个单独的Decorator类,而可以把Decorator和ConcreteDecorator的责任合并成。
装饰模式是为已有功能动态地添加更多功能的一种方式。
在起初的设计中,当系统需要新功能的时候,向旧的类中添加新的代码,这些新加的代码通常装饰了原有类的核心职责或主要行为,但这种做法的问题在于,它们在主类中加入了新的字段、新的方法和新的逻辑,从而增加了主类的复杂度,而这些新加入的东西仅仅是为了满足一些只在某种特定情况下才会执行的特殊行为的需要。
装饰模式却提供了一个非常好的解决方案,它把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象,因此,当需要执行特殊行为时,客户代码就可以在运行时根据需要有选择地、按顺序地使用装饰功能包装对象了。
装饰模式的优点是,把类中的装饰功能搬移去除,这样可以简化原有的类,有效地把类的核心职责和装饰功能区分开,而且可以去除相关类中重复的装饰逻辑。
7)代理模式
代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的访问。
图表5代理模式(Proxy)结构图
●Subject类,定义了RealSubject和Proxy的共用接口,在任何使用RealSubject的地方都可以使用Proxy。
●RealSubject类,定义Proxy所代表的真实实体。
●Proxy类,保持一个引用使得代理可以访问实体,并提供一个与Subject的接口相同的接口,这样代理就可以用来代替实体。
使用代理模式:
1)远程代理,为一个对象在不同的地址空间提供局部代表,以隐藏一个对象存在于不同地址空间的事实。
示例:
WebService在.NET中的应用个,当在引用程序的项目中加入一个Web引用,引用给一个WebService,此时会在项目中生成一个WebReference的文件夹和一些文件,其实它们代理,这就使得客户端程序调用代理就可以解决远程访问的问题。
2)虚拟代理,根据需要创建开销很大的对象,通过它来存放实例化需要很长时间的真实对象。
示例,打开一个很大的HTML网页时,里面可能有很多的文字和图片,对于为代开的图片框,是通过虚拟代理来替代了真实的图片,此时虚拟代理存储真实图片的路径和尺寸。
3)安全代理,控制真实对象访问时的权限。
一般用于对象应该有不同的访问权限的时候。
4)智能指引,指当调用真实的对象时,代理处理另外一些事。
例如计算真实对象的引用次数,这样当该对象没有引用时,可以自释放它;或当一次引用一个持久对象时,将它装入内存;或在访问一个实际对象前,检查是否已经锁定它,以确保其他对象不能改变它。
8)工厂方法模式
工厂方法模式(FactoryMethod),定义了一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。
图表6工厂模式方法(FactoryMethod)结构图
工厂方法模式实现时,客户端需要决定实例化哪一个工厂来实现运算类,选择判断的问题还是存在的,即工厂方法把简单工厂的内部逻辑判断移到了客户端代码来进行,想要加功能由原本的修改工厂类,转变为修改客户端。
简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。
工厂方法克服了简单工厂违背开放-封闭原则的缺点,又保持了封装对象创建过程的优点。
工厂方法模式是简单工厂模式的进一步抽象和推广。
9)原型模式
原型模型(Prototype),用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
图表7原型模式(Prototype)结构图
原型模式是从一个对象再创建另外一个可定制的对象,而且不需知道任何创建的细节。
使用原型模型大大提高程序的性能。
由原先的每NEW一次,都需要执行一次构造函数,如果构造函数的执行时间很长,那么多次的执行初始化操作效率很低。
一般在初始化信息不发生变化的情况下,克隆是最好的办法。
这既隐藏了对象创建的细节,又对性能是大大的提高。
它等于是不用重新初始化对象,而是动态地获得对象运行时的状态。
※MemberwiseClone()方法的执行时,如果字段是值类型,则对该字段执行逐位赋值如果字段是引用类型,则复制引用但不复制引用的对象;因此原始对象及其副本引用同一对象。
在Java中为clone()方法,需要实现Cloneable接口,否则抛出CloneNotSupportedException异常(此时必须在函数声明中抛出此异常)。
且无法复制引用的对象,原始对象及其副本引用同一对象。
浅复制指被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象;深复制吧引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。
10)模板方法模式
模板方法模式(TemplateMethod),定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
图表8模板方法模式(TemplateMethod)结构图
●AbstractClass是抽象类,其实也就是抽象模板,定义并实现了一个模板方法。
这个模板方法一般是一个具体的方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。
顶级逻辑也有可能是一些具体方法。
●ConcreteClass,实现父类所定义的一个或多个抽象方法。
每一个AbstractClass都可以有任意多个ConcreteClass与之对应,而每一个ConcreteClass都可以给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同实现,从而使得顶级逻辑的实现各不相同。
模板方法模式是通过把不变行为搬移到超类,去除子类中的重复代码来体现它的优势,它提供了一个很好的代码复用平台。
换一种说法,当不变的和可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现。
通过模板方法模式把这些行为搬移到单一的地方,这样就帮助子类摆脱重复的不变行为的纠缠。
11)迪米特法则
迪米特法则(LoD)(也叫最少知识原则),如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。
如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
迪米特法则的前提是在类的结构设计上,每一个类都应当尽量降低成员的访问权限,即一个类包装好自己的private状态,不需要让别的类知道的字段或行为就不要公开。
迪米特法则的根本思想,是强调了类之间的松耦合。
在程序设计时,类之间的耦合越弱,越有利于复用,一个处于弱耦合的类被修改,不会对有关系的类造成波及,也就是说,信息的隐藏促进了软件的复用。
12)外观模式
外观模式(Facade)(也叫门面模式),为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
图表9外观模式(Facade)结构图
外观模式完美地体现了依赖倒转原则和迪米特法则的思想。
外观模式的使用:
1)设计初期阶段,要有意识的将不同的两个层分离。
比如经典的三层架构,就需要考虑在数据访问层和业务逻辑层、业务逻辑层和表示层的层与层之间建立外观Facade,这样可以为复杂的子系统提供一个简单的接口,使得耦合大大降低。
2)开发阶段,子系统往往因为不断的重构演化而变得越来越复杂,大多数的模式使用时也都会产生很多很小的类,这本是好事,但也给外部调用它们的用户程序带来了使用上的困难,增加外观Facade可以提供一个简单的接口,减少它们之间的依赖。
3)维护一个遗留的大型系统时,可能这个系统已经非常难以维护和扩展了,但因为它包含非常重要的功能,新的需求开发必须要依赖于它。
此时用外观模式Facade,为新系统开发一个外观Facade类,提供设计粗糙或高度复杂的遗留代码的比较清晰简单的接口,让新系统与Facade对象交互,Facade与遗留代码交互所有复杂的工作。
13)建造者模式
建造者模式(Builder)(又叫生成器模式),将一个复杂对象的构建和表示分离,使得同样的构建过程可以创建不同的表示。
图表10建造者模式(Builder)结构图
●Builder是为创建一个Product对象的各个部件指定的抽象接口;
●ConcreteBuilder是具体的建造者,实现Builder接口,构造和装配各个部件;
●Product是具体的产品;
●Director是指挥者,用来构建一个使用Builder接口的对象。
建造者模式主要用于创建一些复杂的对象,这些对象内部构建间的建造顺序通常是稳定的,但对象内部的构建通常面临着复杂的变化。
使用建造者模式,用户只需指定需要建造的类型,具体的建造细节不需要知道。
建造者模式使得建造代码与表示代码分离,隐藏该产品如何组装,若需要改变一个产品的内部表示,只需要再定义一个具体的建造者就可以了。
建造者模式可以将一个产品的内部表象与产品的生成过程分割开来,从而可以使一个建造过程生成具有不同的内部表象的产品对象。
建造者模式使用:
当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
14)观察者模式
观察者模式(Observer)(又叫做发布-订阅‘Publish/Subscribe’模式),定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。
这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
图表11观察者模式(Observer)结构图
●Subject类,可翻译为主题或抽象通知者,一般用一个抽象类或者一个借口实现。
它把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察者。
抽象主题提供一个接口,可以增加和删除观察者对象。
●Observer类,抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。
这个接口叫做更新接口。
抽象观察者一般用一个抽象类或者一个接口实现。
接口通常包含一个Update()方法,这个方法叫做更新方法。
●ConcreteSubject类,叫做具体主题或具体通知者,将有关状态存入具体观察者镀锡;在具体主题内部状态改变时,给所有登记过的观察者发出通知。
具体主题角色通常用一个具体子类实现。
●ConcreteObserver类,具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。
具体观察者角色可以保持一个指向具体主题对象的引用。
具体观察者角色通常用一个具体子类实现。
将一个系统分割成一系列相互协作的类,需要维护相关对象间的一致性,不希望为了维护一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。
观察者模式的关键对象是主题Subject和观察者Observer,一个Subject可以有任意数目的依赖它的Observer,一旦Subject的状态发生了改变,所有的Observer都可以得到通知。
Subject发出通知时并不需要知道谁是它的观察者,即不需要知道具体的观察者是谁,任何一个具体观察者也不需要知道其他观察者的存在。
使用观察者模式:
1)一个对象的改变需要同时改变其他对象,而且它不知道具体有多少对象有待改变;
2)一个抽象模型有两个方面,其中一方面依赖于另一方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。
观察者模式所作的工作其实就是在解除耦合。
让耦合的双方都依赖于抽象,而不是依赖于具体。
从而使得各自的变化都不会影响另一边的变化。
※委托是一种引用方法的类型,一旦为委托分配了方法,委托将与该方法具有完全相同的行为。
委托方法的使用可以像其他任何方法一样,具有参数和返回值。
委托可以看做是对函数的抽象,是函数的“类”,委托的实例将代表一个具体的函数。
并且一个委托可以搭载多个方法,所有方法被依次唤起,委托方法可以使得委托对象所搭载的方法并不需要属于同一个类。
Java中的委托?
委托的前提,是委托对象所搭载的所有方法必须具有相同的原形和形式,也就是拥有相同的参数列表和返回值类型。
15)抽象工厂模式
抽象工厂模式(AbstractFactory),提供了一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
图表12抽象工厂模式(AbstractFactory)结构图
●AbstractProductA和AbstractProductB是两个抽象产品,ProductA1、ProductA2和ProductB1、ProductB2是对两个抽象产品的具体分类的实现。
●AbstractFactory是一个抽象工厂接口,它里面应该包含所有的产品创建的抽象方法,ConcreteFactory1和ConcreteFactory2是具体的工厂。
●通常是在运行时刻再创建一个ConcreteFactory类的实例,这个具体的工厂再创建具有特定实现的产品对象,即为创建不同的产品对象,客户端应使用不同的具体工厂。
抽象工厂模式优点:
(1)易于交换产品系列,由于具体工厂类,在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,他只需要改变具体工厂即可使用不同的产品配置。
(2)它使具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户端代码中。
抽象工厂模式缺点:
如果需求来自增加功能时,要对抽象工厂的类进行更改。
依赖注入(DependencyInjection),需要专门的IoC容器提供,比如Spring.NET。
?
反射(.NET技术)Java?
,格式:
Assembly.Load(”程序集名称”).CreateInstance(”命名空间.类名称”)
需要在程序顶端写上usingSystem.Reflection;来引入Reflection。
所有用简单工厂的地方,都可考虑用反射技术来去除switch或if,解除分支判断带来的耦合。
用反射+配置文件实现数据库访问程序?
16)状态模式
状态模式(State),当一个对象的内在状态改变时允许改变其行为,这个对象看似改变了其类。
状态模式主要解决的是,当控制一个对象状态转换的条件表达式过于复杂时的情况,把状态的判断逻辑转义到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。
如果这个状态判断很简单,就没必要使用“状态模式”。
图表13状态模式(State)结构图
●State类,抽象状态类,定义一个接口以封装与Context的一个特定状态相关的行为。
●ConcreteState类,具体状态,每一个子类实现一个与Context的一个状态相关的行为。
●Context类,维护一个ConcreteState子类的实例,这个实例定义当前的状态。
状态模式的优点:
将与特定状态相关的行为局部化,并且将不同状态的行为分割开来。
即将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于某个ConcreteState中,所以通过定义新的子类可以很容易地增加新的状态和转换。
这样做的目的是为了消除判断的条件分支语句,状态模式通过把各种状态转移逻辑分布到State的子类之间,来减少互相间的依赖。
使用状态模式:
当一个对象的行为取决于它的状态,且它必须在运行时刻根据状态改变它的行为时。
17)适配器模式
适配器模式(Adapter),将一个类的接口转换成客户希望的另外一个接口。
Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
图表14适配器模式(Adapter)结构图
●Target,客户所期待的接口,目标可以是具体的或抽象的类,也可以是接口;
●Adaptee,需适配的类;
●Adapter,通过在内部包装一个Adaptee对象,把源接口转换成目标接口。
也就是说,需要的东西就在面前,但却不能使用,而短时间又无法改造它,于是就想办法适配它。
在软件开发中,在系统的数据和行为都正确,但接口不符时,应考虑用适配器,目的是使控制范围之外的一个原有对象与某个接口匹配。
适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境不一致的情况。
在GoF的设计模式中,对适配器模式有两种类型,类适配器模式和对象适配器模式。
使用适配器模式:
在使用一个已经存在的类,但如果它的接口,也就是它的方法和你的要求不相同时;即两个所做的事情相同或相似,但是具有不同的接口时使用。
客户代码可以统一调用同一接口,使得程序更简单、更直
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 大话设计模式 大话 设计 模式 自学 笔记