当前位置:   article > 正文

【JAVA 】 23种设计模式详解_java设计模式

java设计模式

目录

JAVA 设计模式总结

前言

一、设计模式是什么?

二、设计模式分类

 1.   创建型

  2.   结构型

  3.   行为型

三、设计原则

1、开闭原则-OCP(Open Close Principle)

2、单一职责原则-SRP(Single Responsibility Principle)

3、里氏替换原则-LSP(Liskov Substitution Principle)

4、依赖倒转原则-DIP(Dependence Inversion Principle)

5、接口隔离原则-ISP(Interface Segregation Principle)

6、迪米特法则-LKP(Least Knowledge Principle)

 

 创建型详细

 结构型详细

 行为型详细

总结


JAVA 设计模式总结

所有见解都来自阅读《java 设计模式》一书而来,不足之处 ,多指点。

 

前言

设计模式是源于早期建筑行业中使用,随后引用到计算机工程中,所以模式命名风格不一样;

目前java 中用到的设计模式一共三大类共23种,不管平时工作 还是面试过程中都会涉及到设计思想,而这也正是程序开发者的基本思想,设计模式的原理就是面向接口编程,不是面向实现。设计模式的目标:降低耦合,增强灵活性;


提示:以下是本篇文章正文内容,下面案例可供参考

一、设计模式是什么?

设计模式(Design Pattern)是一套被反复使用、多数人知晓、经过分类编目的优秀代码设计经验的总结。使用设计模式是为了重用代码、使代码更易理解并保证代码的可靠性。设计模式的原理就是面向接口编程,不是面向实现的。设计模式的目标:降低耦合,增强灵活性;

二、设计模式分类

 1.   创建型

   创建型模式是用来创建对象的模式,抽象了实例化的过程,帮助一个系统独立于其关联对象的创建、组合和表示方式。所有的创建型模式都有两个主要功能:

        ■ 将系统所使用的具体类的信息封装起来;

        ■ 隐藏类的实例是如何被创建和组织的。外界对于这些对象只知道它们共同的接口,而不清楚其具体的实现细节。

      正因为以上两点,创建型模式在创建什么(what),由谁(who)来创建,以及何时(when)创建这些方面,都为软件设计者提供了尽可能大的灵活性。

常见的创建型设计模式有下列五种:

        ■ 单例模式(Singleton Pattern):一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

        ■ 工厂方法模式(Factory Pattern):在工厂方法模式中,工厂类成为了抽象类,实际的创建工作将由其具体子类来完成。工厂方法的用意是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类中去,强调的是“单个对象”的变化。

        ■ 抽象工厂模式(Abstract Factory):抽象工厂是所有工厂模式中最为抽象和最具有一般性的一种形态。抽象工厂可以向客户提供一个接口,使得客户可以在不必指定产品具体类型的情况下,创建多个产品族中的产品对象,强调的是“系列对象”的变化。

        ■ 建造者模式(Builder Pattern):把构造对象实例的逻辑移到了类的外部,在类的外部定义了该类的构造逻辑。它把一个复杂对象的构造过程从对象的表示中分离出来,其直接效果是将一个复杂的对象简化为一个比较简单的目标对象,强调的是产品的构造过程。

        ■ 原型模式(Prototype Pattern):原型模式和工厂模式一样,同样对客户隐藏了对象创建工作,但与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过复制一个现有对象生成新对象。

  2.   结构型

    结构型模式讨论的是类和对象的结构,它采用继承机制来组合接口或实现(类结构型模式),或者通过组合一些对象实现新的功能(对象结构型模式)。这些结构型模式在某些方面具有很大的相似性,但侧重点却各有不同;

   常见的结构型设计模式有以下七种:

       ■ 代理模式(Proxy):为其他对象提供一种代理以控制对该对象的访问。

       ■ 装饰模式(Decorator):动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。

       ■ 适配器模式(Adapter):将一个类的接口变换成客户端所期待的另一接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。

       ■ 组合模式(Composite):也叫合成模式,将对象组合成树形结构以表示“部分—整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

       ■ 桥梁模式(Bridge):也叫桥接模式,将抽象和实现解耦,使得两者可以独立变化。

       ■ 外观模式(Facade):也叫门面模式,要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行,外观模式提供一个高层次的接口,使得子系统更易于使用。

       ■ 享元模式(Flyweight):是池技术的重要实现方式,使用共享对象可有效地支持大量的细粒度的对象。

  3.   行为型

   行为型设计模式关注的是对象的行为,用来解决对象之间的联系问题。

   常见的行为型设计模式有以下11种:

       ■ 模板方法模式(Template Method):定义一个操作中的算法的框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

       ■ 命令模式(Command):是一种高内聚的模式,将一个请求封装成一个对象,从而使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。

       ■ 责任链模式(Chain of Responsibility):使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。

       ■ 策略模式(Strategy):也叫政策模式,定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。

       ■ 迭代器模式(Iterator):提供一种方法访问一个容器对象中的各个元素,而又不需要暴露该对象的内部细节。

       ■ 中介者模式(Mediator):用一个中介对象封装一系列的对象交互,中介者使各对象不需要显式地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

       ■ 观察者模式(Observer):也叫发布订阅模式,定义对象间的一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。

       ■ 备忘录模式(Memento):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。

       ■ 访问者模式(Visitor):封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。

       ■ 状态模式(State):当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类型,状态模式的核心是封装,状态的变更引起行为的变更。

       ■ 解释器模式(Interpreter):给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该文法表示来解释语言中的句子。

 

