天天看点

《Head First Design Pattern 》--设计模式 读书笔记

                   马上校招就要开始了,我看了下我在简历上写下了如下:

熟练掌握面向对象编程,熟练掌握C/C++,了解STL,熟悉Python等脚本语言。

基础扎实,熟悉常见的数据结构和算法、操作系统、计算机网络、设计模式。

熟悉Linux系统基本操作、SQL Server等数据库操作,了解TCP/IP通信协议。

英语听说读写熟练,CET-6 

                   对照着看下,好像就设计模式这部分心里没底,以前也大致扫了下《大话设计模式》,说实话,看了前两章感觉还不错,可是越看越看不下去,不知所云,但是看了一些笔试面试面经发现好多面试官问应聘问懂不懂一些基础的设计模式,所以对常用的设计模式还是要掌握了解下的,所以我打算还是好好看下《[Head First Design Pattern》和《大话设计模式》这两本书,以争取对常用的设计模式有所了解来应付下面试中会涉及到一些基础的有关设计模式方面的问题:

以往是代码复用,现在是经验复用。

当设计“维护”时候,为了复用(reuse)目的而使用的继承,解决并不完美,回导致很多问题,有此问题后,导致了接口的产生,利用接口可以把要改变的剔除出来独立成一个类接口,但是这样会导致重复代码变多不能复用(接口的缺点),这样做实质上是一个恶梦跳进另外一个恶梦,所以此时需要用到设计模式带你走出此苦难......(ps:设计模式可以这样完美的解决:类似于接口,但是接口下面定义各个行为的实现类,这样就不用在子类中实现接口了。哈哈......)

哈哈,设计模式此时闪亮登场了.........

面向对象编程中贯穿的一些设计原则:

设计原则一:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。(这几乎是每个设计模式背后的精神所在,所有的模式都提供了一套方法让“系统中的某些部分改变而不会影响到其他部分”)

设计原则二:针对接口编程,而不是针对实现编程。

设计原则三:多用组合,少用继承。(组合建立系统具有很大的弹性)

1.策略模式:定义了算法族,分别封装起来,让它们之间可以相互替换,此模式可以让算法变化可以独立于使用算法的客户。

设计模式比库的等级更高,设计模式告诉我们如何组织类和对象以解决某种问题。

我认为设计模式其实就是利用OO设计原则(当然这只是通俗易懂的理解,实际要比这个复杂,呵呵)

在书上看到这样的一句话,感觉很好----设计是一门艺术,总有血多可取舍的地方,但是如果你能采用这些经过深思熟虑,且经受过实际考验的设计模式,你就领先别人了。

良好的OO设计必须具备可复用,可扩充,可维护三个特性。模式不是代码,而是针对问题的通用解决方案,你可以把它们应用到特定的应用中去。

2.观察着模式(observer)

让你的对象知悉现况,观察着模式是JDK中使用最多的模式之一,非常有用。

通俗理解:出版着(主题subject),订阅着(观察着observer)=观察着模式

在真实的世界中,你通常会看到观察者模式被定义成:观察者模式定义了对象之间的一对多依赖,这样依赖,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

《Head First Design Pattern 》--设计模式 读书笔记

松耦合的威力:

当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节,观察者模式提供了一种对象涉及,让主题和观察者之间松耦合。

设计原则4:为了交互对象之间的松耦合设计而努力。(松耦合的设计能让我们建立更有弹性的OO系统)

java中有内置的观察者模式,java.util包内包含最基本的Observer接口(相当于observer接口)和observable类(相当于subject接口)

在JDK中,有很多地方都用到了观察者模式,比如:在swing API:JBtuton的超类AbstractButton,会看到许多增加和删除倾听者(listener)的方法,这些方法可以让观察者感应到Swing组件的不同类型的事件。

3.装饰者模式

尽管继承很强大,但是它并不总是能够实现最有弹性和最好维护的设计。

可以利用组合(composition)和委托(delegation)可以在运行时具有继承行为的效果。

代码应该如同晚霞中的莲花一样地关闭(免于改变),如同晨曦中的莲花一样地开发(能够扩展)

设计原则5:类应该对扩展开发,对修改关闭。(其实这听起来是矛盾的,但是有些聪明的技巧还是可以实现的,比如借助设计模式)

我们的目标是允许类容易扩展,在不修改现有代码的情况下,就可以搭配新的行为。

装饰者模式:动态地将责任附加到对象上,若要扩展功能,装饰者提供了比几次更有弹性的替代方案。

装饰者该做的事就是增加行为到被包装对象上。装饰者模式一味这一群装饰者类,这些类用来包装具体组件。

装饰者模式的缺点:虽然能为系统设计注入弹性,但是会在设计中加入大量的小类,这偶尔会导致别人不太容易理解我的设计方式。拿JAVA I/O库来说,人们第一次接触这个库时候旺旺无法轻易地理解它,但是如果他们能认识到这些类都是用来包装InputStream的,一切都会变得简单很多。

I/O的相关类都是装饰者,

4.工厂模式

简单工厂,虽然不是真正的设计模式,但仍不不失为一个简单方法,可以将客户程序成具体类解耦。

针对接口编程,可以隔离掉以后系统可能发生的一大堆改变。为什么呢?如果代码是针对接口而写的,那么通过多态,它可以 与任何新类实现该接口。但是,当代码中使用大量的具体类时,等于是自找麻烦,应为一旦加入一个新的具体类,就必须改变代码。也就是说,你的代码并非“对修改关闭”。想用心的具体类类扩展代码,就必须重新打开它,所以,该怎么办?遇到这样的问题时候,就应该回到OO设计原则去寻找线索。

我们把类中创建对象的代码从类中抽离,放到一个新的对象中,我称这个新对象为“工厂”。工厂(factory)处理创建对象的细节。

所有工厂模式都用来封装对象的创建,工厂方法模式(Factory Method Pattern)通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。(哈哈,这里加入下本人的理解,工厂模式和简单工厂模式的区别在于:原本有一个对象负责所有具体类的实例化,现在通过一些小的转变,变成由一群子类来负责实例化。更通俗的理解就是工厂方法用来处理对象的创建,并将这一的行为封装在子类中,这样,客户程序中关于超类的代码和子类对象创建代码解耦了。呵呵,更通俗地理解:简单工厂吧全部的事情,在一个地方都处理完了,然而工厂方法却是创建一个框架,让子类决定要如何实现)

工厂方法模式:定义了一个创建对象的接口,但由子类决定实例化的类是哪一个。工厂方法让类把实例化推迟到子类。

设计原则6:要依赖抽象,不要依赖具体类(又叫依赖倒置原则)

这个原则听起来很像“针对接口编程,不针对实现编程”,不是吗?的确很相似,然而这里更强调“抽象”。这个原则说明了:不能让高层组件依赖低层组件,而且,不管高层活低层组件,两者都应该依赖于抽象。

下面的几个知道方针,可以帮助你避免在OO设计中违法依赖倒置原则:

1.变量不可以持有具体类的引用(如果用new,就会持有具体类的引用,你可以改用工厂来避免这样的做法)

2.不要让类派生自具体类(如果派生来自具体类,你就会依赖具体类,请派生自一个抽象(接口或者抽象类))

3.不要覆盖几类中已实现的方法(如果覆盖基类已实现的方法,那么你的基类就不是一个真正适合被继承的抽象,基类中已实现的方法,应该由所有的子类共享)

5.抽象工厂模式

抽象工厂模式:提供一个接口,用于创建相关活依赖对象的家族,而不需要明确指定具体类。

关于工厂模式和抽象工厂模式区别及联系:

这两个模式在把程序成特定现实中解耦方面真的都很有一套,只是做法不同而已。(他们两个都能将对象的创建封装起来,使应用程序解耦,并降低对其特定实现的依赖)

工厂方法模式:
一个抽象产品类,可以派生出多个具体产品类。   
一个抽象工厂类,可以派生出多个具体工厂类。   
每个具体工厂类只能创建一个具体产品类的实例。

抽象工厂模式:
多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。   
一个抽象工厂类,可以派生出多个具体工厂类。   
每个具体工厂类可以创建多个具体产品类的实例。   
    
区别:
工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。   
工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。      

6.单件模式

其实实现一个类只有一个实例化,可以定义一个全局变量就可以,但是单件模式实现不仅给我们一个全局的访问点,和全局变量一样方便,又没有全局变量的缺点(啥缺点?比方说,如果将对象赋值给一个全局变量,那么你必须在程序一开始就创建好对象对吗?网易这个对象非常耗费资源,而程序在这次的执行过程中又一直没用到它,这不就形成浪费了吗?但是利用单件模式,我们可以在需要时候才创建对象,呵呵)

单件模式的源码剖析--我们可以发现这个类没有公开的构造器,只能在类中构造。哈哈,此类中有个getInstance()静态方法,外人要取得它的实例化,他们必须要请求得到一个实例,而不是自行实例化得到一个实例,调用这个静态方法,它就立刻现身,随时可以工作。

单件模式:确保一个类只有一个实例,并提供一个全局访问点。

单件模式在多线程下会出问题,可以通过增加synchonized关键字到getInstance()方法中,我们迫使每个线程在进入这个方法之前,要等候别的线程离开该方法,也就是说,不会有两个线程可以同时进入这个方法,但是似乎同步的getInstance()的做法将拖垮性能,可以使用急切实例化和双重检查加锁方法来改善。

7.命令模式

这个模式没怎么看懂,大致是由餐厅点餐例子,点餐者,服务员,和厨师,通过命令模式,可以使服务员和厨师不需要交流(解耦),只需通过订单交流就行了。

个人理解:这个模式允许我们动作封装成命令对象,这样一来就可以随心所欲地储存、传递和调用它们。(当需要将发出请求的对象和执行请求的对象解耦的时候,使用命令模式)

更通俗地理解:命令模式将发出请求的对象和执行请求的对象解耦,在被解耦的两者之间是通过命令对象进行沟通的,命令对象封装了接受者和一个或一组动作。

命令模式:将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。

8.适配器模式

适配器模式:将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。

尽管定义了这个模式,但是其实实际上有“两种”适配器:”对象“适配器和”类“适配器。对象适配器和类适配器使用了两种不同的适配方法,分别是组合(对象适配器)和继承(类适配器)。

9.外观模式

我们还有一个改变接口的新模式,但是它改变接口的原因是为了简化接口,这个模式呗巧妙地命名为外观模式(Facade-Pattern),之所以这么称呼,是因为它将一个或数个类的复杂的一切都隐藏在背后,只显露出一个干净美好的外观。

模式:                                                意图:

装饰者模式                                         不改变接口,但加入责任

适配器模式                                          将一个接口转换成另外一个接口

外观模式                                              让接口更简单

外观没有”封装“子系统的类,外观只提供简化的接口。所以客户如果觉得有必要,依然可以直接使用子系统的类,这就是外观模式的一个很好的特征:提供简化的接口的同时,依然将系统完整的功能暴露出来,以供需要的人使用。

外观模式:提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。

设计原则7:只和你的密友谈话。(最少知识原则)

这个原则希望我们在设计中,不要让太多的类耦合在一起,免得修改系统中的一部分,会影响到其他的部分。如果许多类之间相互依赖,那么这个系统就会变成一个易碎的系统,它需要花许多成本维护,也会因为太复杂而不容易被其他人了解。

这个原则提供了一些方针:

就任何对象而言,在该对象的方法类,我们只应该调用属于以下范围的方法:

1.该对象本身

2.被当做方法的参数而传递进来的对象

3.此方法所创建活实例化的任何对象

4.对象的任何组件

10.模板方法模式(封装算法)

截止目前为止,我们以上的议题都是围绕这封装转;我们已经封装了对象,对象的创建,方法调用,复杂接口等,接下来我们要深入封装算法块,好让子类可以在任何时候都可以将自己挂接进运算里。

模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

设计原则8:别调用(打电话给)我们,我们会调用(打电话给)你。这样可以防止“依赖腐败”的方法

模板方式是一个常见的模式,到处都是,这个模式常见是因为对创建框架来说,这个模式简直棒级了,有框架控制如果做事情,而由你(使用这个框架的人)指定框架算法中的每个步骤的细节。

11.迭代器与组合模式(管理良好的集合)

这个模式我的理解是:就是为了让客户遍历你的对象而又无法窥视你存取对象的方式;学习如何创建一些对象超集合(super collection),能够一口气就跳过某些让人望而生畏的数据结构。

迭代器模式:提供一个方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。

当我面允许一个类不但要完成自己的事情(管理某种聚合),还要同时担负更多的责任(例如遍历)时,我们就给了这个类两个变化的原因,两个?对!就是两个:如果这个集合改变了话,这个类也必须改变;如果我们遍历的方式改变的话,这个类也必须跟着改变。

所以导致了有如下的一个设计原则:

设计原则9:一个类应该只有一个引起变化的原因。

【内聚(cohesion)这个术语你应该听过,它用来度量一个类和模块紧密地达到单一目的或责任。

    当一个模块或者一个类被设计成只支持一组相关功能时,我们说它具有高内聚;反之,当被设计成支持一组不相关的功能时,我们说它具有低 内聚。

内聚是】

12.组合模式

组合模式:允许你将对象组合成树形结构来表现“整体/部分”才层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。

13.状态模式(事物的状态)

基本常识:策略模式和状态模式是双胞胎,在出生时才分开。

通过前面我们可以知道,策略模式是围绕可以互换的算法来创建成功业务的。然而,状态走的是更崇高的路,它通过改变对象内部的状态来帮助对象控制自己的行为。

状态模式:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。

这个模式将状态封装成独立的类,并将动作委托到代表当前状态的对象,我们知道行为会随内部状态而改变。

14.代理模式(控制对象访问)

代理模式:为另一个对象提供一个替身或占位符以控制对这个对象的访问。

远程代理是一般代理模式的一种实现。其实这个模式的变体相当多。

在真实的世界中,代理模式有许多变体,这些变体都有共同点:都会将客户(subject)施加的方法调用拦截下来。

15.复合模式(模式的模式)

模式通常一起使用,并被组合在同一个设计解决方案中。

复合模式在一个解决方案中结合两个或者多个模式,以解决一般或重复发生的问题。

MVC是一种范型!

鼎鼎大名的MVC(Model-View-Controller)就是一个真正的复合模式,它就是你的设计工具箱内最有威力的模式之一。

MVC是复合模式,结合了观察者模式、策略模式和组合模式。

模型使用观察者模式,以便观察者更新,同时保持两者之间解耦。

控制器是视图的策略,视图可以使用不同的控制器实现,得到不同的行为。

视图使用组合模式实现用户界面,用户界面通常组合了嵌套的组件,像面板、框架和按钮。

这些模式携手合作,把MVC模型的三层解耦,这样可以保持干净又有弹性。

适配器模式用来将新的模式适配成已有的视图和控制器。

Model 2是MVC在web上的应用,在Model 2中,控制器实现成Servlet,而JSP/HTML实现视图。

组织设计模式

随着发掘的设计模式数目逐渐增加,有必要将它们分级,好将它们组织起来,以简化我们寻找模式的过程,并让同一群组内的模式相互比较。

在大多数的类目中,模式通常根据某种做法被归为几类哦。最广为人知的分类方式,就是第一个模式类目中所采取的方式,根据模式的目标分成三个不同的类目:创建型、行为型和结构型。

创建型模式:涉及到将对象实例化,这类模式都提供一个方法,将客户从所需要实例化的对象中解耦。

行为型模式:都涉及到类和对象如何交互及分配职责。

结构型模式可以让你把类或对象组合到更大的结构中。

模式还有另外一种分类方式:模式所处理的是类还是对象。

类模式描述类之间的关系如何通过继承定义。类模式的关系是在编译时建立的。

对象模式描述对象之间的关系,而且主要是利用组合定义。对象模式的关系通常在运行时建立,而且更加动态、更有弹性。

我的理解:其实分类并不重要,分类只是起方便我们记忆而已,重要的是了解这些模式和它们之间的关系。只要类目有帮助,我们就用它,反正就不用。

最后就是 用模式去思考。。。。。呵呵。。。完毕!

再多说下,不要急切于使用模式,而是致力于最能解决问题的简单方案。

总结下:

模式:                                                       特点:

策略                                                             封装可互换的行为,然后使用委托来决定要采用哪一个 行为

适配器                                                          改变一个或多个类看接口

迭代器                                                          提供一个方式来遍历集合,而无须暴露集合的实现

外观                                                               简化一群类的接口

组合                                                               客户可以将对象的集合以及个别的对象一视同仁

观察者                                                           当某个状态改变时,允许一群对象能被通知到

工厂方法                                                      哪个具体类

模板方法                                                     子类决定如果实现算法中的某些步骤

状态                                                             封装基于状态的行为,并将行为委托到当前状态 代理                                                              包装对象,以控制对词对象的访问 装饰者                                                          包装一个对象,以提供新的行为 观察者                                                           让对象能够在状态改变时被通知 组合                                                                客户用一致的方式处理对象集合和单个对象 单件                                                                 确保有且只有一个对象被创建 抽象工厂                                                         允许客户创建对象的家族,而无需指定他们的具体类

命令                                                                 封装请求成为对象                                                        

继续阅读