精华内容
下载资源
问答
  • Java常见设计模式总结

    万次阅读 多人点赞 2021-09-18 17:18:54
    设计模式是一套经过反复使用的代码设计经验,目的是为了重用代码、让代码更容易被他人理解、保证代码可靠性。设计模式于己于人于系统都是多赢的,它使得代码编写真正工程化,它是软件工程的基石,如同大厦的一块块...

     一、设计模式总述:

    1、什么是设计模式:

            设计模式是一套经过反复使用的代码设计经验,目的是为了重用代码、让代码更容易被他人理解、保证代码可靠性。 设计模式于己于人于系统都是多赢的,它使得代码编写真正工程化,它是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。总体来说,设计模式分为三大类:

    • 创建型模式:共5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式
    • 结构型模式:共7种:适配器模式、装饰器模式、代理模式、桥接模式、外观模式、组合模式、享元模式
    • 行为型模式:共11种:策略模式、模板方法模式、观察者模式、责任链模式、访问者模式、中介者模式、迭代器模式、命令模式、状态模式、备忘录模式、解释器模式

    其实还有两类:并发型模式和线程池模式,用一个图片来整体描述一下:

    2、设计模式的六大原则:

    (1)开闭原则 (Open Close Principle) :

            开闭原则指的是对扩展开放,对修改关闭。在对程序进行扩展的时候,不能去修改原有的代码,想要达到这样的效果,我们就需要使用接口或者抽象类

    (2)依赖倒转原则 (Dependence Inversion Principle):

            依赖倒置原则是开闭原则的基础,指的是针对接口编程,依赖于抽象而不依赖于具体

    (3)里氏替换原则 (Liskov Substitution Principle) :

            里氏替换原则是继承与复用的基石,只有当子类可以替换掉基类,且系统的功能不受影响时,基类才能被复用,而子类也能够在基础类上增加新的行为。所以里氏替换原则指的是任何基类可以出现的地方,子类一定可以出现。

            里氏替换原则是对 “开闭原则” 的补充,实现 “开闭原则” 的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏替换原则是对实现抽象化的具体步骤的规范。

    (4)接口隔离原则 (Interface Segregation Principle):

            使用多个隔离的接口,比使用单个接口要好,降低接口之间的耦合度与依赖,方便升级和维护方便

    (5)迪米特原则 (Demeter Principle):

            迪米特原则,也叫最少知道原则,指的是一个类应当尽量减少与其他实体进行相互作用,使得系统功能模块相对独立,降低耦合关系。该原则的初衷是降低类的耦合,虽然可以避免与非直接的类通信,但是要通信,就必然会通过一个“中介”来发生关系,过分的使用迪米特原则,会产生大量的中介和传递类,导致系统复杂度变大,所以采用迪米特法则时要反复权衡,既要做到结构清晰,又要高内聚低耦合。

    (6)合成复用原则 (Composite Reuse Principle):

            尽量使用组合/聚合的方式,而不是使用继承。

    二、Java的23种设计模式:

            接下来我们详细介绍Java中23种设计模式的概念,应用场景等情况,并结合他们的特点及设计模式的原则进行分析

    1、创建型-工厂方法模式:

    工厂方法模式分为三种:

    (1)简单工厂模式:

    建立一个工厂类,并定义一个接口对实现了同一接口的产品类进行创建。首先看下关系图:

    (2)工厂方法模式:

    工厂方法模式是对简单工厂模式的改进,简单工厂的缺陷在于不符合“开闭原则”,每次添加新产品类就需要修改工厂类,不利于系统的扩展维护。而工厂方法将工厂抽象化,并定义一个创建对象的接口。每增加新产品,只需增加该产品以及对应的具体实现工厂类,由具体工厂类决定要实例化的产品是哪个,将对象的创建与实例化延迟到子类,这样工厂的设计就符合“开闭原则”了,扩展时不必去修改原来的代码。UML关系图如下:

     (3)静态工厂方法模式:

    静态工厂模式是将工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。

    工厂方法模式详情文章:Java设计模式之创建型:工厂模式详解(简单工厂+工厂方法+抽象工厂)

    2、创建型-抽象工厂模式:

            抽象工厂模式主要用于创建相关对象的家族。当一个产品族中需要被设计在一起工作时,通过抽象工厂模式,能够保证客户端始终只使用同一个产品族中的对象;并且通过隔离具体类的生成,使得客户端不需要明确指定具体生成类;所有的具体工厂都实现了抽象工厂中定义的公共接口,因此只需要改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。

            但该模式的缺点在于添加新的行为时比较麻烦,如果需要添加一个新产品族对象时,需要更改接口及其下所有子类,这必然会带来很大的麻烦。

            UML结构图如下:

    抽象工厂模式详情:Java设计模式之创建型:工厂模式详解(简单工厂+工厂方法+抽象工厂)

    3、创建型-建造者模式:

             建造者模式将复杂产品的创建步骤分解在在不同的方法中,使得创建过程更加清晰,从而更精确控制复杂对象的产生过程;通过隔离复杂对象的构建与使用,也就是将产品的创建与产品本身分离开来,使得同样的构建过程可以创建不同的对象;并且每个具体建造者都相互独立,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。UML结构图如下:

     建造者模式详情:Java设计模式之创建型:建造者模式

    4、创建型-单例模式:

            单例模式可以确保系统中某个类只有一个实例,该类自行实例化并向整个系统提供这个实例的公共访问点,除了该公共访问点,不能通过其他途径访问该实例。单例模式的优点在于:

    • 系统中只存在一个共用的实例对象,无需频繁创建和销毁对象,节约了系统资源,提高系统的性能
    • 可以严格控制客户怎么样以及何时访问单例对象。

    单例模式的写法有好几种,主要有三种:懒汉式单例、饿汉式单例、登记式单例。

    单例模式详情:Java设计模式之创建型:单例模式

    5、创建型-原型模式:

            原型模式也是用于对象的创建,通过将一个对象作为原型,对其进行复制克隆,产生一个与源对象类似的新对象。UML类图如下:

     在 Java 中,原型模式的核心是就是原型类 Prototype,Prototype 类需要具备以下两个条件:

    • 实现 Cloneable 接口:
    • 重写 Object 类中的 clone() 方法,用于返回对象的拷贝;

    Object 类中的 clone() 方法默认是浅拷贝,如果想要深拷贝对象,则需要在 clone() 方法中自定义自己的复制逻辑。

    • 浅复制:将一个对象复制后,基本数据类型的变量会重新创建,而引用类型指向的还是原对象所指向的内存地址。
    • 深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。

            使用原型模式进行创建对象不仅简化对象的创建步骤,还比 new 方式创建对象的性能要好的多,因为 Object 类的 clone() 方法是一个本地方法,直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显;

    原型模式详情:Java设计模式之创建型:原型模式

            

            上面我们介绍了5种创建型模式,下面我们就开始介绍下7种结构型模式:适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式。其中对象的适配器模式是各种模式的起源,如下图:

    6、结构型-适配器模式:

            适配器模式主要用于将一个类或者接口转化成客户端希望的格式,使得原本不兼容的类可以在一起工作,将目标类和适配者类解耦;同时也符合“开闭原则”,可以在不修改原代码的基础上增加新的适配器类;将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性,但是缺点在于更换适配器的实现过程比较复杂。

            所以,适配器模式比较适合以下场景:

    • (1)系统需要使用现有的类,而这些类的接口不符合系统的接口。
    • (2)使用第三方组件,组件接口定义和自己定义的不同,不希望修改自己的接口,但是要使用第三方组件接口的功能。

    下面有个非常形象的例子很好地说明了什么是适配器模式:

    适配器模式的主要实现有三种:类的适配器模式、对象的适配器模式、接口的适配器模式。三者的使用场景如下:

    • 类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。
    • 对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper类,持有原类的一个实例,在Wrapper类的方法中,调用实例的方法就行。
    • 接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可。

    适配器模式详情:Java设计模式之结构型:适配器模式

    7、结构型-装饰器模式:

            装饰器模式可以动态给对象添加一些额外的职责从而实现功能的拓展,在运行时选择不同的装饰器,从而实现不同的行为;比使用继承更加灵活,通过对不同的装饰类进行排列组合,创造出很多不同行为,得到功能更为强大的对象;符合“开闭原则”,被装饰类与装饰类独立变化,用户可以根据需要增加新的装饰类和被装饰类,在使用时再对其进行组合,原有代码无须改变。装饰器模式的UML结构图如下:

            但是装饰器模式也存在缺点,首先会产生很多的小对象,增加了系统的复杂性,第二是排错比较困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。

    装饰器模式详情:Java设计模式之结构型:装饰器模式

    8、结构型-代理模式:

            代理模式的设计动机是通过代理对象来访问真实对象,通过建立一个对象代理类,由代理对象控制原对象的引用,从而实现对真实对象的操作。在代理模式中,代理对象主要起到一个中介的作用,用于协调与连接调用者(即客户端)和被调用者(即目标对象),在一定程度上降低了系统的耦合度,同时也保护了目标对象。但缺点是在调用者与被调用者之间增加了代理对象,可能会造成请求的处理速度变慢。UML结构图如下:

    代理模式详情:Java设计模式之结构型:代理模式

    9、结构型-桥接模式:

            桥接模式将系统的抽象部分与实现部分分离解耦,使他们可以独立的变化。为了达到让抽象部分和实现部分独立变化的目的,桥接模式使用组合关系来代替继承关系,抽象部分拥有实现部分的接口对象,从而能够通过这个接口对象来调用具体实现部分的功能。也就是说,桥接模式中的桥接是一个单方向的关系,只能够抽象部分去使用实现部分的对象,而不能反过来。 

            桥接模式符合“开闭原则”,提高了系统的可拓展性,在两个变化维度中任意扩展一个维度,都不需要修改原来的系统;并且实现细节对客户不透明,可以隐藏实现细节。但是由于聚合关系建立在抽象层,要求开发者针对抽象进行编程,这增加系统的理解和设计难度。桥接模式的UML结构图如下:

            就像在Java中我们使用 JDBC 连接数据库时,在各个数据库之间进行切换,基本不需要动太多的代码,原因就是使用了桥接模式,JDBC 提供统一接口,每个数据库提供各自的实现,然后由桥接类创建一个连接数据库的驱动,使用某一个数据库的时候只需要切换一下就行。JDBC 的结构图如下:

             在 JDBC 中,桥接模式的实现化角色 (Implementor) 为的 Driver 接口,具体实现化 (Concrete Implementor) 角色对应 MysqlDriver、OracleDriver 和 MariadbDriver,扩展抽象化 (Refined Abstraction) 角色对应 DriverManager,不具有抽象化 (Abstraction) 角色作为扩展抽象化角色的父类。

    桥接模式详情:Java设计模式之结构型:桥接模式

    10、结构型-外观模式:

            外观模式通过对客户端提供一个统一的接口,用于访问子系统中的一群接口。使用外观模式有以下几点好处:

    (1)更加易用:使得子系统更加易用,客户端不再需要了解子系统内部的实现,也不需要跟众多子系统内部的模块进行交互,只需要跟外观类交互就可以了;

    (2)松散耦合:将客户端与子系统解耦,让子系统内部的模块能更容易扩展和维护。

    (3)更好的划分访问层次:通过合理使用 Facade,可以更好地划分访问的层次,有些方法是对系统外的,有些方法是系统内部使用的。把需要暴露给外部的功能集中到门面中,这样既方便客户端使用,也很好地隐藏了内部的细节。

            但是如果外观模式对子系统类做太多的限制则减少了可变性和灵活性,所以外观模式适用于为复杂子系统提供一个简单接口,提高系统的易用性场景 以及 引入外观模式将子系统与客户端进行解耦,提高子系统的独立性和可移植性。

            外观模式的UML结构图如下:

    外观模式详情: Java设计模式之结构型:外观模式

    11、结构型-组合模式:

            组合模式将叶子对象和容器对象进行递归组合,形成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性,能够像处理叶子对象一样来处理组合对象,无需进行区分,从而使用户程序能够与复杂元素的内部结构进行解耦。

            组合模式最关键的地方是叶子对象和组合对象实现了相同的抽象构建类,它既可表示叶子对象,也可表示容器对象,客户仅仅需要针对这个抽象构建类进行编程,这就是组合模式能够将叶子节点和对象节点进行一致处理的原因。组合模式的UML结构图如下:

    组合模式详情: Java设计模式之结构型:组合模式

    12、结构型-享元模式:

            享元模式通过共享技术有效地支持细粒度、状态变化小的对象复用,当系统中存在有多个相同的对象,那么只共享一份,不必每个都去实例化一个对象,极大地减少系统中对象的数量,从而节省资源。

            享元模式的核心是享元工厂类,享元工厂类维护了一个对象存储池,当客户端需要对象时,首先从享元池中获取,如果享元池中存在对象实例则直接返回,如果享元池中不存在,则创建一个新的享元对象实例返回给用户,并在享元池中保存该新增对象,这点有些单例的意思。

            工厂类通常会使用集合类型来保存对象,如 HashMap、Hashtable、Vector 等等,在 Java 中,数据库连接池、线程池等都是用享元模式的应用。

            享元模式的UML结构图如下:

             Java 中,String 类型就是使用享元模式,String 对象是 final 类型,对象一旦创建就不可改变。而 Java 的字符串常量都是存在字符串常量池中的,JVM 会确保一个字符串常量在常量池中只有一个拷贝。

            而且提到共享池,我们也很容易联想到 Java 里面的JDBC连接池,通过连接池的管理,实现了数据库连接的共享,不需要每一次都重新创建连接,节省了数据库重新创建的开销,提升了系统的性能!

    享元模式详情:Java设计模式之结构型:享元模式

            前面我们介绍了7种结构型设计模式,接下来我们介绍一下11种行为型设计模式:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。先来张图,看看这11中模式的关系:

     13、行为型-策略模式:

            将类中经常改变或者可能改变的部分提取为作为一个抽象策略接口类,然后在类中包含这个对象的实例,这样类实例在运行时就可以随意调用实现了这个接口的类的行为。

            比如定义一系列的算法,把每一个算法封装起来,并且使它们可相互替换,使得算法可独立于使用它的客户而变化,这就是策略模式。UML结构图如下:

            策略模式的优点在于可以动态改变对象的行为;但缺点是会产生很多策略类,并且策略模式的决定权在用户,系统只是提供不同算法的实现,所以客户端必须知道所有的策略类,并自行决定使用哪一个策略类; 

            策略模式适用用于以下几种场景:

    • (1)应用程序需要实现特定的功能服务,而该程序有多种实现方式使用,所以需要动态地在几种算法中选择一种
    • (2)一个类定义了多种行为算法,并且这些行为在类的操作中以多个条件语句的形式出现,就可以将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。

    策略模式详情:Java设计模式之行为型:策略模式

    14、行为型-模板方法:

            模板方法是基于继承实现的,在抽象父类中声明一个模板方法,并在模板方法中定义算法的执行步骤(即算法骨架)。在模板方法模式中,可以将子类共性的部分放在父类中实现,而特性的部分延迟到子类中实现,只需将特性部分在父类中声明成抽象方法即可,使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤,不同的子类可以以不同的方式来实现这些逻辑。

            模板方法模式的优点在于符合“开闭原则”,也能够实现代码复用,将不变的行为转移到父类,去除子类中的重复代码。但是缺点是不同的实现都需要定义一个子类,导致类的个数的增加使得系统更加庞大,设计更加抽象。模板方法模式的UML图如下:

    模板方法详情:Java设计模式之行为型:模板方法模式

    15、行为型-责任链模式:

            职责链可以将请求的处理者组织成一条链,并将请求沿着链传递,如果某个处理者能够处理请求则处理,否则将该请求交由上级处理。客户端只需将请求发送到职责链上,无须关注请求的处理细节,通过职责链将请求的发送者和处理者解耦了,这也是职责链的设计动机。        

           职责链模式可以简化对象间的相互连接,因为客户端和处理者都没有对方明确的信息,同时处理者也不知道职责链中的结构,处理者只需保存一个指向后续者的引用,而不需要保存所有候选者的引用。

            另外职责链模式增加了系统的灵活性,我们可以任意增加或更改处理者,甚至更改处理者的顺序,不过有可能会导致一个请求无论如何也得不到处理,因为它可能被放置在链末端。

    所以责任链模式有以下几个优点:

    • (1)降低耦合度,将请求的发送者和接收者解耦。反映在代码上就是不需要在类中写很多丑陋的 if….else 语句,如果用了职责链,相当于我们面对一个黑箱,只需将请求递交给其中一个处理者,然后让黑箱内部去负责传递就可以了。
    • (2)简化了对象,使得对象不需要链的结构。
    • (3)增加系统的灵活性,通过改变链内的成员或者调动他们的次序,允许动态地新增或者删除处理者
    • (4)增加新的请求处理类很方便。

    但是责任链模式也存在一些缺点:

    • (1)不能保证请求一定被成功处理
    • (2)系统性能将受到一定影响,并且可能会造成循环调用。
    • (3)可能不容易观察运行时的特征,而且在进行代码调试时不太方便,有碍于除错。

            责任链模式的UML结构图如下:

    责任链模式详情:Java设计模式之行为型:责任链模式

    16、行为型-观察者模式:

            观察者模式又称为 发布-订阅模式,定义了对象之间一对多依赖关系,当目标对象(被观察者)的状态发生改变时,它的所有依赖者(观察者)都会收到通知。一个观察目标可以对应多个观察者,而这些观察者之间没有相互联系,所以能够根据需要增加和删除观察者,使得系统更易于扩展,符合开闭原则;并且观察者模式让目标对象和观察者松耦合,虽然彼此不清楚对方的细节,但依然可以交互,目标对象只知道一个具体的观察者列表,但并不认识任何一个具体的观察者,它只知道他们都有一个共同的接口。

            但观察者模式的缺点在于如果存在很多个被观察者的话,那么将需要花费一定时间通知所有的观察者,如果观察者与被观察者之间存在循环依赖的话,那么可能导致系统崩溃,并且观察者模式没有相应的机制让观察者知道被观察对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。观察者模式的UML结构图如下:

     观察者模式详情:Java设计模式之行为型:观察者模式

    17、行为型-访问者模式:

            访问者模式就是一种分离对象数据结构与行为 (基于数据结构的操作) 的方法,通过这种分离,达到为一个被访问者动态添加新的操作而无需做其它修改的效果,使得添加作用于这些数据结构的新操作变得简单,并且不需要改变各数据结构,为不同类型的数据结构提供多种访问操作方式,这样是访问者模式的设计动机。

            除了使新增访问操作变得更加简单,也能够在不修改现有类的层次结构下,定义该类层次结构的操作,并将有关元素对象的访问行为集中到一个访问者对象中,而不是分散搞一个个的元素类中。

           但访问者模式的缺点在于让增加新的元素类变得困难,每增加一个新的元素类都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作,违背了“开闭原则”的要求;

            所以访问者模式适用于对象结构中很少改变,但经常需要在此对象结构上定义新的操作的系统,使得算法操作的增加变得简单;或者需要对一个对象结构中进行很多不同并且不相关的操作,并且需要避免让这些操作污染这些对象,也不希望在增加新操作时修改这些类的场景。

            访问者模式的UML结构图如下:

            从上面的 UML 结构图中我们可以看出,访问者模式主要分为两个层次结构,一个是访问者层次结构,提供了抽象访问者和具体访问者,主要用于声明一些操作;一个是元素层次结构,提供了抽象元素和具体元素,主要用于声明 accept 操作;而对象结构 ObjectStructure 作为两者的桥梁,存储了不同类型的对象,以便不同的访问者来访问,相同访问者可以以不同的方式访问不同的元素,所以在访问者模式中增加新的访问者无需修改现有代码,可扩展行强。

            在访问者模式使用了双分派技术,所谓双分派技术就是在选择方法的时候,不仅仅要根据消息接收者的运行时区别,还要根据参数的运行时区别。在访问者模式中,客户端将具体状态当做参数传递给具体访问者,这里完成第一次分派,然后具体访问者作为参数的“具体状态”中的方法,同时也将自己this作为参数传递进去,这里就完成了第二次分派。双分派意味着得到的执行操作决定于请求的种类和接受者的类型。

     访问者模式详情:Java设计模式之行为型:访问者模式

    18、行为型-中介者模式:

             中介者模式通过中介者对象来封装一系列的对象交互,将对象间复杂的关系网状结构变成结构简单的以中介者为核心的星形结构,对象间一对多的关联转变为一对一的关联,简化对象间的关系,便于理解;各个对象之间的关系被解耦,每个对象不再和它关联的对象直接发生相互作用,而是通过中介者对象来与关联的对象进行通讯,使得对象可以相对独立地使用,提高了对象的可复用和系统的可扩展性。

            在中介者模式中,中介者类处于核心地位,它封装了系统中所有对象类之间的关系,除了简化对象间的关系,还可以对对象间的交互进行进一步的控制。中介者模式的UML结构图如下:

            但是,中介者对象封装了对象之间的关联关系,导致中介者对象变得比较庞大复杂,所承担的责任也比较多,维护起来也比较困难,它需要知道每个对象和他们之间的交互细节,如果它出问题,将会导致整个系统都会出问题。

    中介者模式详情:Java设计模式之行为型:中介者模式

    19、行为型-命令模式:

            命令模式的本质是将请求封装成对象,将发出命令与执行命令的责任分开,命令的发送者和接收者完全解耦,发送者只需知道如何发送命令,不需要关心命令是如何实现的,甚至是否执行成功都不需要理会。命令模式的关键在于引入了抽象命令接口,发送者针对抽象命令接口编程,只有实现了抽象命令接口的具体命令才能与接收者相关联。

            使用命令模式的优势在于降低了系统的耦合度,而且新命令可以很方便添加到系统中,也容易设计一个组合命令。但缺点在于会导致某些系统有过多的具体命令类,因为针对每一个命令都需要设计一个具体命令类。

            命令模式的UML结构图如下:

    命令模式详情: Java设计模式之行为型:命令模式

    20、行为型-状态模式:

            状态模式,就是允许对象在内部状态发生改变时改变它的行为,对象看起来就好像修改了它的类,也就是说以状态为原子来改变它的行为,而不是通过行为来改变状态。

            当对象的行为取决于它的属性时,我们称这些属性为状态,那该对象就称为状态对象。对于状态对象而言,它的行为依赖于它的状态,比如要预订房间,只有当该房间空闲时才能预订,想入住该房间也只有当你预订了该房间或者该房间为空闲时。对于这样的一个对象,当它的外部事件产生互动的时候,其内部状态就会发生变化,从而使得他的行为也随之发生变化。

            状态模式的UML结构图如下:

     从上面的UML结构图我们可以看出状态模式的优点在于:

    (1)封装了转换规则,允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块

    (2)将所有与状态有关的行为放到一个类中,可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。 

    但是状态模式的缺点在于:

    (1)需要在枚举状态之前需要确定状态种类

    (2)会导致增加系统类和对象的个数。

    (3)对 “开闭原则” 的支持并不友好,新增状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。

    所以状态模式适用于:代码中包含大量与对象状态有关的条件语句,以及对象的行为依赖于它的状态,并且可以根据它的状态改变而改变它的相关行为。

    状态模式详情:Java设计模式之行为型:状态模式

    21、行为型-备忘录模式:

            备忘录模式提供了一种恢复状态的机制,在不破坏封装的前提下,捕获对象的某个时刻内部状态,并保存在该对象之外,保证该对象能够恢复到某个历史状态;备忘录模式将保存的细节封装在备忘录中,除了创建它的创建者之外其他对象都不能访问它,并且实现了即使要改变保存的细节也不影响客户端。但是备忘录模式都是多状态和多备份的,会早用较多的内存,消耗资源。备忘录模式的额UML结构图如下:

             备忘录模式的核心就是备忘录 Memento,在备忘录中存储的就是原发器 Originator 的部分或者所有的状态信息,而这些状态信息是不能够被其他对象所访问的,也就是说我们是不能使用备忘录之外的对象来存储这些状态信息,如果暴漏了内部状态信息就违反了封装的原则,故备忘录除了原发器外其他对象都不可以访问。所以为了实现备忘录模式的封装,我们需要对备忘录的访问做些控制:

    (1)对原发器:可以访问备忘录里的所有信息。

    (2)对负责人 caretaker:不可以访问备忘录里面的数据,但是他可以保存备忘录并且可以将备忘录传递给其他对象。

    (3)其他对象:不可访问也不可以保存,它只负责接收从负责人那里传递过来的备忘录同时恢复原发器的状态。

    备忘录模式详情:Java设计模式之行为型:备忘录模式

    22、行为型-迭代器模式:

            迭代器模式提供一种访问集合中的各个元素,而不暴露其内部表示的方法。将在元素之间游走的职责交给迭代器,而不是集合对象,从而简化集合容器的实现,让集合容器专注于在它所应该专注的事情上,更加符合单一职责原则,避免在集合容器的抽象接口层中充斥着各种不同的遍历操作。迭代器模式的UML结构图如下:

    迭代器模式详情:Java设计模式之行为型:迭代器模式

    23、行为型-解释器模式:

            解释器模式,就是定义语言的文法,并建立一个解释器来解释该语言中的句子,通过构建解释器,解决某一频繁发生的特定类型问题实例。

            解释器模式描述了如何构成一个简单的语言解释器,主要应用在使用面向对象语言开发的编译器中,它描述了如何为简单的语言定义一个文法,如何在该语言中表示一个句子,以及如何解释这些句子。    

            解释器模式中除了能够使用文法规则来定义一个语言,还能通过使用抽象语法树来更加直观表示、更好地地表示一个语言的构成,每一颗抽象语法树对应一个语言实例。抽象语法树描述了如何构成一个复杂的句子,通过对抽象语法树的分析,可以识别出语言中的终结符和非终结符类。 在解释器模式中由于每一种终结符表达式、非终结符表达式都会有一个具体的实例与之相对应,所以系统的扩展性比较好。

            解释器模式的UML如下:

     解释器模式详情:Java设计模式之行为型:解释器模式


    相关推荐阅读:

    Spring常见面试题总结

    SpringMVC常见面试题总结

    Mybatis常见面试题总结

    MySQL常见面试题总结

    Redis常见面试题总结

    RabbitMQ消息队列常见面试题总结

    ElasticSearch搜索引擎常见面试题总结

    计算机网络常见面试题总结

    操作系统常见面试题总结

    Java基础、集合、多线程常见面试题总结

    Java虚拟机常见面试题总结

    Java常见设计模式总结

    海量数据处理的方法总结


    参考文章:

    Java之美[从菜鸟到高手演变]之设计模式

    Java之美[从菜鸟到高手演变]之设计模式二

    Java之美[从菜鸟到高手演变]之设计模式三

    Java之美[从菜鸟到高手演变]之设计模式四

    展开全文
  • 1.Introduction2....这是我在游戏中所使用的模式总结,这些模式能让我们的代码更整洁,更清晰易懂,以及运行更快!当我开始编写游戏时,我希望我有一本这样的书。 --------------------Bob Nystrom
  • 设计模式23模式介绍

    万次阅读 多人点赞 2020-01-18 08:20:52
    一、什么是设计模式 设计模式是一套被反复使用、多数人知晓的、经过分类编写的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。毫无疑问,设计模式使代码编程真正...

    🍅 周周有福利,周周有惊喜 哪吒社区 - 风火轮计划

    🍅 Java学习路线配套文章:搬砖工逆袭Java架构师

    🍅 Java经典面试题大全:10万字208道Java经典面试题总结(附答案)

    🍅 简介:Java领域优质创作者🏆、CSDN哪吒公众号作者✌ 、Java架构师奋斗者💪

    🍅 扫描主页左侧二维码,加入群聊,一起学习、一起进步 

    🍅 欢迎点赞 👍 收藏 ⭐留言 📝   

    一、什么是设计模式

    设计模式(Design pattern) 是解决软件开发某些特定问题而提出的一些解决方案也可以理解成解决问题的一些思路。通过设计模式可以帮助我们增强代码的可重用性、可扩充性、 可维护性、灵活性好。我们使用设计模式最终的目的是实现代码的高内聚和低耦合。

    二、设计模式的三大分类及关键点

    1、创建型模式

    对象实例化的模式,创建型模式用于解耦对象的实例化过程。

    1. 单例模式:某个类智能有一个实例,提供一个全局的访问点。
    2. 工厂模式:一个工厂类根据传入的参量决定创建出哪一种产品类的实例。
    3. 抽象工厂模式:创建相关或依赖对象的家族,而无需明确指定具体类。
    4. 建造者模式:封装一个复杂对象的创建过程,并可以按步骤构造。
    5. 原型模式:通过复制现有的实例来创建新的实例。

    2、结构型模式

    把类或对象结合在一起形成一个更大的结构。

    1. 装饰器模式:动态的给对象添加新的功能。
    2. 代理模式:为其它对象提供一个代理以便控制这个对象的访问。
    3. 桥接模式:将抽象部分和它的实现部分分离,使它们都可以独立的变化。
    4. 适配器模式:将一个类的方法接口转换成客户希望的另一个接口。
    5. 组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构。
    6. 外观模式:对外提供一个统一的方法,来访问子系统中的一群接口。
    7. 享元模式:通过共享技术来有效的支持大量细粒度的对象。

    3、行为型模式

    类和对象如何交互,及划分责任和算法。

    1. 策略模式:定义一系列算法,把他们封装起来,并且使它们可以相互替换。
    2. 模板模式:定义一个算法结构,而将一些步骤延迟到子类实现。
    3. 命令模式:将命令请求封装为一个对象,使得可以用不同的请求来进行参数化。
    4. 迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。
    5. 观察者模式:对象间的一对多的依赖关系。
    6. 仲裁者模式:用一个中介对象来封装一系列的对象交互。
    7. 备忘录模式:在不破坏封装的前提下,保持对象的内部状态。
    8. 解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器。
    9. 状态模式:允许一个对象在其对象内部状态改变时改变它的行为。
    10. 责任链模式:将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。
    11. 访问者模式:不改变数据结构的前提下,增加作用于一组对象元素的新功能。

    三、设计模式的几种原则

    1、单一职责原则

    对于一个类,只有一个引起该类变化的原因;该类的职责是唯一的,且这个职责是唯一引起其他类变化的原因。

    2、接口隔离原则

    客户端不应该依赖它不需要的接口,一个类对另一个类的依赖应该建立在最小的接口上。

    3、依赖倒转原则

    依赖倒转原则是程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。

    4、里式代换原则

    任何基类可以出现的地方,子类一定可以出现。里氏代换原则是继承复用的基石,只有当衍生类可以替换基类,软件单位的功能不受影响时,基类才能真正的被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

    5、开闭原则

    (1)对于扩展是开放的(Open for extension)。这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。也就是说,我们可以改变模块的功能。

    (2)对于修改是关闭的(Closed for modification)。对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。模块的二进制可执行版本,无论是可链接的库、DLL或者.EXE文件,都无需改动。

    6、迪米特法则

    迪米特法则又叫做最少知识原则,就是说一个对象应当对其它对象又尽可能少的了解,不和陌生人说话。

    7、合成复用原则

    合成复用原则要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。如果要使用继承关系,则必须严格遵循里氏替换原则。合成复用原则同里氏替换原则相辅相成的,两者都是开闭原则的具体实现规范。

    设计模式七大原则总结(超详细)

    四、设计模式关系

    五、设计模式感想

    一共有23种设计模式,可以说都是为了提高代码的可读性、可扩展性、可复用性、类的可替换性、组件化、可移植性等等特性。通过接口、抽象类、继承、实现、委托、抽象、面向接口编程、多态、重载、重写等方式使得代码的这些特性得以彰显,可以说只有深刻的理解了这些概念背后的哲学思想才能更好的理解设计模式。在设计模式中有很多思想,比如可以使用委托的不要使用继承、开闭原则,面向扩展开放,面向修改关闭,里式代换原则,父类一定能被子类代替并使用,反置则不然,面向接口编程,功能层次和实现层次分离(桥接模式)、高内聚低耦合等思想,这些思想都是宝贵的,正是因为这样的思想的存在才使得代码的更新换代的时候能够尽可能少的甚至不用修改之前的代码,直接加入新的内容。提高软件的开发周期,便于维护和升级,便于查找和纠错,易于扩展和使用。

    同样的设计模式主要分为三大类,创建型、行为型、结构型。我们可以简单的这样分类,只不过这样的分类似乎并不准确,不能一语道出所有的本质,设计模式是相互关联的,有的设计模式内部其实是使用了别的设计模式作为支撑的,但是大体上这样的一种划分便于我们去记忆,仅此而已。

    六、设计模式回顾

    从迭代器开始,我们将类中数据结构的遍历和类的功能实现分离出来,本质上使用了工厂模式;

    其次我们学习了适配器模式,它将不同的接口进行适配,从而便于版本的兼容性以及其他功能;

    然后我们学习了模板方法,使用模板面向抽象编程,便于新的子类的实现和管理;

    之后学习了工厂模式,其实借用了模板模式来创建产品,是一种非常重要用处很广的一种方法;

    然后我们学习了单例模式,有懒汉式、饿汉式等,生成关于某个类全局唯一的对象,注意多线程的影响;

    之后是原型模式,用来复制复杂的对象,使用了clone方法,然后是builder模式,用一个新的类对已有的抽象接口进行整合和编程,从而构建出我们想要的东西;

    然后是抽象工厂模式,使用了工厂模式,组合模式等模式,面向抽象编程,将抽象零件组装成抽象产品,便于具体工厂的创建,提高了代码的组件化和复用性;

    然后是桥接模式,将类的功能层次和实现层次分割开来,便于对应的扩展和使用;

    然后是策略模式,可以整体的替换策略,使用也很广泛;然后是组合模式,保证了同根同源,通过委托添加自己构成递归,树形结构,将具有树形特点的对象组合起来;

    然后是装饰器模式,和组合模式的结构类似,同样是递归结构,从而可以不断的装饰,增加新的功能,很好用;

    接着是visitor访问者模式,通过在类外访问类中的数据结构从而得到想要的结果,便于程序的可扩展性和组件化;

    接着是责任链模式,推卸责任,根据问题的大小来考虑自己释放处理,本质是链表,便于职责分明;

    然后是外观模式,通过整合各个类之间的调用关系,组建成了统一的接口(API),便于外部类的调用;

    接着是仲裁者模式,将很多类之间互相关联的关系交给仲裁者处理,省去了各个类之间的嵌套和调动,有利于高内聚和低耦合,思路清晰,便于扩展;

    然后是观察者模式,通过互相委托从而能够在被观察的类发生改变的时候得到相应的改变的信息并且处理;

    然后是备忘录模式,通过在某一时刻的状态保存下来,便于恢复,在游戏中使用的比较多;

    然后是状态模式,将状态当做类,从而职责分明,解除了很多繁琐的if和else这些分支逻辑,便于扩展;

    然后是享元模式,轻量级对象,通过共用不变对象来实现;

    然后是代理模式,懒加载真正的服务器,加快访问速度,代理是帮助服务器代理的;

    然后是命令模式,将命令当做类,通过保存一些列命令,从而能够随时执行这些命令,需要清除命令的本质就是一些操作和数据;

    最后是解释器模式,利用编程原理的方法,来更高层次的封装代码,将自己开发的java代码当做编译系统,从而不用改变java代码只修改更高语言层次的代码就能实现不同的功能。 

    🍅 Java基础教程系列:Java基础教程系列

    🍅 Java学习路线配套文章:搬砖工逆袭Java架构师

    🍅 Java经典面试题大全:10万字208道Java经典面试题总结(附答案)

    🍅 简介:Java领域优质创作者🏆、CSDN哪吒公众号作者✌ 、Java架构师奋斗者💪

    🍅 扫描主页左侧二维码,加入群聊,一起学习、一起进步 

    🍅 欢迎点赞 👍 收藏 ⭐留言 📝   

    添加微信,备注1024,赠送Java学习路线思维导图 

    展开全文
  • android源码设计模式解析与实战.pdf下载 完整版高清

    千次下载 热门讨论 2016-12-02 19:35:05
    本书从Android源码的角度由浅入深地剖析设计模式的运用,让工程师们把设计与模式重视起来,提升自己的设计能力与代码质量。因此本书适合的读者为初、中、高级Android工程师。另外,设计思想都是相通的,其他领域的...
  • 设计模式 | 适配器模式及典型应用

    万次阅读 多人点赞 2018-09-20 01:37:29
    适配器模式 适配器模式(Adapter Pattern):将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。 在...

    适配器模式

    适配器模式(Adapter Pattern):将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。

    在适配器模式中,我们通过增加一个新的适配器类来解决接口不兼容的问题,使得原本没有任何关系的类可以协同工作。

    根据适配器类与适配者类的关系不同,适配器模式可分为对象适配器和类适配器两种,在对象适配器模式中,适配器与适配者之间是关联关系;在类适配器模式中,适配器与适配者之间是继承(或实现)关系。

    角色

    Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。

    Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。

    Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。

    缺省适配器模式(Default Adapter Pattern):当不需要实现一个接口所提供的所有方法时,可先设计一个抽象类实现该接口,并为接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可以选择性地覆盖父类的某些方法来实现需求,它适用于不想使用一个接口中的所有方法的情况,又称为单接口适配器模式。缺省适配器模式是适配器模式的一种变体,其应用也较为广泛。在JDK类库的事件处理包java.awt.event中广泛使用了缺省适配器模式,如WindowAdapter、KeyAdapter、MouseAdapter等。

    示例

    类适配器

    首先有一个已存在的将被适配的类

    public class Adaptee {
        public void adapteeRequest() {
            System.out.println("被适配者的方法");
        }
    }
    

    定义一个目标接口

    public interface Target {
        void request();
    }
    

    怎么才可以在目标接口中的 request() 调用 AdapteeadapteeRequest() 方法呢?

    如果直接实现 Target 是不行的

    public class ConcreteTarget implements Target {
        @Override
        public void request() {
            System.out.println("concreteTarget目标方法");
        }
    }
    

    如果通过一个适配器类,实现 Target 接口,同时继承了 Adaptee 类,然后在实现的 request() 方法中调用父类的 adapteeRequest() 即可实现

    public class Adapter extends Adaptee implements Target{
        @Override
        public void request() {
            //...一些操作...
            super.adapteeRequest();
            //...一些操作...
        }
    }
    

    我们来测试一下

    public class Test {
        public static void main(String[] args) {
            Target target = new ConcreteTarget();
            target.request();
    
            Target adapterTarget = new Adapter();
            adapterTarget.request();
        }
    }
    

    输出

    concreteTarget目标方法
    被适配者的方法
    

    类适配器模式类图

    这样我们即可在新接口 Target 中适配旧的接口或类

    对象适配器

    对象适配器与类适配器不同之处在于,类适配器通过继承来完成适配,对象适配器则是通过关联来完成,这里稍微修改一下 Adapter 类即可将转变为对象适配器

    public class Adapter implements Target{
        // 适配者是对象适配器的一个属性
        private Adaptee adaptee = new Adaptee();
    
        @Override
        public void request() {
            //...
            adaptee.adapteeRequest();
            //...
        }
    }
    

    对象适配器模式类图

    注意这里的 Adapter 是将 Adaptee 作为一个成员属性,而不是继承它

    电压适配器

    再来一个好理解的例子,我们国家的民用电都是 220V,日本是 110V,而我们的手机充电一般需要 5V,这时候要充电,就需要一个电压适配器,将 220V 或者 100V 的输入电压变换为 5V 输出

    定义输出交流电接口,输出220V交流电类和输出110V交流电类

        int outputAC();
    }
    
    public class AC110 implements AC {
        public final int output = 110;
    
        @Override
        public int outputAC() {
            return output;
        }
    }
    
    public class AC220 implements AC {
        public final int output = 220;
    
        @Override
        public int outputAC() {
            return output;
        }
    }
    

    适配器接口,其中 support() 方法用于检查输入的电压是否与适配器匹配,outputDC5V() 方法则用于将输入的电压变换为 5V 后输出

    public interface DC5Adapter {
        boolean support(AC ac);
    
        int outputDC5V(AC ac);
    }
    

    实现中国变压适配器和日本变压适配器

    public class ChinaPowerAdapter implements DC5Adapter {
        public static final int voltage = 220;
        
        @Override
        public boolean support(AC ac) {
            return (voltage == ac.outputAC());
        }
        
        @Override
        public int outputDC5V(AC ac) {
            int adapterInput = ac.outputAC();
            //变压器...
            int adapterOutput = adapterInput / 44;
            System.out.println("使用ChinaPowerAdapter变压适配器,输入AC:" + adapterInput + "V" + ",输出DC:" + adapterOutput + "V");
            return adapterOutput;
        }
    }
    
    public class JapanPowerAdapter implements DC5Adapter {
        public static final int voltage = 110;
    
        @Override
        public boolean support(AC ac) {
            return (voltage == ac.outputAC());
        }
    
        @Override
        public int outputDC5V(AC ac) {
            int adapterInput = ac.outputAC();
            //变压器...
            int adapterOutput = adapterInput / 22;
            System.out.println("使用JapanPowerAdapter变压适配器,输入AC:" + adapterInput + "V" + ",输出DC:" + adapterOutput + "V");
            return adapterOutput;
        }
    }
    

    测试,准备中国变压适配器和日本变压适配器各一个,定义一个方法可以根据电压找到合适的变压器,然后进行测试

    public class Test {
        private List<DC5Adapter> adapters = new LinkedList<DC5Adapter>();
    
        public Test() {
            this.adapters.add(new ChinaPowerAdapter());
            this.adapters.add(new JapanPowerAdapter());
        }
    
        // 根据电压找合适的变压器
        public DC5Adapter getPowerAdapter(AC ac) {
            DC5Adapter adapter = null;
            for (DC5Adapter ad : this.adapters) {
                if (ad.support(ac)) {
                    adapter = ad;
                    break;
                }
            }
            if (adapter == null){
                throw new  IllegalArgumentException("没有找到合适的变压适配器");
            }
            return adapter;
        }
    
        public static void main(String[] args) {
            Test test = new Test();
            AC chinaAC = new AC220();
            DC5Adapter adapter = test.getPowerAdapter(chinaAC);
            adapter.outputDC5V(chinaAC);
    
            // 去日本旅游,电压是 110V
            AC japanAC = new AC110();
            adapter = test.getPowerAdapter(japanAC);
            adapter.outputDC5V(japanAC);
        }
    }
    

    输出

    使用ChinaPowerAdapter变压适配器,输入AC:220V,输出DC:5V
    使用JapanPowerAdapter变压适配器,输入AC:110V,输出DC:5V
    

    适配器模式总结

    主要优点

    1. 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构。
    2. 增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者的复用性,同一个适配者类可以在多个不同的系统中复用。
    3. 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。

    具体来说,类适配器模式还有如下优点:

    • 由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。

    对象适配器模式还有如下优点:

    • 一个对象适配器可以把多个不同的适配者适配到同一个目标;
    • 可以适配一个适配者的子类,由于适配器和适配者之间是关联关系,根据“里氏代换原则”,适配者的子类也可通过该适配器进行适配。

    类适配器模式的缺点如下:

    1. 对于Java、C#等不支持多重类继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者;
    2. 适配者类不能为最终类,如在Java中不能为final类,C#中不能为sealed类;
    3. 在Java、C#等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性。

    对象适配器模式的缺点如下:

    • 与类适配器模式相比,要在适配器中置换适配者类的某些方法比较麻烦。如果一定要置换掉适配者类的一个或多个方法,可以先做一个适配者类的子类,将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行适配,实现过程较为复杂。

    适用场景

    • 系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码。
    • 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。

    源码分析适配器模式的典型应用

    spring AOP中的适配器模式

    在Spring的Aop中,使用的 Advice(通知) 来增强被代理类的功能。

    Advice的类型有:MethodBeforeAdviceAfterReturningAdviceThrowsAdvice

    在每个类型 Advice 都有对应的拦截器,MethodBeforeAdviceInterceptorAfterReturningAdviceInterceptorThrowsAdviceInterceptor

    Spring需要将每个 Advice 都封装成对应的拦截器类型,返回给容器,所以需要使用适配器模式对 Advice 进行转换

    三个适配者类 Adaptee 如下:

    public interface MethodBeforeAdvice extends BeforeAdvice {
        void before(Method var1, Object[] var2, @Nullable Object var3) throws Throwable;
    }
    
    public interface AfterReturningAdvice extends AfterAdvice {
        void afterReturning(@Nullable Object var1, Method var2, Object[] var3, @Nullable Object var4) throws Throwable;
    }
    
    public interface ThrowsAdvice extends AfterAdvice {
    }
    

    目标接口 Target,有两个方法,一个判断 Advice 类型是否匹配,一个是工厂方法,创建对应类型的 Advice 对应的拦截器

    public interface AdvisorAdapter {
        boolean supportsAdvice(Advice var1);
    
        MethodInterceptor getInterceptor(Advisor var1);
    }
    

    三个适配器类 Adapter 分别如下,注意其中的 Advice、Adapter、Interceptor之间的对应关系

    class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
    	@Override
    	public boolean supportsAdvice(Advice advice) {
    		return (advice instanceof MethodBeforeAdvice);
    	}
    
    	@Override
    	public MethodInterceptor getInterceptor(Advisor advisor) {
    		MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
    		return new MethodBeforeAdviceInterceptor(advice);
    	}
    }
    
    @SuppressWarnings("serial")
    class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {
    	@Override
    	public boolean supportsAdvice(Advice advice) {
    		return (advice instanceof AfterReturningAdvice);
    	}
    	@Override
    	public MethodInterceptor getInterceptor(Advisor advisor) {
    		AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
    		return new AfterReturningAdviceInterceptor(advice);
    	}
    }
    
    class ThrowsAdviceAdapter implements AdvisorAdapter, Serializable {
    	@Override
    	public boolean supportsAdvice(Advice advice) {
    		return (advice instanceof ThrowsAdvice);
    	}
    	@Override
    	public MethodInterceptor getInterceptor(Advisor advisor) {
    		return new ThrowsAdviceInterceptor(advisor.getAdvice());
    	}
    }
    

    客户端 DefaultAdvisorAdapterRegistry

    public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {
        private final List<AdvisorAdapter> adapters = new ArrayList(3);
    
        public DefaultAdvisorAdapterRegistry() {
            // 这里注册了适配器
            this.registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
            this.registerAdvisorAdapter(new AfterReturningAdviceAdapter());
            this.registerAdvisorAdapter(new ThrowsAdviceAdapter());
        }
        
        public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
            List<MethodInterceptor> interceptors = new ArrayList(3);
            Advice advice = advisor.getAdvice();
            if (advice instanceof MethodInterceptor) {
                interceptors.add((MethodInterceptor)advice);
            }
    
            Iterator var4 = this.adapters.iterator();
    
            while(var4.hasNext()) {
                AdvisorAdapter adapter = (AdvisorAdapter)var4.next();
                if (adapter.supportsAdvice(advice)) {   // 这里调用适配器方法
                    interceptors.add(adapter.getInterceptor(advisor));  // 这里调用适配器方法
                }
            }
    
            if (interceptors.isEmpty()) {
                throw new UnknownAdviceTypeException(advisor.getAdvice());
            } else {
                return (MethodInterceptor[])interceptors.toArray(new MethodInterceptor[0]);
            }
        }
        // ...省略...
    }    
    

    这里看 while 循环里,逐个取出注册的适配器,调用 supportsAdvice() 方法来判断 Advice 对应的类型,然后调用 getInterceptor() 创建对应类型的拦截器

    spring aop 适配器模式

    这里应该属于对象适配器模式,关键字 instanceof 可看成是 Advice 的方法,不过这里的 Advice 对象是从外部传进来,而不是成员属性

    spring JPA中的适配器模式

    在Spring的ORM包中,对于JPA的支持也是采用了适配器模式,首先定义了一个接口的 JpaVendorAdapter,然后不同的持久层框架都实现此接口。

    jpaVendorAdapter:用于设置实现厂商JPA实现的特定属性,如设置Hibernate的是否自动生成DDL的属性generateDdl;这些属性是厂商特定的,因此最好在这里设置;目前Spring提供 HibernateJpaVendorAdapterOpenJpaVendorAdapterEclipseLinkJpaVendorAdapterTopLinkJpaVendorAdapter 四个实现。其中最重要的属性是 database,用来指定使用的数据库类型,从而能根据数据库类型来决定比如如何将数据库特定异常转换为Spring的一致性异常,目前支持如下数据库(DB2、DERBY、H2、HSQL、INFORMIX、MYSQL、ORACLE、POSTGRESQL、SQL_SERVER、SYBASE)

    public interface JpaVendorAdapter
    {
      // 返回一个具体的持久层提供者
      public abstract PersistenceProvider getPersistenceProvider();
    
      // 返回持久层提供者的包名
      public abstract String getPersistenceProviderRootPackage();
    
      // 返回持久层提供者的属性
      public abstract Map<String, ?> getJpaPropertyMap();
    
      // 返回JpaDialect
      public abstract JpaDialect getJpaDialect();
    
      // 返回持久层管理器工厂
      public abstract Class<? extends EntityManagerFactory> getEntityManagerFactoryInterface();
    
      // 返回持久层管理器
      public abstract Class<? extends EntityManager> getEntityManagerInterface();
    
      // 自定义回调方法
      public abstract void postProcessEntityManagerFactory(EntityManagerFactory paramEntityManagerFactory);
    }
    

    我们来看其中一个适配器实现类 HibernateJpaVendorAdapter

    public class HibernateJpaVendorAdapter extends AbstractJpaVendorAdapter {
        //设定持久层提供者
        private final PersistenceProvider persistenceProvider;
        //设定持久层方言
        private final JpaDialect jpaDialect;
    
        public HibernateJpaVendorAdapter() {
            this.persistenceProvider = new HibernatePersistence();
            this.jpaDialect = new HibernateJpaDialect();
        }
    
        //返回持久层方言
        public PersistenceProvider getPersistenceProvider() {
            return this.persistenceProvider;
        }
    
        //返回持久层提供者
        public String getPersistenceProviderRootPackage() {
            return "org.hibernate";
        }
    
        //返回JPA的属性
        public Map<String, Object> getJpaPropertyMap() {
            Map jpaProperties = new HashMap();
    
            if (getDatabasePlatform() != null) {
                jpaProperties.put("hibernate.dialect", getDatabasePlatform());
            } else if (getDatabase() != null) {
                Class databaseDialectClass = determineDatabaseDialectClass(getDatabase());
                if (databaseDialectClass != null) {
                    jpaProperties.put("hibernate.dialect",
                            databaseDialectClass.getName());
                }
            }
    
            if (isGenerateDdl()) {
                jpaProperties.put("hibernate.hbm2ddl.auto", "update");
            }
            if (isShowSql()) {
                jpaProperties.put("hibernate.show_sql", "true");
            }
    
            return jpaProperties;
        }
    
        //设定数据库
        protected Class determineDatabaseDialectClass(Database database)     
        {                                                                                       
            switch (1.$SwitchMap$org$springframework$orm$jpa$vendor$Database[database.ordinal()]) 
            {                                                                                     
            case 1:                                                                             
              return DB2Dialect.class;                                                            
            case 2:                                                                               
              return DerbyDialect.class;                                                          
            case 3:                                                                               
              return H2Dialect.class;                                                             
            case 4:                                                                               
              return HSQLDialect.class;                                                           
            case 5:                                                                               
              return InformixDialect.class;                                                       
            case 6:                                                                               
              return MySQLDialect.class;                                                          
            case 7:                                                                               
              return Oracle9iDialect.class;                                                       
            case 8:                                                                               
              return PostgreSQLDialect.class;                                                     
            case 9:                                                                               
              return SQLServerDialect.class;                                                      
            case 10:                                                                              
              return SybaseDialect.class; }                                                       
            return null;              
        }
    
        //返回JPA方言
        public JpaDialect getJpaDialect() {
            return this.jpaDialect;
        }
    
        //返回JPA实体管理器工厂
        public Class<? extends EntityManagerFactory> getEntityManagerFactoryInterface() {
            return HibernateEntityManagerFactory.class;
        }
    
        //返回JPA实体管理器
        public Class<? extends EntityManager> getEntityManagerInterface() {
            return HibernateEntityManager.class;
        }
    }
    

    配置文件中可以这样指定

    <bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> 
       <property name="generateDdl" value="false" />  
       <property name="database" value="HSQL"/>  
    </bean>  
    <bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>  
    

    spring MVC中的适配器模式

    Spring MVC中的适配器模式主要用于执行目标 Controller 中的请求处理方法。

    在Spring MVC中,DispatcherServlet 作为用户,HandlerAdapter 作为期望接口,具体的适配器实现类用于对目标类进行适配,Controller 作为需要适配的类。

    为什么要在 Spring MVC 中使用适配器模式?Spring MVC 中的 Controller 种类众多,不同类型的 Controller 通过不同的方法来对请求进行处理。如果不利用适配器模式的话,DispatcherServlet 直接获取对应类型的 Controller,需要的自行来判断,像下面这段代码一样:

    if(mappedHandler.getHandler() instanceof MultiActionController){  
       ((MultiActionController)mappedHandler.getHandler()).xxx  
    }else if(mappedHandler.getHandler() instanceof XXX){  
        ...  
    }else if(...){  
       ...  
    }  
    

    这样假设如果我们增加一个 HardController,就要在代码中加入一行 if(mappedHandler.getHandler() instanceof HardController),这种形式就使得程序难以维护,也违反了设计模式中的开闭原则 – 对扩展开放,对修改关闭。

    我们来看看源码,首先是适配器接口 HandlerAdapter

    public interface HandlerAdapter {
        boolean supports(Object var1);
    
        ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;
    
        long getLastModified(HttpServletRequest var1, Object var2);
    }
    

    现该接口的适配器每一个 Controller 都有一个适配器与之对应,这样的话,每自定义一个 Controller 需要定义一个实现 HandlerAdapter 的适配器。

    springmvc 中提供的 Controller 实现类有如下

    spring mvc Controller 提供的实现类

    springmvc 中提供的 HandlerAdapter 实现类如下

    spring mvc HandlerAdapter 提供的实现类

    HttpRequestHandlerAdapter 这个适配器代码如下

    public class HttpRequestHandlerAdapter implements HandlerAdapter {
        public HttpRequestHandlerAdapter() {
        }
    
        public boolean supports(Object handler) {
            return handler instanceof HttpRequestHandler;
        }
    
        public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            ((HttpRequestHandler)handler).handleRequest(request, response);
            return null;
        }
    
        public long getLastModified(HttpServletRequest request, Object handler) {
            return handler instanceof LastModified ? ((LastModified)handler).getLastModified(request) : -1L;
        }
    }
    

    当Spring容器启动后,会将所有定义好的适配器对象存放在一个List集合中,当一个请求来临时,DispatcherServlet 会通过 handler 的类型找到对应适配器,并将该适配器对象返回给用户,然后就可以统一通过适配器的 hanle() 方法来调用 Controller 中的用于处理请求的方法。

    public class DispatcherServlet extends FrameworkServlet {
        private List<HandlerAdapter> handlerAdapters;
        
        //初始化handlerAdapters
        private void initHandlerAdapters(ApplicationContext context) {
            //..省略...
        }
        
        // 遍历所有的 HandlerAdapters,通过 supports 判断找到匹配的适配器
        protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    		for (HandlerAdapter ha : this.handlerAdapters) {
    			if (logger.isTraceEnabled()) {
    				logger.trace("Testing handler adapter [" + ha + "]");
    			}
    			if (ha.supports(handler)) {
    				return ha;
    			}
    		}
    	}
    	
    	// 分发请求,请求需要找到匹配的适配器来处理
    	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    		HttpServletRequest processedRequest = request;
    		HandlerExecutionChain mappedHandler = null;
    
    		// Determine handler for the current request.
    		mappedHandler = getHandler(processedRequest);
    			
    		// 确定当前请求的匹配的适配器.
    		HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    
    		ha.getLastModified(request, mappedHandler.getHandler());
    					
    		mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
        }
    	// ...省略...
    }	
    

    通过适配器模式我们将所有的 controller 统一交给 HandlerAdapter 处理,免去了写大量的 if-else 语句对 Controller 进行判断,也更利于扩展新的 Controller 类型。

    参考:
    刘伟:设计模式Java版
    慕课网java设计模式精讲 Debug 方式+内存分析
    孤落:Spring MVC中的适配器模式
    ToughMind_:深入浅出设计模式(五):7.适配器模式

    推荐阅读

    设计模式 | 简单工厂模式及典型应用
    设计模式 | 工厂方法模式及典型应用
    设计模式 | 抽象工厂模式及典型应用
    设计模式 | 建造者模式及典型应用
    设计模式 | 原型模式及典型应用
    设计模式 | 外观模式及典型应用
    设计模式 | 装饰者模式及典型应用

    更多内容可访问我的个人博客:http://laijianfeng.org

    关注【小旋锋】微信公众号

    展开全文
  • Java设计模式之结构型:桥接模式

    万次阅读 2021-09-13 17:26:40
    桥接模式将系统的抽象部分与实现部分分离解耦,使他们可以独立的变化。为了达到让抽象部分和实现部分独立变化的目的,桥接模式使用组合关系来代替继承关系,抽象部分拥有实现部分的接口对象,从而能够通过这个接口...

    一、什么是桥接模式:

            桥接,顾名思义,就是用来连接两个部分,使得两个部分可以互相通讯,桥接模式的作用就是为被分离的抽象部分和实现部分搭桥。在现实生活中一个物品在搭配不同的配件时会产生不同的动作和结果,例如一辆赛车搭配的是硬胎或者是软胎就能够在干燥的马路上行驶,而如果要在下雨的路面行驶,就需要搭配雨胎了,这种根据行驶的路面不同,需要搭配不同的轮胎的变化的情况,我们从软件设计的角度来分析,就是一个系统由于自身的逻辑,会有两个或多个维度的变化,而为了应对这种变化,我们就可以使用桥接模式来进行系统的解耦。 桥接模式将一个系统的抽象部分和实现部分分离,使它们都可以独立地进行变化,对应到上面就是赛车的种类可以相对变化,轮胎的种类可以相对变化,形成一种交叉的关系,最后的结果就是一种赛车对应一种轮胎就能够成功产生一种结果和行为。 

            桥接模式将系统的抽象部分与实现部分分离解耦,使他们可以独立的变化。为了达到让抽象部分和实现部分独立变化的目的,桥接模式使用组合关系来代替继承关系,抽象部分拥有实现部分的接口对象,从而能够通过这个接口对象来调用具体实现部分的功能。也就是说,桥接模式中的桥接是一个单方向的关系,只能够抽象部分去使用实现部分的对象,而不能反过来。 

            桥接模式符合“开闭原则”,提高了系统的可拓展性,在两个变化维度中任意扩展一个维度,都不需要修改原来的系统;并且实现细节对客户不透明,可以隐藏实现细节。但是由于聚合关系建立在抽象层,要求开发者针对抽象进行编程,这增加系统的理解和设计难度。

            所以,桥接模式一般适用于以下几种应用场景:

    • (1)系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,则可以通过桥接模式使他们在抽象层建立一个关联关系;
    • (2)系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时
    • (3)一个类存在两个独立变化的维度,而这两个维度都需要进行扩展。

    二、UML结构图:

    • 抽象化角色 Abstraction:定义抽象的接口,包含一个对实现化角色的引用,抽象角色的方法需要调用实现化角色;
    • 扩展抽象化角色 RefinedAbstraction:抽象化角色的子类,一般对抽象部分的方法进行完善和扩展,实现父类中的业务方法,并通过组合/聚合关系调用实现化角色中的业务方法
    • 实现化角色 Implementor:定义具体行为、具体特征的应用接口,供扩展抽象化角色使用,一般情况下是由实现化角色提供基本的操作,而抽象化角色定义基于实现部分基本操作的业务方法;
    • 具体实现化角色 ConcreteImplementor:完善实现化角色中定义的具体逻辑。

    三、代码实现:

    Implementor 接口类:

    public interface Implementor {
        void operationImpl();
    }

    ConcreteImplementor 接口实现类:

    public class ConcreteImplementorA implements Implementor{
        @Override
        public void operationImpl() {
            //具体实现
        }
    }
    
    public class ConcreteImplementorB implements Implementor{
        @Override
        public void operationImpl() {
            //具体实现
        }
    }

    Abstraction 抽象类:

    public abstract class Abstraction {
        private Implementor implementor;
    
        public Abstraction(Implementor implementor) {
            this.implementor = implementor;
        }
    
        public void operation() {
            implementor.operationImpl();
        }
    }

    RefinedAbstraction 抽象类的具体实现:

    public class RefinedAbstraction extends Abstraction{
        public RefinedAbstraction(Implementor implementor) {
            super(implementor);
        }
    
        public void refinedOperation() {
            //对 Abstraction 中的 operation 方法进行扩展
        }
    }

            看了这段通用代码之后,桥接模式的结构应该就很清楚了,需要注意的是 RefinedAbstraction 根据实际情况是可以有多个的。 当然上面的 UML 类图和通用代码只是最常用的实现方式而已,在实际使用中可能会有其他的情况,比如 Implementor 只有一个类的情况,虽然这时候可以不去创建 Implementor 接口,精简类的层次,但是我建议还是需要抽象出实现部分的接口。

    四、JDBC源码解析-桥接模式:

    该部分引用自:JDBC和桥接模式 - 枯落 - 博客园

            Java 中,我们使用 JDBC 连接数据库时,在各个数据库之间进行切换,基本不需要动太多的代码,原因就是使用了桥接模式,JDBC 提供统一接口,每种类型的数据库提供各自的实现,然后由桥接类创建一个连接数据库的驱动,使用某一个数据库的时候只需要切换一下就行。接下来我们就对 JDBC 的源码做下剖析:

    通过原生JDBC API连接MySQL数据库,则有如下示例代码:

    Class.forName("com.mysql.cj.jdbc.Driver");
    Connection conn = DriverManager.getConnection("jdbc:mysql://<host>:<port>/<database>");

    短短两行代码难以看出桥接模式的结构,下面先对源码进行一定的分析,理解各个类和接口之间的关系:

    1、源码分析:

    (1)Class.forName() 方法:

            该方法将返回与给定字符串名的类或接口相关联的 java.lang.Class 类对象,用于在程序运行时动态加载该类或该接口到当前线程中,如果 Class.forName() 加载的是一个类,也会执行类中包含的static { } 静态代码段

    (2)com.mysql.cj.jdbc.Driver 类:

            MySQL 将具体的 java.sql.Driver 接口的实现放到了 NonRegisteringDriver 中,com.mysql.cj.jdbc.Driver 类仅包含一段静态代码,具体类图如下:

            其中最关键的是静态代码段中的 DriverManager.registerDriver(new Driver()) ,它会在客户端调用Class.forName() 方法加载 com.mysql.cj.jdbc.Driver 类的同时被执行,Driver 类自身的一个实例被注册到 DriverManager(即保存到 DriverManager 的静态字段 registeredDrivers 内),注册过程的源码如下: 

    public static synchronized void registerDriver(java.sql.Driver driver, DriverAction da)
    throws SQLException {
      /* Register the driver if it has not already been added to our list */
      if(driver != null) {
        registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
      } else {
        // This is for compatibility with the original DriverManager
        throw new NullPointerException();
      }
      println("registerDriver: " + driver);
    }

            registeredDrivers 静态字段的类型是实现了 List 接口的 CopyOnWriteArrayList 类,它能够保存进一步封装 java.sql.Driver 接口的 DriverInfo 类实例,DriverInfo 类的声明代码如下:

    class DriverInfo {
      final Driver driver;
      DriverAction da;
      DriverInfo(Driver driver, DriverAction action) {
        this.driver = driver;
        da = action;
      }
      // ……
    }

    DriverInfo 还包装了 DriverAction,DriverAction 会在Driver被取消注册时被调用,在 MySQL 的 Driver 在向 DriverManager 进行注册时,DriverAction 被设置为 null

    (3)DriverManager 类:

    由上面的分析可得,Class.forName() 方法调用后,com.mysql.cj.jdbc.Driver 类被加载,并执行static { } 静态代码段,将 com.mysql.cj.jdbc.Driver 类实例注册到 DriverManager 中。然后,客户端会调用 DriverManager.getConnection() 方法获取一个 Connection 数据库连接实例,该方法的部分源码如下:

    private static Connection getConnection(String url, java.util.Properties info, Class<?> caller) throws SQLException {
      // ……
      for(DriverInfo aDriver : registeredDrivers) {
        // If the caller does not have permission to load the driver then
        // skip it.
        if(isDriverAllowed(aDriver.driver, callerCL)) {
          try {
            println(" trying " + aDriver.driver.getClass().getName());
            Connection con = aDriver.driver.connect(url, info);
            if (con != null) {
              // Success!
              println("getConnection returning " + aDriver.driver.getClass().getName());
              return (con);
            }
          } catch (SQLException ex) {
            if (reason == null) {
              reason = ex;
            }
          }
        } else {
          println(" skipping: " + aDriver.getClass().getName());
        }
      }
      // ……
    }

    DriverManager.getConnection() 方法会遍历 registeredDrivers 静态字段,获取字段内保存的每一个 Driver 来尝试响应客户端的数据库连接请求,若所有 Driver 都连接数据库失败,则提示连接失败信息

    (4)Connection接口:

    Connection 代表和特定数据库的连接会话,能够执行SQL语句并在连接的上下文中返回执行结果。因此,DriverManager.getConnection() 方法返回的 Connection 数据库连接实例根据不同的数据库有不同的实现,MySQL 的 Connection 接口实现关系如下:

     2、源码类图:

    根据源码的分析,绘制类图如下:

     对 Driver 和 Connection 进行抽象,绘制类图如下:

            桥接模式通过聚合关系代替继承关系,实现抽象化和实现化部分的解耦。以上述 JDBC 在 MySQL 中的简略类图为例,抽象化部分有 DriverManager,实现化部分有 Driver 接口和 Connection 接口。对于不同的数据库,Driver接口和Connection接口都有自己独特的实现类。

            但是,和 Driver 接口不同的是,Connection 接口与 DriverManager 类的关系只是联系较弱的依赖关系,并不符合桥接模式的定义和特点。因此,在考虑桥接模式的情况下,可以再次将类图进行简化:

     最后,我们将其它数据库的Driver接口实现也考虑在内,绘制类图如下:

            桥接模式中的实现化角色 (Implementor) 对应上图的 Driver 接口,具体实现化 (Concrete Implementor) 角色对应 MysqlDriver、OracleDriver 和 MariadbDriver,扩展抽象化 (Refined Abstraction) 角色对应 DriverManager,不具有抽象化 (Abstraction) 角色作为扩展抽象化角色的父类

    3、对 JDBC 的观点:

    (1)观点一:JDBC 的桥接模式是一中简化的桥接模式

            桥接模式的主要应用场景是某个类存在两个独立变化的维度,且这两个维度都需要进行扩展,而现在仅有 Driver 一个变化维度,DriverManager 没有抽象化父类,它本身也没有任何子类,因此我认为,在 JDBC 中,是一种简化的桥接模式。

            倘若 JDBC 针对 Connection 接口的设计不是将它作为 Driver 和 DriverManager 的"依赖"来处理,而是也作为一个变化的维度加入到桥接模式,或许能够更好地体现JDBC对桥接模式的实现,一种"假想"的桥接模式如下:

     (2)观点二:JDBC采用的是策略模式而不是桥接模式

    问题来源知乎:jdbc是桥接模式还是策略模式? - 知乎

            因为这确实和策略模式十分相似,如果把桥接模式的抽象部分简化来看,不去设计Abstraction,也就是用 Refined Abstraction 代替 Abstraction,那么就类似于策略模式的 Context 来使用接口的对象。

            但是,桥接模式和策略模式的目的是不一样的,策略模式属于对象行为模式(描述对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,以及怎样分配职责),它的目的是封装一系列的算法,使得算法可以相互替代,并在程序运行的不同时刻选择合适的算法。而桥接模式属于对象结构模式(描述如何将对象按某种布局组成更大的结构),它的目的是将抽象与实现分离,使它们可以独立变化

            因此,从设计的目的来看,JDBC采用的并不是策略模式,在一段程序中数据库驱动并不存在频繁地相互替换

    (3)观点三:变化的维度一个是平台,另一个是数据库

    问题来源:https://www.unclewang.info/learn/java/771/?tdsourcetag=s_pctim_aiomsg

            这是我认同的一个观点,引用原文的话:变的是平台和数据库,平台在 JVM 这个层面就解决了,因为所有操作系统 Java 基本都会提供对应JDK,这也是 "Once Write,Run AnyWhere" 的原因。而数据库则是依托公司的具体实现,各个公司都提供对应的 Driver 类,我用 DriverManager 类进行懒加载.

            考虑数据库的实际应用场景,我们可能在不同的操作系统上使用不同的数据库,但是JVM的平台无关性使得我们不再有操作系统层面上的变化。假设不存在JVM,那么不同的客户端加载和运行数据库驱动程序的代码自然也各有不同,即 DriverManager 会因操作系统的变化而变化,不同的操作系统可以有不同的注册 Driver 的方式,不过因为存在JVM,我们现在不再有"平台"这一变化维度了

    (4)观点四:变化的维度一个是客户端应用系统,另一个是数据库

    问题来源:java设计模式-桥梁模式(桥接模式 Bridge) - 简书

            一个比较独特的观点,引用原文的话:应用系统作为一个等级结构,与 JDBC 驱动器这个等级结构是相对独立的,它们之间没有静态的强关联。应用系统通过委派与JDBC驱动器相互作用,这是一个桥梁模式的例子。

            原文笔者不认为 DriverManager 作为 Refined Abstraction 角色存在,而是视作两个变化维度之间的一个"过渡",原本的"桥"是 Abstraction 和 Implementor 之间的组合/聚合关系,而现在DriverManager 类本身成为了"桥",可以看作是桥梁模式的一个变体

    (5)观点五:变化的维度一个是 Driver,一个是 Connection:

            如果从观点四的原文笔者的角度看,把 DriverManager 类本身作为"桥",那么我们还可以提出一种新的观点,绘制类图如下:


    设计模式系列文章:

    Java设计模式之创建型:工厂模式详解(简单工厂+工厂方法+抽象工厂)

    Java设计模式之创建型:建造者模式

    Java设计模式之创建型:单例模式

    Java设计模式之创建型:原型模式

    Java设计模式之结构型:适配器模式

    Java设计模式之结构型:装饰器模式

    Java设计模式之结构型:代理模式

    Java设计模式之结构型:桥接模式

    Java设计模式之结构型:外观模式

    Java设计模式之结构型:组合模式

    Java设计模式之结构型:享元模式

    Java设计模式之行为型:策略模式

    Java设计模式之行为型:模板方法模式

    Java设计模式之行为型:责任链模式

    Java设计模式之行为型:观察者模式

    Java设计模式之行为型:访问者模式

    Java设计模式之行为型:中介者模式

    Java设计模式之行为型:命令模式

    Java设计模式之行为型:状态模式

    Java设计模式之行为型:备忘录模式

    Java设计模式之行为型:迭代器模式

    Java设计模式之行为型:解释器模式


    参考博客:

    JDBC和桥接模式 - 枯落 - 博客园

    java/android 设计模式学习笔记(8)---桥接模式_Shawn_Dut的专栏-CSDN博客_桥接模式

    JAVA开发的23种设计模式之 --- 桥接模式_叶孤心的专栏-CSDN博客

    展开全文
  • 软件秘笈:设计模式那点事 下载

    千次下载 热门讨论 2016-12-03 14:36:47
    在第1章软件设计模式概述后,从第2章到第24章诠释23个软件设计模式。每一种都以一个生活故事开始,然后是模式定义、模式分析、模式实现、设计原则和使用场合。模式实现通过Eclipse中的Java工程展开,采用软件编程...
  • java的23种设计模式视频---免费共享

    千次下载 热门讨论 2016-01-26 01:55:48
    java的23种设计模式视频,非常给力,不收费了,免费共享给大家,觉得不错的留个言!
  • win10如何进入安全模式删除文件,卸载 1、首先大家进入win10系统,然后使用window键+R组合键点击运行框,写入“msconfig”回车进入系统配置,如下图: 2:以后大家在点击的系统配置中,找到"引导"选项,然后单击,...
  • Android源码设计模式解析与实战.PDF(完整版)

    千次下载 热门讨论 2016-07-01 11:45:30
    Android源码设计模式解析与实战.PDF(完整版),文件中有百度网盘下载链接。
  • 设计模式——设计模式概述

    万次阅读 多人点赞 2019-10-12 08:07:49
    软件设计模式(Design pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。设计模式主要是为了解决某类重复出现的问题而出现的一套成功或有效的解决方案。设计模式提供...
  • 发布订阅模式与观察者模式

    万次阅读 多人点赞 2019-03-29 18:25:12
    设计模式并非是软件开发的专业术语,实际上,“模式”最早诞生于建筑学。 设计模式的定义是:在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案。通俗一点说,设计模式是在某种场合下对某个问题的一种...
  • 关系,关系模式,关系模型区别和联系

    万次阅读 多人点赞 2019-12-18 09:40:11
    关系模式:关系的描述 关系模型:关系模型由关系数据结构,关系操作集合,关系完整性约束三部分组成. 关系和关系模式的区别 关系模式是型,关系是值,关系模式是对关系的描述 关系是关系模式在某一个时刻的状态或者内容,...
  • JAVA设计模式之单例模式

    万次阅读 多人点赞 2014-04-16 06:51:34
    本文继续介绍23种设计模式系列之单例模式。 概念:  java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。  单例模式有以下特点:  1、单例类...
  • 互联网电商都是怎么用工厂模式的?

    万次阅读 多人点赞 2021-04-19 23:26:18
    简单的模式,不简单的场景
  • 大致按照模式的应用目标分类,设计模式可以分为创建型模式、结构型模式和行为型模式。 创建型模式,是对对象创建过程的各种问题和解决方案的总结,包括各种工厂模式(Factory、Abstract Factory)、单例模式...
  • 《设计模式实训教程》【PPT+类图与代码+样章】

    千次下载 热门讨论 2012-12-05 21:39:30
    6.2.6访问者模式、组合模式与迭代器模式联用 6.3综合实例实训 6.3.1多人联机射击游戏 6.3.2数据库同步系统 6.4实训练习 附录A参考答案 A.1第1章实训练习参考答案 A.2第2章实训练习参考答案 A.3第3章实训练习...
  • 设计模式(一):策略模式

    万次阅读 多人点赞 2019-03-01 21:37:32
    设计模式(一):策略模式 本文将以一个小Demo及其新需求来分析使用策略模式的好处。 设计模式简述: 设计模式: 1.设计模式是人们在面对同类型软件工程设计问题所总结出的一些有用的经验。模式不是代码,而是某类...
  • 本书主要内容分为12章,包括绪论、VisualC++数字图像处理基础、图像特征、统计模式识别、模式识别决策方法及实现,以及人脸检测与特征点定位、汽车牌照识别、脑部医学影像诊断、印刷体汉字识别、手写体数字识别、一...
  • 设计模式概述(Design pattern): 设计模式:代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员...
  • 工厂模式将目的将创建对象的具体过程屏蔽隔离起来,从而达到更高的灵活性,工厂模式可以分为三类:简单工厂模式、工厂方法模式、抽象工厂模式;简单工厂模式的核心是定义一个创建对象的接口,将对象的创建和本身的...
  • 设计模式 | 外观模式及典型应用

    万次阅读 2018-09-16 20:59:32
    前言 本文的主要内容: ...外观模式是一种使用频率非常高的结构型设计模式,它通过引入一个外观角色来简化客户端与子系统之间的交互,为复杂的子系统调用提供一个统一的入口,降低子系统与客户...
  • 设计模式(二):几种工厂模式详解

    万次阅读 2020-05-10 21:07:43
    ​ 工厂顾名思义就是创建产品,根据产品是具体产品还是具体工厂可分为简单工厂模式和工厂方法模式,根据工厂的抽象程度可分为工厂方法模式和抽象工厂模式。该模式用于封装和管理对象的创建,是一种创建型模式。 工厂...
  • 数据库三级模式结构

    万次阅读 多人点赞 2018-03-08 17:24:12
    一、三级模式结构数据库系统的三级模式结构是指模式、外模式和内模式。1、模式模式也称为逻辑模式或概念模式,是数据库中全体数据的逻辑结构和特征的描述,是所有用户的公共数据视图。一个数据库只有一个模式模式...
  • 非常全的23种设计模式详解

    万次阅读 2018-12-08 11:28:31
    从招式与内功谈起——设计模式概述(一):设计模式从何而来? 从招式与内功谈起——设计模式概述(二):设计模式是什么? 从招式与内功谈起——设计模式概述(三):设计模式有什么用?附:个人观点   面向...
  • 大端模式和小端模式

    万次阅读 多人点赞 2018-08-28 17:09:25
    一.概念 大端模式(Big-endian):高位字节排放在内存的低地址端,低位字节排放在...例(无论是小端模式还是大端模式。每个字节内部都是按顺序排列): 1)大端模式: 低地址 -----------------&gt; 高地...
  • 从设计模式到恋爱宝典,程序员们的福利来了!

    千次阅读 多人点赞 2019-12-20 17:39:27
    1、Factory(工厂模式)2、Builder(建造模式)3、Factory Method(工厂方法模式)4、Prototype(原始模型模式)5、Singleton(单例模式)6、Adapter(适配器模式)7、Bridge(桥接模式)8、Composite(合成模式)9...
  • 设计模式 | 备忘录模式及典型应用

    千次阅读 2018-10-25 00:56:18
    介绍备忘录模式 示例 备忘录模式总结 备忘录模式 备忘录模式经常可以遇到,譬如下面这些场景: 浏览器回退:浏览器一般有浏览记录,当我们在一个网页上点击几次链接之后,可在左上角点击左箭头回退到上一次的页面...
  • 设计模式(十四)中介者模式

    千次阅读 多人点赞 2017-03-06 12:31:56
    写了很多篇设计模式的文章,才发现没有讲关于设计模式的分类,那么这一篇就补上这一内容,顺便带来中介者模式的讲解。并与此前讲过的代理模式和外观模式做对比。
  • 设计模式(二)结构型模式

    万次阅读 2018-08-04 20:58:08
    Java 中一般认为有23 种设计模式,我们不需要所有的都会,但是其中常用的几种设计模式应该去掌握。 下面列出了所有的设计模式。需要掌握的设计模式我单独列出来了,当然能掌握的越多越好。 总体来说设计模式分为三...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,977,092
精华内容 2,390,836
关键字:

模式