三、设计原则

1、开闭原则-OCP(Open Close Principle)

开闭原则就是说对扩展开放,对修改关闭。一个软件实体,如类、模块和函数应该对外扩展开放,对修改关闭。

这个原则说的是,在设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展,即应当可以在不必修改源代码的情况下改变这个模块的行为。在面向对象的编程中,开闭原则是最基础的原则,起到总的指导作用,其他原则(单一职责、里氏替换、依赖倒置、接口隔离、迪米特法则)都是开闭原则的具体形态,即其他原则都是开闭原则的手段和工具。开闭原则的重要性可以通过以下几个方面来体现。

    ■ 开闭原则提高复用性。在面向对象的设计中,所有的逻辑都是从原子逻辑组合而来的,而不是在一个类中独立实现一个业务逻辑,代码粒度越小,被复用的可能性就越大,避免相同的逻辑重复增加。开闭原则的设计保证系统是一个在高层次上实现了复用的系统。

    ■ 开闭原则提高可维护性。一个软件投产后,维护人员的工作不仅仅是对数据进行维护,还可能对程序进行扩展,就是扩展一个类,而不是修改一个类。开闭原则对已有软件模块,特别是最重要的抽象层模块要求不能再修改,这就使变化中的软件系统有一定的稳定性和延续性,便于系统的维护。

    ■ 开闭原则提高灵活性。所有的软件系统都有一个共同的性质,即对系统的需求都会随时间的推移而发生变化。在软件系统面临新的需求时,系统的设计必须是稳定的。开闭原则可以通过扩展已有的软件系统,提供新的行为,能快速应对变化,以满足对软件新的需求,使变化中的软件系统有一定的适应性和灵活性。

    ■ 开闭原则易于测试。测试是软件开发过程中必不可少的一个环节。测试代码不仅要保证逻辑的正确性,还要保证苛刻条件(高压力、异常、错误)下不产生“有毒代码”(Poisonous Code),因此当有变化提出时,原有健壮的代码要尽量不修改,而是通过扩展来实现。否则,就需要把原有的测试过程回笼一遍,需要进行单元测试、功能测试、集成测试,甚至是验收测试。开闭原则的使用,保证软件是通过扩展来实现业务逻辑的变化,而不是修改。因此,对于新增加的类,只需新增相应的测试类,编写对应的测试方法,只要保证新增的类是正确的就可以了。

2、单一职责原则-SRP(Single Responsibility Principle)

不要存在多于一个导致类变更的原因,也就是说每个类应该实现单一的职责,如若不然,就应该把类拆分。

单一职责原则的优点有:

    ■ 降低类的复杂性;

    ■ 提高类的可读性;

    ■ 提高代码的可维护性和复用性;

    ■ 降低因变更引起的风险。

单一职责原则提出了一个编写程序的标准,用“职责”或“变化原因”来衡量接口或类设计是否优良,但“职责”和“变化原因”都是不可度量的,因项目而异,因环境而异。

3、里氏替换原则-LSP(Liskov Substitution Principle)

  基于对继承优缺点出发,有两种定义:

  1. 如果对一个类型为S的对象o1,都有类型为T的对象o2,使得以S定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型T是类型S的子类型。

  2.所有引用基类的地方必须能透明地使用其子类对象。清晰明确地说明只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道父类还是子类;但是反过来则不可以,有子类的地方,父类未必就能适应。

在编译期,Java语言编译器会检查一个程序是否符合里氏替换原则,这是一个无关实现的、纯语法意义上的检查。里氏替换要求凡是使用基类的地方,子类一定适用,因此子类必须具备基类的全部接口。或者说,子类型的接口必须包括全部的基类的接口,而且还有可能更宽。如果一个Java程序破坏这一条件,Java编译器就会在编译程序时抛出错误提示,并停止编译;

里氏替换原则为良好的继承定义了一个规范,它包含4层含义:

    ■ 子类必须完全实现父类的方法;

    ■ 子类可以有自己的个性;

    ■ 覆盖或实现父类的方法时输入参数可以被放大;

    ■ 覆盖或实现父类的方法时输出结果可以被缩小。

 

满足里氏替换原则的设计模式有 策略模式,组合模式,代理模式;

4、依赖倒转原则-DIP(Dependence Inversion Principle)

高层模块不应该依赖低层模块,两者都依赖其抽象;抽象不依赖细节; 细节应该依赖于抽象。传统的过程性系统的设计办法倾向于高层次的模块依赖于低层次的模块;抽象层次依赖于具体层次。

 

