设计模式 - Design Patterns

来源:互联网 发布:js 在线预览word文档 编辑:程序博客网 时间:2024/03/29 18:32

这里写图片描述
上图为GoF(“四人帮”,又称Gang of Four,即Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides四人)

本文基本内容都来源于网络,我试图汇聚提炼其为一篇:清晰易懂的《概述纪要》。

一、 什么是设计模式?

  • 官方点说:

    在软件工程中,设计模式是对软件设计中普遍存在、反复出现的各种问题,所提出的解决方案

  • 好理解点说:

    设计模式是写代码时的一种思路。也就是说,哎,我可以这样写。

  • 再详细点说:

    设计模式不是创造发明,它只是对某类问题的经验总结,并在此基础上给出的解决问题的最优思路。
    它只是在教你代码怎么写,所以,也可能你自己没学过设计模式时就已经想到了某种设计模式那样的写法了。

二、历史由来

正史

  1. 1977/79年,建筑师 - 克里斯托佛·亚历山大(Christopher Alexander)编制了一本汇集建筑领域设计模式的书。当时的他一定不曾想到,这种设计模式的思想在若干年后竟对软件开发领域里产生了极其深刻的影响。

  2. 1987年,肯特·贝克 沃德·坎宁安 利用了亚历山大 的设计模式思想,把它们应用在了Smalltalk 中的图形用户接口(GUI )的生成代码中。

  3. 1988年,埃里希·伽玛(Erich Gamma) 在他的苏黎世大学 博士毕业论文中开始尝试把这种思想改写为适用于软件开发。与此同时James Coplien 在1989年至1991年也在利用相同的思想致力于C++ 的开发,而后于1991年发表了他的著作Advanced C++ Programming Styles and Idioms

  4. 还是在1988年,这一天终于到来。Erich Gamma 得到了博士学位,他启程前往了美国。在那里,他与Richard Helm , Ralph Johnson ,John Vlissides 合作出版了:

    《设计模式:可复用面向对象软件的基础》(Design Patterns - Elements of Reusable Object-Oriented Software)

    这里写图片描述

    随后,设计模式的思想很快就传播开来,并深刻影响了之后的软件开发领域。这四位作者也在软件开发领域里以“四人帮”Gang of Four ,简称GoF )而闻名于世。有时,GoF 也会用于代指《设计模式》 这本书。

补充

  1. 亚历山大的理论
    埃里希·伽玛(Erich Gamma )等人在Design Patterns 书中引用的建筑师Christopher Alexander 的原话是:

    Christopher Alexander says, “Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice”.

  2. 再说设计模式
    设计模式并不直接用来完成代码的编写,而是描述在各种不同情况下,要怎么解决问题的一种方案。面向对象设计模式通常以类别或对象来描述其中的关系和相互作用,但不涉及用来完成应用程序的特定类别或对象。设计模式能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免会引起麻烦的紧耦合,以增强软件设计面对并适应变化的能力。

    另外呢,并非所有的软件模式都是设计模式,设计模式特指软件“设计”层次上的问题。还有其它非设计模式的模式,如架构模式。同时,算法不能算是一种设计模式,因为算法主要是用来解决计算上的问题,而非设计上的问题。

三、为什么要学习设计模式?

所以说设计模式是一种思想,那为什么我们要学习思想呢……?

  • 如果你自己只是简单的写一个极其小的代码,做了一个最简单的事情,那么你可能不需要设计模式。
    但当问题的规模上升到一定程度的时候,如果你的代码没有提前规划过结构,那么面对一定会出现的:代码的复用、需求的变更、模块的扩展和维护等等问题时,你一定会欲哭无泪的。

所以,设计模式就是一种思想、一种公式、一种前人的经验总结,它能提前帮你规划好你可能遇到的问题的解决方案;

  • 不知道什么时候开始,设计模式被妖魔化了,大家都说不要用,会让程序臃肿不堪… 我想告诉大家,我觉得最初说这种话的,肯定都是已经完完全全掌握了设计模式的那些人,这是他们在遇到某些只需要简单代码就能解决的问题时,所发的切合实际的片段言论罢了。

所以你一定要学习设计模式,你真要说不需要用,那也得吃过葡萄再说酸吧~

四、分类概括

概述

如上所述,GoF的《Design Patterns: Elements of Reusable Object-Oriented Software》(1995年出版,出版社:Addison Wesly Longman.Inc),第一次将设计模式提升到理论高度,并将之规范化。

该书共提出了23种基本设计模式。

时至今日,在可复用面向对象软件的发展过程中,新的设计模式仍然会不断的出现。

