设计模式(Design Pattern),是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 设计模式于己、于他人、于系统都是多赢的,设计模式使代码编制真正工程化。设计模式是软件工程的基石,如同大厦的一块块砖石一样使代码编制真正工程化。
只有精通了设计模式,才敢说真正理解了软件工程。可以说,设计模式是每一个架构师所必备的技能之一。作为一个面向对象设计程序员,只有精通了设计模式,才能摆脱码奴的命运,成为一个真正的软件工程师,才能完成自身价值的飞跃和设计思想的升华!
1、设计模式和框架
可复用面向对象软件系统现在一般划分为两大类:应用程序工具箱和框架(Framework),我们平时开发的具体软件都是应用程序,Java的API属于工具箱;而框架是构成一类特定软件可复用设计的一组相互协作的类,EJB(EnterpriseJavaBeans)是Java应用于企业计算的框架。
框架通常定义了应用体系的整体结构类和对象的关系等等设计参数,以便于具体应用实现者能集中精力于应用本身的特定细节。框架主要记录软件应用中共同的设计决策,框架强调设计复用,因此框架设计中必然要使用设计模式。 另外,设计模式有助于对框架结构的理解,成熟的框架通常使用了多种设计模式,如果你熟悉这些设计模式,毫无疑问,你将迅速掌握框架的结构,我们一般开发者如果突然接触EJBJ2EE等框架,会觉得特别难学,难掌握,那么转而先掌握设计模式,无疑是给了你剖析EJB或J2EE系统的一把利器。
2、设计模式的原则
近年来,大家都开始注意设计模式。那么,到底我们为什么要用设计模式呢?为什么要提倡设计模式呢?
根本原因是为了摆脱编程低效率,提高代码复用,增强代码健壮稳定,增加可维护性。
那怎么才能实现代码复用呢?
从面向过程到面向对象,是软件设计诞生以来的迈出的最伟大的一步,面向对象程序设计成功解决了面向过程软件编程的低效率问题,并且彻底改变了人们的编程思维,为软件设计揭开了新的篇章。然而,要实现面向对象设计,彻底摆脱面向过程设计思维,并不仅仅是只要使用了一门面向对象的编程语言就能够达到的。使用面向对象设计,可以设计出优秀的软件,同样也可以设计出糟糕的软件。只有遵循一些特定的原则,才能设计出复用性高灵活性好的软件来。
在运用面向对象的思想进行软件设计时,需要遵循的原则一共有 6 条:
在软件设计的过程中,只要我们尽量遵循以上六条设计原则,设计出来的软件才会是一个优秀的软件,它必定足够健壮、足够稳定,并以极大的灵活性来迎接随时而来的需求变更等因素。
3、设计模式的四要素
设计模式使人们可以更加简单方便地复用成功的设计和体系结构。将已证实的技术表述成设计模式也会使新系统开发者更加容易理解其设计思路。
4、设计模式分类概览
《设计模式》一书,第 1 次将设计模式提升到理论高度,并将之规范化。书中一共总结了23种基本的设计模式,《设计模式》下载
这23种设计模式,几乎涵盖了面向对象设计过程中所有问题的解决方案,书中提到的23种设计模式分别是:
设计模式 (Design Pattern) |
||
创建型 (Creational) |
结构型 (Structural) |
行为型 (Behavioral) |
1、Abstract Factory(抽象工厂) 2、Builder(建造者) 3、Factory Method(工厂方法) 4、Prototype(原型) 5、Singleton(单态) |
6、Adapter(适配器) 7、Bridge(桥模式) 8、Composite(组合) 9、Decorator(装饰) 10、Façade(外观) 11、Flyweight(享元) 12、Proxy(代理) |
13、Chain of Responsibility(职责链) 14、Command(命令) 15、Interpreter(解释器) 16、Iterator(迭代器) 17、Mediator(中介) 18、Memento(备忘录) 19、Observer(观察者) 20、State(状态) 21、Strategy(策略) 22、Template Method(模板方法) 23、Visitor(访问者) |
5、设计模式的设计场景
那么如此多的设计模式又是从何而来呢?
《易经》有云:“易有太极,是生两仪,两仪生四象,四象生八卦”,意思就是说世界万物皆有起源。
设计模式的起源,是面向对象程序设计思想,是面向对象设计的精髓——抽象。面向对象通过类和对象来实现抽象,实现时产生了面向对象的三个重要机制:封装、继承、多态,正是这三个机制衍生出了各种各样的设计模式。
设计模式的支持设计 |
||
目的 |
设计模式 |
可变方面 |
创建 (Creational) |
1、Abstract Factory 2、Builder 3、Factory Method 4、Prototype 5、Singleton |
产品对象家族 如何创建一个组合对象 被实例化的子类 被实例化的类 一个类的唯一实例 |
结构 (Structural) |
6、Adapter 7、Bridge 8、Composite 9、Decorator 10、Façade 11、Flyweight 12、Proxy |
对象的接口 对象的实现 一个对象的结构和组成 对象的职责,不生成子类 一个子系统的接口 对象的存储开销 如何访问一个对象,该对象的位置 |
行为 (Behavioral) |
13、Chain of Responsibility 14、Command 15、Interpreter 16、Iterator 17、Mediator 18、Memento 19、Observer 20、State 21、Strategy 22、Template Method 23、Visitor |
满足一个请求的对象 何时、怎样满足一个请求 一个语言的文法和解释 如何遍历、访问一个聚合的各元素 对象间如何交互、与谁交互 一个对象中哪些私有信息存放在该对象之外,以及在什么时候进行存储 多个对象依赖于另外一个对象,而这些对象又如何保持一致 对象的状态 算法设计 算法中的某些步骤 某些可作用于一个(组)对象上的操作,但不修改这些对象的类 |
6、设计模式的分类描述
设计模式,分为创建型模式、结构型模式、行为型模式
设计模式的分类描述 |
|
创建型模式 |
|
1.抽象工厂模式 |
为一个产品族提供了统一的创建接口。当需要这个产品族的某一系列的时候,可以从抽象工厂中选出相应的系列创建一个具体的工厂类。 |
2.建造者模式 |
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 |
3.工厂方法模式 |
定义一个接口用于创建对象,但是让子类决定初始化哪个类。工厂方法把一个类的初始化下放到子类。 |
4.原型模式 |
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 |
5.单例模式 |
确保一个类只有一个实例,并提供对该实例的全局访问。 |
5.多例模式 |
确保一个类只有命名的实例,并提供对这些实例的全局访问。 |
对象池模式 |
通过回收利用对象避免获取和释放资源所需的昂贵成本。 |
惰性初始模式 |
推迟对象的创建、数据的计算等需要耗费较多资源的操作,只有在第一次访问的时候才执行。 |
资源获取为初始化 |
通过绑定到合适对象的生命周期来确保资源被适当地释放。 |
结构型模式 |
|
6.适配器模式 |
将某个类的接口转换成客户端期望的另一个接口表示。适配器模式可以消除由于接口不匹配所造成的类兼容性问题。 |
7.桥接模式 |
将一个抽象与实现解耦,以便两者可以独立的变化。 |
8.组合模式 |
把多个对象组成树状结构来表示局部与整体,这样用户可以一样的对待单个对象和对象的组合。 |
9.装饰模式 |
向某个对象动态地添加更多的功能。修饰模式是除类继承外另一种扩展功能的方法。 |
10.外观模式 |
为子系统中的一组接口提供一个一致的界面, 外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。 |
11.享元 |
通过共享以便有效的支持大量小颗粒对象。 |
12.代理 |
为其他对象提供一个代理以控制对这个对象的访问。 |
行为型模式 |
|
13.职责链 |
为解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它。 |
14.命令 |
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。 |
15.解释器 |
给定一个语言, 定义它的文法的一种表示,并定义一个解释器, 该解释器使用该表示来解释语言中的句子。 |
16.迭代器 |
提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。 |
17.中介者 |
包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用,从而使它们可以松散偶合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用,保证这些作用可以彼此独立的变化。 |
18.备忘录 |
备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捉住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。 |
19.观察者模式 |
在对象间定义一个一对多的联系性,由此当一个对象改变了状态,所有其他相关的对象会被通知并且自动刷新。 |
20.状态 |
让一个对象在其内部状态改变的时候,其行为也随之改变。状态模式需要对每一个系统可能取得的状态创立一个状态类的子类。当系统的状态变化时,系统便改变所选的子类。 |
21.策略 |
定义一个算法的系列,将其各个分装,并且使他们有交互性。策略模式使得算法在用户使用的时候能独立的改变。 |
22.模板方法 |
模板方法模式准备一个抽象类,将部分逻辑以具体方法及具体构造子类的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。先构建一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。 |
23.访问者 |
封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改,接受这个操作的数据结构可以保持不变。访问者模式适用于数据结构相对未定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化。 |
空对象 |
通过提供默认对象来避免空引用。 |
黑板 |
广义的观察者在系统范围内交流信息,允许多位读者和写者。 |
规格 |
以布尔形式表示的可重绑定的商业逻辑。 |
在面向对象软件设计的发展过程中,除了《设计模式》一书中提到的23中设计模式之外,新的设计模式仍然不断出现。
若想更进一步了解关于面向对象设计的背景,参考接口模式、内聚、耦合。
若想更进一步了解关于面向对象编程的背景,参考继承,重载,多态,接口
MVC(Model-View-Controller,模型-视图-控制器)是软件工程中的一种软件架构模式,它把软件系统分为三个基本部分:模型(Model)、视图(View)、控制器(Controller)。
MVC不是一种设计模式(Design Pattern),而是一种架构模式(Architectural Pattern),用以描述应用程序的结构以及结构中各部分的职责和交互方式。它最先是在1979年的时候第一次被人提出,不过,当时环境有些不同,网络应用的概念在当时还不存在。
提姆·伯纳斯李( Tim Berners-Lee)在上世纪九十年代初期的时候播种下了万维网(WWW)的种子,并永远的改变了世界。目前我们在网络开发中所采用的这种模式实际上是原版模式的一个改编版。这种架构模式的疯狂流行是由于两个极其流行的开发框架将这种模式包含了进来,它们是: Struts 和 Ruby on Rails。这两个开发框架给稍后诞生的数百框架打上了深深的烙印。1、 MVC 三层架构
模型(Model): 数据模型用于封装与应用程序的业务逻辑相关的数据以及对数据的处理方法。模型有对数据直接访问的权力,例如对数据库的访问。“模型”不依赖“视图”和“控制器”,也就是说,模型不关心它会被如何显示或是如何被操作。但是模型中数据的变化一般会通过一种刷新机制被公布。为了实现这种机制,那些用于监视此模型的视图必须事先在此模型上注册,从而,视图可以了解在数据模型上发生的改变。(比较:软件设计模式中的观察者模式)
视图(View): 视图层能够实现数据有目的的显示(理论上,这不是必需的)。在视图中一般没有程序上的逻辑。为了实现视图上的刷新功能,视图需要访问它监视的数据模型,因此应该事先在被它监视的数据那里注册。
控制器(Controller): 控制器起到不同层面间的组织作用,用于控制应用程序的流程。它处理事件并作出响应。“事件”包括用户的行为和数据模型上的改变。
2、MVC原理
MVC(模型-视图-控制器)架构模式背后的思想非常简单,我们的应用程序中必须区分下面这些职责:
应用程序被分成了三个主要的部分,每个部分负责掌管不同的任务。
3、MVC模式的网络应用
控制器(Controller)
控制器掌管着用户的请求(当用户点击图形用户界面(GUI)上的元素执行操作时,控制器会收到HTTP GET或者POST请求)。它的主要功能就是调用并协调需要的资源/对象来执行用户请求。通常控制器会为任务调用合适的模型,以及选择合适的视图。
模型(Model)
模型是指运用于数据之上的数据规则和数据内容,它一般对应于应用程序所要管理的对象。在软件系统中,任何事物都可以被抽象成可以对其以某种方式进行处理的数据模型。应用程序中的用户,信息以及图书是什么?它们只是一堆必须按照对应规则处理的数据(日期不能是未来的日期,电子邮件有特定的格式,名字的长度不能超过多少字符等等)。
模型给控制器提供了一个用户请求内容对应的数据表达(比如信息,书,相册)。不管我们如何向用户展示,这个数据模型都不会变。这也是我们为什么可以随意选择使用哪个视图来展示数据的原因。模型包含我们应用程序逻辑中最重要的组成部分,这些逻辑运用于我们要处理的问题过程中。控制器更多的是包含应用程序自身的内部组织逻辑。
视图(View)
视图提供了展示模型数据的不同方式。它可能是数据填充的模板。视图可以有多个,而控制器则决定使用哪个视图。一个网络应用通常由许多控制器,模型和视图组成。控制器可以被看成是一个主控制器,用于接收用户的所有请求,然后在调用特定的控制器来处理不同的情况。
4、MVC 优点
MVC的一个最明显好处就是它将视图展示和应用逻辑清晰的分离开来。
对不同用户以及不同设备类型的支持一直是当下的一个常见问题,例如:
来自台式电脑和手机的请求所得到的视图应该是不相同的,模型会返回完全相同的数据,但是不同的地方是控制器会选择使用的视图文件来展示数据(我们可以把它看作是不同的模板)。
除了将视图从业务逻辑中分离开外,MVC的分离也降低了大型应用设计的难度,代码也更具结构性,因此也更容易维护,测试和重用。
5、MVC 适用场景
MVC模式的缺点是由于它没有明确的定义,所以完全理解MVC模式并不是很容易。使用MVC模式需要精心的计划,由于它的内部原理比较复杂,所以需要花费一些时间去思考。开发一个MVC模式架构的工程,将不得不花费相当可观的时间去考虑如何将MVC模式运用到应用程序中,同时由于模型和视图要严格的分离,这样也给调试应用程序带来了一定的困难。每个构件在使用之前都需要经过彻底的测试。另外由于MVC模式将一个应用程序分成了三个部件,所以这意味着同一个工程将包含比以前更多的文件。
過去MVC模式并不适合小型甚至中等规模的应用程序,这样会带来额外的工作量,增加应用的复杂性。但現在多數軟體設計框架,能直接快速提供MVC骨架,供中小型應用程序開發,此問題不再存在。对于开发存在大量用户界面,并且逻辑复杂的大型应用程序,MVC将会使软件在健壮性、代码重用和结构方面上一个新的台阶。尽管在最初构建MVC模式框架时会花费一定的工作量,但从长远的角度来看,它会大大提高后期软件开发的效率。
6、 MVC 实现示例
1)MFC
MFC(Microsoft Foundation Classes) Document/View架构,是微软早期对于MVC模式的实现,MFC将工程分成CView 和 CDocument 两大类,其中的Document对应MVC中的Model,View相当于MVC中的View+Controller,再加上CWinApp类别,合成三大项,但是MFC基本上是一个失败的MVC模式作品。
由于MFC之下的Document/View定义过于模糊,未将Controller(MessageMap)部份取出,因此Controller可以置入View或Document,但不管置入哪一方面,都会与View或Document绑死,没有弹性。
2)Swing
Swing,是一个标准的MVC结构。ComponentUI代表View,负责描画组件。组件尤其Model层,比如JTextField的Document, JTable的TableModel, JTree的TreeModel等等。
而Control可能不是很明显,我们或许可以简单的将其Event机制看作一个Swing团队开发给开发者的Controller。
作为Java开发者, 如果想理解MVC的结构,学习Swing的确是个不错的选择。
3).NET(ASP.NET、VB.NET、C#.NET)
ASP.NET,针对视图(View)和控制器(Controller)的模式没有被很好地定义,而模型(Model)则留给开发者去设计。
ASP.NET 不严格需要一个模型,开发者可以自行选择创建一个模型类,但是很多人选择放弃这一步,直接把事件处理放在控制器里处理任何计算、数据保存等等。但用模型来包含商业逻辑和数据存取是可实现的。
4)Qt Design
Qt,是一个跨平台的C++应用程式开发框架,广泛用于开发GUI程式,这种情况下又被称为部件工具箱,也可用于开发非GUI程式,比如控制台工具和服务器。
Qt Design 开发界面类似于ASP.NET,把视图(View)和控制器(Controller)隔离看来,模型(Model)由设计者自己设计。