在Java语言中,抽象就是指接口或抽象类,两者都是不能直接被实例化的;细节就是具体的实现类,实现类实现了接口或继承了抽象类,其特点是可以直接被实例化。依赖倒置原则在Java语言中的表现是:

    ■ 模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生;

    ■ 接口或抽象类不依赖于实现类;

    ■ 实现类依赖于接口或抽象类。

依赖倒置原则更加精确的定义就是“面向接口编程”——OOD(Object-Oriented Design)的精髓之一。依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,降低并行开发引起的风险,提高代码的可读性和可维护性。依赖倒置原则是JavaBean、EJB和COM等组件设计模型背后的基本原则。

5、接口隔离原则-ISP(Interface Segregation Principle)

接口隔离原则 是对接口的明确规范,此处的接口两层含义:一种是逻辑上的实例接口,只要我们实例化的类 都可以称这为内部方法业务的接口;第二种,传统意义上,按照标准规范创建的类,使用 interface 关键字修饰的类,称之为接口;

所以延伸出两种定义:

 1.客户端不应该依赖它不需要的接口;

2. 类间的依赖关系应该建立在最小的接口上;

针对定义分析含义:

      ■ 一个类对另外一个类的依赖性应当是建立在最小的接口上的。

      ■ 一个接口代表一个角色,不应当将不同的角色都交给一个接口。没有关系的接口合并在一起,形成一个臃肿的大接口,这是对角色和接口的污染。因此使用多个专门的接口比使用单一的总接口要好。

      ■ 不应该强迫客户依赖于它们不用的方法。接口属于客户,不属于它所在的类层次结构,即不要强迫客户使用它们不用的方法,否则这些客户就会面临由于这些不使用的方法的改变所带来的改变。

接口隔离原则和其他的设计原则一样,都是需要花费时间和精力来进行设计和筹划的,但是它带来了设计的灵活性,并降低整个项目的风险,当业务变化时能够快速应付。在设计接口时应根据经验和常识决定接口的粒度大小,如果接口粒度太小,导致接口数量剧增,给开发带来难度;如果接口粒度太大,灵活性降低,无法提供定制服务,给项目带来无法预计的风险。