不过,虽然设计模式在不断发展,但可以想到:你能遇到的问题,N年前已经有人遇到过相似的了。所以GoF提出的解决方案已经能解决99%的问题了,所以我们普通开发者只需要学习掌握了他们提出的23种模式就足够了。

分类

Gof在《设计模式》中把设计模式分为3种类型23种:创建型模式、结构型模式、行为型模式,并把它们通过授权、聚合、诊断的概念来描述。

1. 创建型模式(Creational Patterns)5个

  1. Abstract Factory(抽象工厂模式)
    Provide an interface for creating families of related or dependent objects without specifying their concrete classes.

  2. Builder(建造者模式)

  3. Factory Method(工厂模式)

  4. Prototype(原型模式)

  5. Singleton(单例模式)

2. 结构型模(Structural Patterns)7个

  1. 适配器模式(Adapter)
    Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn’t otherwise bacasuse of incompatible interfaces.
    将某个类的接口转换成客户端期望的另一个接口表示。适配器模式可以消除由于接口不匹配所造成的类兼容性问题

  2. 桥接模式(Bridge)
    Decouple an abstraction from its implementation so that the two can vary independently.
    将一个抽象与实现解耦,以便两者可以独立的变化。

  3. 组合模式(Composite)
    把多个对象组成树状结构来表示局部与整体,这样用户可以一样的对待单个对象和对象的组合。

  4. 装饰模式(Decorator)
    向某个对象动态地添加更多的功能。修饰模式是除类继承外另一种扩展功能的方法。

  5. 外观模式(Facade)
    为子系统中的一组接口提供一个一致的界面, 外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

  6. 享元(Flyweight)
    通过共享以便有效的支持大量小颗粒对象。

  7. 代理(Proxy)
    Provide a surrogate or placeholder for another object to control access to it.
    为其他对象提供一个代理以控制对这个对象的访问。
    我的博客:《代理模式 - Proxy Patterns》

3. 行为型模式(Behavioral Patterns)11个

  1. Chain of Responsibility(职责链模式)
  2. Command(命令模式)
  3. Interpreter(解释器模式)
  4. Iterator(迭代器模式)
  5. Mediator(中介者模式)
  6. Memento(备忘录模式)
  7. Observer(观察者模式)
  8. State(状态模式)
  9. Strategy(策略模式)
  10. Template Method(模版方法模式)
  11. Visitor(访问者模式)

下面部分再从理论上论述下设计模式

五、设计原则

首先说,为什么要提倡Design Pattern 呢?

根本理论原因是:为了代码复用(Reusable),增加可维护性。

那么,怎么才能实现代码复用呢?

简单来讲,就是遵守面向对象的6个基本原则:

  1. 开闭原则(Open Closed Principle,OCP)

  2. 里氏代换原则(Liskov Substitution Principle,LSP)

  3. 依赖倒转原则(Dependency Inversion Principle,DIP)

  4. 接口隔离原则(Interface Segregation Principle,ISP)

  5. 合成/聚合复用原则(Composite/Aggregate Reuse Principle,CARP)

  6. 最小知识原则(Principle of Least Knowledge,PLK,也叫迪米特法则)

这其中,开闭原则具有理想主义的色彩,它是面向对象设计的终极目标。而其它几条,则可以看做是开闭原则的实现方法。

所以,设计模式就是实现了这些原则,从而达到了代码复用、增加可维护性的目的。

1、开闭原则(Open Closed Principle)

  1. 内容此原则是由Bertrand Meyer 提出的,原文是:

    Software entities should be open for extension, but closed for modification。
    开放括展,拒绝修改

    本原则是面向对象设计中“可复用设计”的基石,是最重要的原则,其它原则是实现开闭原则的手段。
    具体到项目中来说:就是对组件功能的扩展开放,而对修改原有代码的关闭。模块应尽量在不修改原代码的情况下进行扩展。

  2. 表现

    举一个工厂模式(factory pattern)的例子

    假设中关村有一个卖毛片盘和盗版盘(PirateDisc)的小贩(Vendor),我们给他设计一个“光盘销售管理软件”。

    首先,我们应该先设计一“光盘(Disc)”接口。而毛片盘和盗版盘是其子类。小贩通过“DiscFactory”来管理这些光盘。代码为:

    public class DiscFactory{    public static Disc getDisc(String name){        return (Disc)Class.forName(name).newInstance();    }}

    接着,有人要买盗版盘,怎么实现呢?

    public class Vendor{    public static void main(String[] args){        Disc d = DiscFactory.getDisc("PirateDisc");        d.sell();    }}

    然后,如果有一天,这小子良心发现了,开始卖正版盘(GenuineDisc)怎么办?
    没关系,我们只要再创建一个“光盘”的子类“正版盘(GenuineDisc)”就可以了,不需要修改原结构和代码。

    (工厂模式是对具体产品进行扩展,有的项目可能需要更多的扩展性,要对这个“工厂”也进行扩展,那就成了“抽象工厂模式”)

    以上,我们的代码做到了:对扩展开放,对修改关闭 —— 符合了开闭原则,也能很好的复用了。

  3. Tips
    OCP 具有理想主义色彩,它是面向对象设计的终极目标,但因为其要求在开发前就将所有可预见情况按照原则设计是不可能的。所以,针对开闭原则的实现方法,一直都有大师费尽心机去实现,后面的原则都可以看做其实现。