6、迪米特法则-LKP(Least Knowledge Principle

      迪米特法则又叫最少知识原则(Least Knowledge Principle,LKP),意思是一个对象应当对其他对象尽可能少的了解。迪米特法则最初是用来作为面向对象的系统设计风格的一种法则;

迪米特法则不同于其他的OO设计原则,它具有很多种表述方式,其中具有代表性的是以下几种表述:

     ■ 只与你直接的朋友们通信(Only talk to your immediate friends);

     ■ 不要跟“陌生人”说话(Don't talk to strangers);

     ■ 每一个软件单位对其他的单位都只有最少的了解,这些了解仅局限于那些与本单位密切相关的软件单位。

按照迪米特法则,如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用;如果一个类需要调用另一个类的某一个方法,可以通过第三者转发这个调用。

 

 创建型详细

    单例模式 :确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

     单例模式的主要作用是确保一个类只有一个实例存在。单例模式可以用在建立目录、数据库连接等需要单线程操作的场合,用于实现对系统资源的控制。由于Java语言的特点,使得在Java中实现单例模式通常有两种表现形式。

     ■ 饿汉式单例类:类加载时,就进行对象实例化;

     ■ 懒汉式单例类:第一次引用类时,才进行对象实例化。

1.饿汉式

              

     从上述代码中可以看到,在类被加载时,静态变量m_instance会被初始化,此时类的私有构造函数会被调用,单例类的唯一实例就被创建出来了。单例类中一个最重要的特点是类的构造函数是私有的,从而避免外界利用构造函数直接创建出任意多的实例。另外需要注意的是,由于构造函数是私有的,因此该类不能被继承。

2.懒汉式

            

     上述代码中,懒汉式单例类中对静态方法 getInstance()进行同步,以确保多线程环境下只创建一个实例,例如,如果getInstance()方法未被同步,并且线程A和线程B同时调用此方法,则执行if (_instance == null)语句时都为真,那么线程A和线程B都会创建一个对象,在内存中就会出现两个对象,这样就违反了单例模式;但使用synchronized关键字进行同步后,则不会出现此种情况。当然,当下有很多变种的单例懒汉实现方式,但是核心思想不变;

饿汉式单例类与懒汉式单例类之间的区别如下:

     ■ 饿汉式单例类在被加载时实例化,而懒汉式单例类在第一次引用时实例化;

     ■ 从资源利用效率上说,饿汉式单例类要差一些,但从速度和反应时间角度来讲,饿汉式单例类则比懒汉式单例类稍好些;

     ■ 饿汉式单例类可以在Java中实现,但不易在C++内实现。GoF在提出单例模式的概念时,举的例子是懒汉式的,他们的书影响之大,以致Java中单例类的例子也大多是懒汉式的。实际上,饿汉式单例类更符合Java语言本身的特点。

工厂方法模式:又叫虚拟构造函数(VirtualConstructor)模式或者多态性工厂(Polymorphic Factory)模式。工厂方法模式的用意是定义一个创建产品对象的工厂接口,将实际创建性工作推迟到子类中。

 工厂模式可分为简单工厂、工厂方法和抽象工厂模式:

     ■ 简单工厂模式中,一个工厂类处于对产品类实例化的中心位置上,它知道每一个产品,它决定哪一个产品类应当被实例化。这个模式的优点是允许客户端相对独立于产品创建的过程,并且在系统引入新产品的时候无须修改客户端,即在某种程度上支持“开-闭”原则。这个模式的缺点是对“开-闭”原则的支持不够,因为如果有新的产品加入到系统中,则需要修改工厂类,将必要的逻辑加入到工厂类中。

     ■ 工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,且克服了它的缺点。首先,在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建的工作交给子类去做。这个核心类则成为一个抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不接触哪个产品类应当被实例化的细节。此种进一步抽象化的结果,使这种工厂方法模式可以用来允许系统在不修改具体工厂角色的情况下引进新的产品,这一特点使得工厂模式具有超过简单工厂的优越性。

     ■ 抽象工厂模式是所有形态的工厂模式中最为抽象和最具有一般性的一种形态。抽象工厂模式与工厂方法模式的最大区别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则需要面对多个产品等级结构。

在工厂方法模式中,共涉及以下4个角色:

     ■ 抽象工厂(Creator)角色:该角色是工厂方法模式的核心,与应用系统无关,任何在创建对象的工厂类必须实现这个接口。

     ■ 具体工厂(Concrete Creator)角色:该角色实现了抽象工厂接口,含有与应用密切相关的逻辑,并且受到应用程序的调用以创建产品对象。

     ■ 抽象产品(Product)角色:该角色负责定义产品的共性,实现对产品最抽象的定义。

     ■ 具体产品(Concrete Product)角色:该角色实现抽象产品角色所声明的接口,工厂方法模式所创建的每一个对象都是某个具体产品角色的实例。

1.抽象工厂:

      

2.抽象产品:

      

3.具体工厂:

      

4.具体产品:

     

5.测试类:

     

 

工厂方法模式的优点有以下几个方面:

    ■ 良好的封装性,代码结构清晰。一个对象创建是有条件约束的,如果一个调用者需要一个具体的产品对象,只要知道这个产品的类名或约束字符串即可,不用知道创建对象的过程如何,降低了模块间的耦合。

    ■ 优秀的可扩展性。在增加产品类的情况下,只要适当地修改具体的工厂类或扩展一个工厂类,就可以适应变化。

    ■ 屏蔽产品类。产品类的实现如何变化,调用者都不需要关心,而只需要关心产品的接口,只要接口保持不变,系统的上层模块就不需要发生变化。因为产品的实例化是由工厂类负责的,具体生产何种产品对象是由不同的工厂类决定的。

    ■ 工厂方法模式是典型的解耦框架。高层模块只需要知道产品的抽象类,其他的实现类都不用关心。工厂方法模式符合迪米特法则,也符合依赖倒置原则,只依赖产品类的抽象;另外还符合里氏替换原则,可以使用产品子类替换产品父类。

抽象工厂模式:为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们的具体类。

 抽象工厂模式中的角色与工厂方法类似,也是有4个角色:

    ■ 抽象工厂(Abstract Factory)角色:该角色是抽象工厂模式的核心,与应用系统无关,任何创建对象的工厂类必须实现这个接口。

    ■ 具体工厂(Concrete Factory)角色:该角色实现了抽象工厂接口,含有选择合适的产品对象的逻辑,并且受到应用程序的调用以创建产品对象。

    ■ 抽象产品(Abstract Product)角色:该角色负责定义产品的共性,实现对产品最抽象的定义。

    ■ 具体产品(Concrete Product)角色:该角色实现抽象产品角色所声明的接口,抽象工厂模式所创建的任何产品对象都是某个具体产品角色的实例。

 抽象工厂模式的优点:

      抽象工厂模式是工厂方法模式的进一步抽象,针对的是一族产品。如果产品族中只有一种产品,则抽象工厂模式就退化为工厂方法模式。除了工厂方法模式的优点外,抽象工厂模式还具有下列优点。

         ■ 产品族内的约束为非公开状态,在不同的工厂中,各种产品可能具有不同的相互依赖关系,这些依赖关系由工厂封装在其内部,对于工厂的使用者是不可见的。

         ■ 生产线的扩展非常容易,如果要针对同一产品族建立新的生产线,只需要实现产品族中的所有产品接口并建立新的工厂类即可。

 抽象工厂模式的缺点:

     抽象工厂模式的最大缺点就是产品族本身的扩展非常困难,如果需要在产品族中增加一个新的产品类型,则需要修改多个接口,并且会影响已有的工厂类。

 具体实现:

1.抽象工厂:

     

两个不同的实现具体工厂

     

     

2.抽象产品:

    

3.两个不同的具体产品

   

   

测试:

   

建造者模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

  在建造者模式中,有如下4个角色:

     ■ 抽象建造者(Builder)角色:该角色用于规范产品的各个组成部分,并进行抽象,一般独立于应用程序的逻辑。

     ■ 具体建造者(Concrete Builder)角色:该角色实现抽象建造者中定义的所有方法,并且返回一个组建好的产品实例。

     ■ 产品(Product)角色:该角色是建造中的复杂对象,一个系统中会有多于一个的产品类,这些产品类并不一定有共同的接口,完全可以是不相关联的。

     ■ 导演者(Director)角色:该角色负责安排已有模块的顺序,然后告诉Builder 开始建造。

建造者模式的优点建造者模式的优点有以下几个方面:

     ■ 封装性,使用建造者模式可以使客户端不必知道产品内部组成的细节。

     ■ 建造者独立,容易扩展。

     ■ 便于控制细节风险,由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响。

具体实现:

  1.产品

  2.抽象建造者

3.具体建造者

4.测试

 

原型模式:用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。

原型模式涉及3个角色:

   ■ 客户(Client)角色:该角色提出创建对象的请求。

   ■ 抽象原型(Prototype)角色:该角色是一个抽象角色,通常由一个Java接口或抽象类实现,给出所有的具体原型类所需的接口。

   ■ 具体原型(Concrete Prototype)角色:该角色是被复制的对象,必须实现抽象原型接口。

 

实现:

1.抽象原型

2.具体原型

3.客户

 结构型详细

  代理模式:为其他对象提供一种代理以控制对这个对象的访问。

   代理模式提供以下3个角色:

        ■ 抽象主题(Subject)角色:该角色是真实主题和代理主题的共同接口,以便在任何可以使用真实主题的地方都可以使用代理主题。

        ■ 代理主题(Proxy Subject)角色:也叫做委托类、代理类,该角色负责控制对真实主题的引用,负责在需要的时候创建或删除真实主题对象,并且在真实主题角色处理完毕前后做预处理和善后处理工作。

        ■ 真实主题(Real Subject)角色:该角色也叫做被委托角色、被代理角色,是业务逻辑的具体执行者。

   代理模式的优点有:

        ■ 职责清晰:真实的角色实现实际的业务逻辑,不用关心其他非本职的事务,通过后期的代理完成附加的事务,附带的结果就是编程简洁清晰。

        ■ 高扩展性:具体主题角色随需求不同可能有很多种,但只要实现了接口,代理类就完全可以在不做任何修改的情况下代理各种真实主题角色。

        ■ 智能化:代理类可以在运行时才确定需要去代理的真实主题,这是一种强大的功能。

   实现:

    抽象主题类

   

  真实主题

  

  抽象主题

  

  装饰模式:动态地给一个对象添加一些额外的职责.

   装饰模式有以下4个角色:

       ■ 抽象构件(Component)角色:该角色用于规范需要装饰的对象(原始对象)。

       ■ 具体构件(Concrete Component)角色:该角色实现抽象构件接口,定义一个需要装饰的原始类。

       ■ 装饰(Decorator)角色:该角色持有一个构件对象的实例,并定义一个与抽象构件接口一致的接口。

       ■ 具体装饰(Concrete Decorator)角色:该角色负责对构件对象进行装饰。

  适配器模式的优点适配器模式的优点有以下几个方面:

       ■ 适配器模式可以让两个没有任何关系的类在一起运行。

       ■ 增加了类的透明性。

       ■ 提高类的复用度。

       ■ 增强代码的灵活性。

抽象构件

   

具体构件:

   

装饰角色

   

 具体装饰

   

测试

   

(排版问题 ,不再一一举例演示剩余部分,请谅解);

 适配器模式: 将一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。

     适配器模式涉及以下3个角色:

         ■ 目标(Target)角色:该角色定义要转换成的目标接口。

         ■ 源(Adaptee)角色:需要被转换成目标角色的源角色。

         ■ 适配器(Adapter)角色:该角色是适配器模式的核心,其职责是通过继承或是类关联的方式,将源角色转换为目标角色。

 组合模式:将对象组合成树形结构以表示“部分—整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

 组合模式提供以下3个角色:

       ■ 抽象构件(Component)角色:该角色定义参加组合对象的共有方法和属性,规范一些默认的行为接口。

       ■ 叶子构件(Leaf)角色:该角色是叶子对象,其下没有其他的分支,定义出参加组合的原始对象的行为。

       ■树枝构件(Composite)角色:该角色代表参加组合的、其下有分支的树枝对象,它的作用是将树枝和叶子组合成一个树形结构,并定义出管理子对象的方法,如add()、remove()等。

 桥梁模式:将抽象和实现解耦,使得两者可以独立地变化

 桥梁模式有以下4个角色:

       ■ 抽象化(Abstraction)角色:该角色抽象化的给出定义,并保存一个对实现化对象的引用。

       ■ 实现化(Implementor)角色:该角色给出实现化角色的接口,但不给出具体的实现。

       ■ 修正抽象化(RefinedAbstraction)角色:该角色扩展抽象化角色,它引用实现化角色并对抽象化角色进行修正。

       ■ 具体实现化(ConcreteImplementor)角色:该角色对实现化角色接口中的方法进行具体实现。

  外观模式:要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。外观模式提供一个高层次的接口,使得子系统更易使用。

  外观模式具有以下两个角色:

       ■ 外观(Facade)角色:客户端可以调用该角色的方法,该角色知晓相关子系统的功能和责任。正常情况下,本角色会将所有从客户端发来的请求委派到相应的子系统,即该角色没有实际的业务逻辑,只是一个委托类。

       ■ 子系统(Subsystem)角色:可以同时有一个或多个子系统,每一个子系统都不是一个单独的类,而是一个类的集合。子系统不知道外观角色的存在,对于子系统而言,外观角色仅仅是另外一个客户端而已。

  外观模式的优点有以下几个方面:

       ■ 减少系统的相互依赖,所有的依赖都是对Façade对象的依赖,与子系统无关。

       ■ 提高灵活性,不管子系统内部如何变化,只要不影响Facade对象,任何活动都是自由的。

       ■ 提高安全性,Facade中未提供的方法,外界就无法访问,提高系统的安全性。

 享元模式:使用共享对象可有效地支持大量的细粒度的对象.

  享元模式是以共享的方式高效地支持大量的细粒度对象。享元对象能做到共享的关键是区分内部状态(Internal State)和外部状态(External State)。

     ■ 内部状态是存储在享元对象内部的、可以共享的信息,并且不会随环境改变而改变。

     ■ 外部状态是随环境改变而改变且不可以共享的状态。享元对象的外部状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入到享元对象内部。

享元模式具有以下4个角色:

    ■ 抽象享元(Flyweight)角色:该角色对享元类进行抽象,需要外部状态的操作可以通过参数的形式将外部状态传入。

    ■ 具体享元(ConcreteFlyweight)角色:该角色实现抽象享元定义的业务,注意享元对象的内部状态必须与环境无关,从而使得享元对象可以在系统内共享。

    ■ 享元工厂(FlyweightFactory)角色:该角色就是构造一个池容器,负责创建和管理享元角色,并提供从池容器中获得对象的方法,保证享元对象可以被系统适当的共享。当一个客户端对象请求一个享元对象时,享元工厂角色会检查系统中是否已经有一个符合要求的享元对象。如果已经有了,享元工厂则提供这个已有的享元对象;否则创建一个合适的享元对象。

    ■ 客户端(Client)角色:该角色需要自行存储所有享元对象的外部状态。注意除了上面4个角色,享元模式还会涉及复合享元角色,该角色是将一些单纯享元使用合成模式加以复合,形成复合享元对象,这些复合的享元对象本身不能共享,不会出现在享元工厂中,因此也称为“不可共享的享元角色”。但可以将复合享元对象分解成单纯的享元对象,而后者则可以共享。

应用场景Java基础类库中大量使用了享元模式,如String、Integer、Boolean、Character等类都通过享元模式提供了内部的池化机制。(此模式比较特殊,池技术中需要使用的,所以再贴实现加深说明)

  抽象享元

   

具体享元

   

 享元工厂

 

 行为型详细

模板方法模式:定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

模板方法模式涉及两个角色:

     ■ 抽象模板(Abstract Template)角色:该角色定义一个或多个抽象操作,以便让子类实现;这些抽象操作是基本操作,是一个顶级逻辑的组成步骤。还需要定义并实现一个或几个模板方法,这些模板方法一般是具体方法,即一个框架,实现对基本方法的调度,完成固定的逻辑。

     ■ 具体模板(Concrete Template)角色:该角色实现抽象模板中定义的一个或多个抽象方法,每一个抽象模板角色都可以有任意多个具体模板角色与之对应,而每一个具体模板角色都可以给出这些抽象方法的不同实现,从而使得顶级逻辑的实现各不相同。

模板方法模式的优点有以下几个方面:

    ■ 封装不变的部分,扩展可变部分。不变的部分封装到父类中实现,而可变的部分则可以通过继承进行扩展。

    ■ 提取公共部分代码,便于维护。将公共部分的代码抽取出来放在父类中,维护时只需要修改父类中的代码。

    ■ 行为由父类控制,子类实现。模板方法模式中的基本方法是由子类实现的,因此子类可以通过扩展增加相应的功能,符合开闭原则。

命令模式:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。

   命令模式中有如下4个角色:

    ■ 命令(Command)角色:该角色声明一个给所有具体命令类的抽象接口,定义需要执行的命令。

    ■ 具体命令(Concrete Command)角色:该角色定义一个接收者和行为之间的弱耦合,实现命令方法,并调用接收者的相应操作。

    ■ 调用者(Invoker)角色:该角色负责调用命令对象执行请求。

    ■ 接收者(Receiver)角色:该角色负责具体实施和执行一个请求。

责任链模式:使多个对象都有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。

  责任链模式涉及以下两个角色:

    ■ 抽象处理者(Handler)角色:该角色对请求进行抽象,并定义一个方法以设定和返回对下一个处理者的引用。

    ■ 具体处理者(Concrete Handler)角色:该角色接到请求后,可以选择将请求处理掉,或者将请求传给下一个处理者。由于具体处理者持有对下一个处理者的引用,因此,如果需要,具体处理者可以访问下一个处理者。

策略模式:定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。

策略模式涉及以下3个角色:

      ■ 环境(Context)角色:该角色也叫上下文角色,起到承上启下的作用,屏蔽高层模块对策略、算法的直接访问,它持有一个Strategy类的引用。

      ■ 抽象策略(Strategy)角色:该角色对策略、算法进行抽象,通常定义每个策略或算法必须具有的方法和属性。

      ■ 具体策略(Concrete Strategy)角色:该角色实现抽象策略中的具体操作,含有具体的算法。

策略模式的优点有以下几个方面:

     ■ 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族,恰当地使用继承可以把公共的代码移到父类中,从而避免代码重复。

     ■ 策略模式提供了可以替换继承关系的办法。继承可以处理多种算法或行为,如果不用策略模式,那么使用算法或行为的环境类就可能会有一些子类,每一个子类提供一个不同的算法或行为。但是,这样算法或行为的使用者就和算法本身混在一起,从而不可能再独立演化。

     ■ 使用策略模式可以避免使用多重条件转移语句。多重转移语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重转移语句里面,这比使用继承的办法还要原始和落后。

 策略模式的缺点策略模式的缺点:

     ■ 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类,即策略模式只适用于客户端知道所有的算法或行为的情况。

     ■ 策略模式造成很多的策略类。有时候可以通过把依赖于环境的状态保持到客户端里面,而将策略类设计成可共享的,这样策略类实例可以被不同客户端使用。可以使用享元模式来减少对象的数量。

使用策略模式的典型场景如下:

     ■ 多个类只是在算法或行为上稍有不同的场景。

     ■ 算法需要自由切换的场景。

     ■ 需要屏蔽算法规则的场景。

迭代器模式:提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节。

  迭代器模式有以下4个角色。

    ■ 抽象迭代器(Iterator)角色:该角色负责定义访问和遍历元素的接口。

    ■ 具体迭代器(Concrete Iterator)角色:该角色实现Iterator接口,完成容器元素的遍历。

    ■ 抽象聚集(Aggregate)角色:该角色提供创建迭代器角色的接口。

    ■ 具体聚集(Concrete Aggregate)角色:该角色实现抽象聚集接口,创建出容纳迭代器的对象。

迭代器模式的优点如下:

    ■ 迭代器模式简化了访问容器元素的操作,具备一个统一的遍历接口。

    ■ 封装遍历算法,使算法独立于聚集角色。客户无须知道聚集对象的类型,即使聚集对象的类型发生变化,也不会影响遍历过程。

迭代器模式的缺点如下:

    ■ 迭代器模式给使用者一个序列化的错觉,从而产生错误。

    ■ 迭代器的元素都是Object类型,没有类型特征(JDK1.5后通过引入泛型可以解决此问题)。

  

中介者模式:用一个中介对象封装一系列对象(同事)的交互,中介者使各对象不需要显式地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

 中介者模式有以下4个角色:

     ■ 抽象中介者(Mediator)角色:该角色定义出同事对象到中介者对象的统一接口,用于各同事角色之间的通信。

     ■ 具体中介者(Concrete Mediator)角色:该角色实现抽象中介者,它依赖于各个同事角色,并通过协调各同事角色实现协作行为。

     ■ 抽象同事(Colleague)角色:该角色定义出中介者到同事对象的接口,同事对象只知道中介者而不知道其余的同事对象。

     ■ 具体同事(Concrete Colleague)角色:该角色实现抽象同事类,每一个具体同事类都清楚自己在小范围内的行为,而不知道大范围内的目的。

观察者模式:定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。

 观察者模式具有以下4个角色:

      ■ 抽象主题(Subject)角色:该角色又称为“被观察者”,可以增加和删除观察者对象。

      ■ 抽象观察者(Observer)角色:该角色为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。

      ■ 具体主题(Concrete Subject)角色:该角色又称为“具体被观察者”,它将有关状态存入具体观察者对象,在具体主题的内部状态改变时,给所有登记过的观察者发出通知。

      ■ 具体观察者(Concrete Observer)角色:该角色实现抽象观察者所要求的更新接口,以便使自身的状态与主题的状态相协调。

观察者模式具有以下几个优点:

      ■ 观察者和被观察者之间是抽象耦合。被观察者角色所知道的只是一个具体观察者集合,每一个具体观察者都符合一个抽象观察者的接口。被观察者并不认识任何一个具体的观察者,它只知道它们都有一个共同的接口。由于被观察者和观察者没有紧密的耦合在一起,因此它们可以属于不同的抽象化层次,且都非常容易扩展。

      ■ 支持广播通信。被观察者会向所有登记过的观察者发出通知,这就是一个触发机制,形成一个触发链。

观察模式的缺点如下:

      ■ 如果一个主题有多个直接或间接的观察者,则通知所有的观察者会花费很多时间,且开发和调试都比较复杂。

      ■ 如果在主题之间有循环依赖,被观察者会触发它们之间进行循环调用,导致系统崩溃。在使用观察者模式时要特别注意这一点。

      ■ 如果对观察者的通知是通过另外的线程进行异步投递,系统必须保证投递的顺序执行。

      ■ 虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有提供相应的机制使观察者知道所观察的对象是如何发生变化。

观察者模式的注意事项如下:

      ■ 广播链的问题。一个观察者可以有双重身份,既是观察者也是被观察者,广播链一旦建立,逻辑就比较复杂,可维护性非常差。一般在一个观察者模式中最多出现一个对象既是观察者也是被观察者,这样消息最多转发一次(传递两次),较易控制。

      ■ 异步处理的问题。异步处理就要考虑线程安全和队列的问题。注意观察者广播链和责任链模式的最大区别就是观察者广播链在传播的过程中,消息是随时更改的,是由相邻的两个节点协商的消息结构;而责任链模式在消息传递过程中,消息是保持不变的,如果要改变,也只有在原有消息上进行修正。

备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样,以后就可以将该对象恢复到原先保存的状态。

  备忘录模式中有如下3个角色:

    ■ 发起人(Originator)角色:该角色记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘数据。

    ■ 备忘录(Memento)角色:该角色负责存储发起人角色的内部状态,在需要时提供发起人需要的内部状态数据。

    ■ 负责人(Caretaker)角色:该角色对备忘录角色进行管理、保存和提供备忘录。

访问者模式:封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。

访问者模式涉及以下5个角色:

    ■ 抽象访问者(Visitor)角色:该角色声明一个或多个访问操作,定义访问者可以访问哪些元素。

    ■ 具体访问者(Concrete Visitor)角色:该角色实现抽象访问者角色中的各个访问操作。

    ■ 抽象元素(Element)角色:该角色声明一个接受操作,接受一个访问者对象。

    ■ 具体元素(Concrete Element)角色:该角色实现抽象元素中的接受操作。

    ■ 结构对象(Object Structure)角色:该角色有以下责任,可以遍历结构中的所有元素;如果需要,提供一个高层次的接口让访问者对象可以访问每一个元素,也可以设计一个复合对象或者一个集合,如List或Set。

状态模式:当一个对象内在状态改变时允许改变行为,这个对象看起来像改变了其类型。

状态模式涉及以下3个角色:

     ■ 抽象状态(State)角色:该角色用以封装环境对象的一个特定状态所对应的行为。

     ■ 具体状态(Concrete State)角色:该角色实现环境的一个状态所对应的行为。

     ■ 环境(Context)角色:该角色定义客户端需要的接口,并负责具体状态的切换。它会保留一个具体状态类的实例,该实例给出环境对象的现有状态。

解释器模式:给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。

 解释器有以下5个角色:

      ■ 抽象表达式(Abstract Expression)角色:该角色声明一个所有的具体表达式角色都需要实现的抽象接口,该接口主要是一个解释操作interpret()方法。

      ■ 终结符表达式(Terminal Expression)角色:该角色实现了抽象表达式角色所要求的接口,文法中的每一个终结符都有一个具体终结表达式与之对应。

      ■ 非终结符表达式(Nonterminal Expression)角色:该角色是一个具体角色,文法中的每一条规则都对应一个非终结符表达式类。

      ■ 环境(Context)角色:该角色提供解释器之外的一些全局信息。

      ■ 客户端(Client)角色:该角色创建一个抽象语法树,调用解释操作。


总结

  目前延生出很多混合模式,模式的混合使用必须和实际的项目相关联,采用混合模式可以扬长避短,提高项目的稳定性、扩展性、可靠性等。在客户变更需求时,我们不会因为工作量暴增而沮丧;在业务临时爆发时,我们不用心惊胆战地盯看服务器的资源情况;在系统发现bug的时候,我们不用皱着眉头思考如何处理淆乱的数据;而且有些问题还是灾难性的。

扩展知识:

混合模式:

  ■ 命令链模式:命令模式+责任链模式的混合使用模式。

  ■ 工厂策略模式:工厂方法模式+策略模式的混合使用模式。

  ■ 观察中介者模式:观察者模式+中介者模式的混合使用模式。

  ■ 规格模式:是策略模式+组合模式的扩展。

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/569038
推荐阅读
相关标签
  

闽ICP备14008679号

        
cppcmd=keepalive&