2、里氏代换原则(Liskov Substitution Principle)

  1. 内容
    里氏代换原则是由Barbara Liskov 提出的,就是说是Liskov**重点内容**提出的置换原则

    如果一个面向对象设计是遵守本原则的,那么有:任何基类可以出现的地方,子类一定可以出现

    这个原则很严格,再用一句话说就是:“子类必须能够替换基类,否则不应当设计其为子类

  2. 表现
    表现在代码里,如果调用的是父类的话,那么换成子类也完全可以运行。比如:

    Disc d = new PirateDisc();d.sell();

    此时,可以将“盗版盘”类改为“毛片”类吗?
    没问题,完全可以运行。Java编译器会检查程序是否符合里氏代换原则

    还记得java继承的一个原则吗?

    子类override的方法的访问权限不能小于父类对应方法的访问权限。

    那么,现在知道里氏代换原则就明白了:如果子类权限小的话就不能保证代换原则了

    比如“Disc”中的方法“sell()”访问权限是“public”,那么子类“PirateDisc”和“毛片”中的“sell()”方法就不能是protected或private,编译不能通过

    为什么要这样呢?你想啊:如果“盗版盘”的“卖”方法是private。那么上面的这段代码就不能执行了:

    Disc d = new PirateDisc();d.sell();

    可以说:里氏代换原则是继承复用的一个基础。

3、依赖倒转原则(Dependence Inversion Principle)

  1. 内容
    在传统结构化编程中,最上层的模块通常都要依赖下面的子模块来实现,那么依赖倒置就是说:逆转这种依赖,高层不应该依赖于低层。

    高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。抽象不应该依赖于具体,具体应当依赖于抽象

    一句话:设计要依赖于抽象而不是具体。不要一上来就设计具体的类,要抽象的想。

  2. 表现
    如果没有任何继承关系存在,就会导致具体业务代码(高层)依赖于底层具体模块。此时,只要具体模块要增删改变,高层也要改变。

    应该:

    1. 传递参数,或者在组合聚合关系中,尽量引用层次高(祖先)的类。

    2. 主要是在构造对象时可以动态的创建各种具体对象,当然如果一些具体类比较稳定,就不必在弄一个抽象类做它的父类,这样有画蛇添足的感觉。

四、接口隔离原则(Interface Segregation Principle)

  1. 内容

    要使用多个专门的接口。每一个接口对应且仅对应一种角色。

  2. 表现
    如果不是接口隔离,那么就会出现这种情况:

    使用此接口的客户会面临由于这些不使用的方法的改变所带来的改变。所以Java 要能够实现多个接口

五、合成/聚合复用(Composite/Aggregate Reuse Principle)

  1. 内容
    合成/聚合复用原则(Composite/Aggregate Reuse Principle,CARP)经常又叫做合成复用原则

    合成/聚合复用原则就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过向这些对象的委派达到复用已有功能的目的。就是说要少用继承,多用合成关系来实现

    一句话:设计者首先应当考虑合成/聚合,而不是继承。实践中合成/聚合会带来比继承更大的利益。

  2. 表现
    有几个类要与数据库打交道,就写了一个数据库操作的类,然后别的跟数据库打交道的类都继承这个。结果后来,我修改了数据库操作类的一个方法,各个类都需要改动。“牵一发而动全身”!面向对象是要把波动限制在尽量小的范围。

    Java 中,应尽量针对Interface编程,而非实现类。这样,更换子类不会影响调用它方法的代码。要让各个类尽可能少的跟别人联系,“不要与陌生人说话”。扩展性和维护性才能提高。

六、最少知识原则

  • 内容

    也叫迪米特法则。不要和陌生人说话,即一个对象应对其他对象有尽可能少的了解。这是一个如何低耦合(Loosely-Coupled)的法则。

    一句话:一个对象应当尽可能少的去了解其他对象。



参考:
wikipedia维基百科 - Design_Patterns设计模式
wikipedia维基百科 - 设计模式_(计算机)


说完了设计模式以及其设计原则,接下来我会将基本的23种设计模式都用博文介绍一遍,敬请期待啦~

2 0
原创粉丝点击