category
学习思考
date
Dec 18, 2022
icon
Origin
password
slug
design_pattern
status
Published
summary
关于设计模式的学习和思考
tags
Tags
type
Post
设计模式7大原则:
1、单一职责原则
2、接口隔离原则
3、依赖倒转原则
4、里氏替换原则
5、开闭原则ocp
6、迪米特法则
7、合成复用原则
金融借贷平台:
借贷平台的订单有审核,发布,抢单的等状态。随着操作的不同会更改订单的状态。项目中这个过程实现就会使用状态模式。
这类代码如果使用if/else 在添加一种功能的时候要对所有状态进行判断。因此代码会变得越来越臃肿,并且一旦没有处理某个状态就会出现严重的bug。难以维护。
单一职责原则:
对类来说的,即一个类应该只负责一项职责,如果A类负责两个不同职责:职责1、职责2。当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的力度分解为A1,A2。
应用实例:
交通工具案例
单一职责原则注意事项和细节:
1、降低类的复杂度,一个类只负责一项职责。
2、提高累的可读性,可维护性
3、降低变更引起的风险
4、通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则;只有类中方法数量足够少,可以在方法级别保持单一职责原则
接口隔离原则:
客户端不应该依赖它不需要的借口,即一个类对另一个类的依赖应该建立在最小的接口上。
依赖倒转原则:
依赖倒转原则(Dependence Inversion Principle)是指:
1)、高层模块不应该依赖底层模块,二者都应该依赖其抽象
2)、抽象不应该依赖细节,细节应该依赖抽象
3)、依赖倒转(倒置)的中心思想是面向接口编程
4)、依赖倒转(倒置)原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的框架比以细节为基础的架构要稳定的多。在java中,抽象指的是接口或者抽象类,细节就是具体的实现类。
5)、使用接口或者抽象类的目的是制定好规范,而不设计任何具体的操作,把战线细节的任务交给他们的实现类去完成。
依赖关系传递的三种方式和应用案例:
1)、接口传递
2)、构造方法传递
3)、setter方式传递
依赖倒转原则的注意事项和细节:
1)、低层模块尽量都要有抽象类或者接口,或者两者都有,程序稳定性更好。
2)、变量的声明类型尽量是抽象类或者接口,这样我们的变量引用和世界对象间,就存在一个缓冲层,易于程序扩展和优化
3)、继承时遵循里氏替换原则
里氏替换原则:
1)、继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有子类必须遵循这些契约,但是如果子类对这些已经实现的放啊任意修改,就会对整个继承体系造成破坏。
2)、继承在给程序设计带来便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,增加对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有子类,并且父类修改后,所有涉及到子类的功能都有可能产生故障。
3)、如何正确的使用继承?=====》 里氏替换原则
如果对每个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有繁盛变化,那么类型T2室类型 T1的子类型,换句话说,所有引用基类的地方必须能透明的使用其子类的对象。
在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类方法
里氏替换原则告诉我,继承实际上让啷个类耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖来解决问题。
开闭原则:
1)、开闭原则是编程中最基础、最重要的设计原则
2)、一个软件实体如类、模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节。
3)、当软件需要变化时,尽量用过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
4)、编程中遵循其他原则,以及使用设计模式的目的就是遵循开闭原则。
迪米特法则:
1)、一个对象应该对其他对象保持最少的了解
2)、类与类关系越密切,耦合度越大
3)、迪米特法则又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外储了提供的public方法,不对外泄露任何信息。
4)、迪米特法则还有个更简单的定义,只与直接的朋友通信
5)、直接的朋友:没个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖,关联,组合,聚合等。其中称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部。
注意事项和细节:
1)、迪米特法则的核心是降低类之间的耦合
2)、但是注意:由于每个类都减少了不必要的依赖,因此迪米特法则只是要求降低类间(对象间)耦合关系,并不是要求完全没有依赖关系
合成复用原则:
尽量使用合成/聚合的方式,而不是使用继承
设计原则核心思想:
1)、找出应用中可能需要变化之处,把他们独立出来,不要和那些不需要变化的代码混在一起。
2)、针对接口编程,而不是针对实现编程。
3)、为了交互对象之间的松耦合设计而努力。
UML类图:
1)、UML———Unified modeling language UML(统一建模语言),是一种用于软件系统分析和设计的语言工具,它用于帮助软件开发人员进行思考和记录思路的结果
2)、UML本身是一套符号的规定,就像数学符号和化学符号一样,这些符号用于描述软件模型中的各个元素和他们之间的关系,比如类,接口,实现,繁华,依赖,组合,聚合等。
3)、使用UML来建模,常用的工具有RationalRose,也可以使用一些插件来建模。
设计模式:
掌握设计模式的层次:
1)、第一层:刚开始学编程不久,听说过什么是设计模式
2)、第二层:有很长时间的编程经验,自己写了很多代码,其中用到了设计模式,但是自己却不知道。
3)、第三层:学习过了设计模式,发现自己已经在使用了,并且发现了一些新的模式挺好用的。
4)、第四层:阅读了很多别人洗的源码和框架,在其中看到别人设计模式,并且能够领会设计模式的精妙和带来的好处
5)、代码写着写着,自己都没意识到使用了设计模式,并且熟练的写了出来
设计模式类型:
1)、创建模式:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式。
2)、结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式
3)、行为型模式:模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式
单例模式:
所谓单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
1)、饿汉式(静态常量)
步骤:1.构造器私有化(防止new) 2.类的内部创建对象 3.向外暴露一个静态的公共方法。
getInstance 4.代码实现
- 优点:这种写法比较简单,就是在类加载的时候就完成了实例话。避免了线程同步问题。
- 缺点:在类装载的时候就完成实例话,没有达到Lazy Loading的效果,如果从开始至终末从未使用过这个实例,则会造成内存的浪费。
- 这种方式基于classoder机制避免了多线程的同步问题,不过,instannce在类装载时就实例化,在单例模式中大多数都是调用getInstance方法,但是导致类装载的原因有很多种,因此不能确定有其他方式(或者其他的静态方法)导致类装载,这时候初始化Instance就没有达到Lazy loading的效果
- 结论:这种单例模式可用,可能造成内存浪费
2)、饿汉式(静态代码块)
- 优缺点说明:这种方式和上面的方式其实类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执行静态代码中的代码,初始化类的实例。优缺点和上面是一样的。
- 结论:这种单例模式可用,但是可能造成内存浪费。
3)、懒汉式(线程不安全)
- 优缺点说明:1.起到了Lazy Loading的效果,但是只能在单线程下使用。2.如果在多线程下,一个线程进入了if(singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式。
- 结论:在实际开发中,不要使用这种方式
4)、懒汉式(线程安全,同步方法)
- 优缺点说明:1.解决了线程不安全问题。2.效率太低了,每个线程在想获取的类的实例的时候,执行getInstance()方法都要进行同步,而其实这个方法只是执行一次实例代码就够了,后面的想获得该实例,直接return就行了。方法进行同步效率太低。
- 结论:在实际开发中,不推荐使用这种方法。
5)、懒汉式(线程安全,同步代码块)
- 优缺点说明:这种方式本意是想对第四种实现方式的改进,因为前面同步方法效率太低,改为同步产生实例化的代码块。但是这种同步不能起到线程同步的作用。跟第3中实现方式遇到的情形一直,加入一个线程进入了if(singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了判断语句,这时便会产生多个实例。
- 结论:在实际开发中,不能使用这种方式。
6)、双重检查
- 优缺点说明:Double-Check概念是多线程开发中常使用的,如代码中所示,我们进行了两次if(singleton == null)检查,这样就可以保证线程安全了。这样,实例化代码只执行了一次,后面再次访问时,判断if(singleton == null),直接return实例化对象,也避免了反复的进行方法同步。线程安全:延迟加载,效率较高。
- 结论:实际开发中,推荐使用这种单例模式。
7)、静态内部类
- 优缺点说明:
- 这种方式采用了类装载的机制来保证初始化实例时只有一个线程。静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装在SingletonInstance类,从而完成Singleton的实例化。类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程时无法进入的。优点:避免了线程不安全,利用了静态内部类特点实现延迟加载,效率高。
- 结论:推荐使用。
8)、枚举
- 优缺点说明:借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化从新创建的对象。这张方式时Effective java作者Josh Bloch提倡的方式
- 结论:推荐使用
单例模式在JDK应用的源码分析
1)、我们JDK中,java.lang.Runtime就是经典的单例模式(饿汉式)
2)、代码分析 + Debug源码 + 代码说明
单例模式注意事项和细节说明:
单例模式保证饿了系统内存中该类只存在一个对象,节省了系统资源,对以一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
当像实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new
单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或者耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或者文件的对象(比如数据源、session工厂等)
总结:
所谓类的单例模式,就是采取一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个去的其对象实例的方法(静态方法)
工厂模式:
看一个具体的需求:
一个披萨项目:要便于披萨种类的扩展,要便于维护
1)、披萨的种类很多(比如GreekPizz、CheesePizz等)
2)、披萨的制作有prepare,bake,cut,box
3)、完成披萨店订购功能。
传统方式的优缺点:
1)、优点是比较好理解,简单易操作
2)、缺点是违反了设计模式ocp原则,即“对扩展开放,对修改关闭”。即当我们给类增加新功能的时候,尽量不修改代码,或者尽可能少修改代码。
3)、比如我们这时要新增一个Pizza种类,我们还需要再添加else if修改
4)、改进的思路分析:修改代码可以接受,但是如果我们其他的地方也有创建Pizza的代码,就意味着,也需要修改Pizza代码,往往有多处。思路:把创建Pizza对象封装到一个类中,这样我们有新的Pizza种类时,只需要修改该类就可以,其他创建到Pizza对象的代码就不需要修改了。—-》简单工厂模式。
简单工厂模式:
1)、简单工厂模式是属于创建模型,是工厂模式的一种。简单工厂模式是邮一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式。
2)、简单工厂模式:定义了一个创建对象的类,由这个类来封装实例化对象的行为(代码)
3)、在软件开发中,当我们会用到大量的创建某种类、某类或者某批次对象时,就回使用到工厂模式。
工厂方法模式:
一个新需求:客户在点披萨时,可以点不同口味的披萨,比如北京的奶酪pizza、北京的胡椒pizza 或者时伦敦的奶酪pizza、伦敦的胡椒pizza。
思路1:使用简单工厂模式,创建不同的简单工厂类,比如BJPizzaSimpleFactory、LDPizzaSimpleFactory等等,从当前这个案例来说,也是可以的,但是考虑到项目的规模,以及软件的可维护性、可扩展性并不是很好。
思路2:使用工厂方法模式
工厂方法模式介绍:
将披萨项目的实例化功能抽象成抽象方法,在不同的口味点餐子类中具体实现。
工厂方法模式:定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类。
抽象方法模式:
定义了一个interface用于创建相关或有依赖关系的对象蔟,而无需指明具体的类。抽象工厂模式可以讲简单工厂模式和工厂方法模式进行整合,从设计层面看,抽象工厂模式就是最简单工厂模式的改进(或者称为进一步的抽象)。将工厂抽象成两层,AbsFactory(抽象工厂)和具体实现的工厂子类。程序员可以根据创建对象类型使用对象对应的工厂子类。这样讲单个的简单工厂类变成了工厂蔟,更利于代码的维护和扩展。
工厂模式在JDK-Calendar应用:
1)、JDK中的Calendar类中,就使用了简单工厂模式
工厂模式小结:
1)、工厂模式的意义:将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系的结偶。从而提高项目的扩展和维护性。
2)、三种工厂模式:简单工厂模式、工厂方法模式、抽象工厂模式
3)、设计模式的依赖抽象原则
创建实例对象时,不要直接new类,而是把这个new类的动作放在一个工厂的方法中,并返回。有的书上说,变量不要直接持有具体类的引用。
不要让类继承具体类,而是继承抽象类或者是实现接口
不要覆盖基类中已经实现的方法。
原型模式:
现在有一只羊tom,姓名为tom,年龄为:1,颜色为白色,请编写程序创建和tom羊属性完全相同的10只羊。
传统方式的优缺点:
1)、优点是比较好理解,简单易操作
2)、在创建新的对象时,总是需要重新获得原始对象的属性,如果创建的对象比较复杂时,效率较低
3)、总是需要重新初始化对象,而不是动态的获取对象运行时的状态,不够灵活
4)、改进思路分析
思路:java中Object类是所有类的跟类,Object类提供了一个clone方法,该方法可以将一个java对象复制一份,但是需要实现clone的java类必须要实现一个接口Cloneable,该接口表示该类能够复制并且具有复制的能力====》原型模式。
原型模式-基本介绍:
1)、原型模式(Prototype模式)是指:用圆形实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。
2)、原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节。
3)、工作原理是:通过讲一个原型对象传给那个需要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们字节来实现创建,即 对象 .clone()
4)、形象的理解:孙大圣拔出猴毛,变出其他孙大圣
原型模式在Spring框架中的使用:
Spring中原型bean的创建,就是原型模式的应用
浅拷贝的介绍:
1)、对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
2)、对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中被修改成员变量会影响到另一个对象的该成员变量值。
3)、前面我们的克隆羊就是浅拷贝
4)、浅拷贝是使用默认的clone()方法来实现的 sheep = (Sheep)super.clone();
深拷贝的介绍:
1)、复制对象的所有基本数据类型的成员变量值、
2)、为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,知道该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象进行拷贝。
3)、深拷贝实现方式1:重写clone方法来实现深拷贝
4)、深拷贝实现方式2: 通过对象序列化实现深拷贝
原型模式的注意事项和细节:
1)、创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高刷新率
2)、不用重新初始化对象,而是动态的获得对象运行时的状态。
3)、如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码
4)、缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了ocp原则。
建造者模式:
盖房项目需求:
1)、需要建房子:这一过程为打桩、砌墙、封顶
2)、房子有各种各样的,比如普通房,高楼,别墅,各种房子的过程虽然已有,但是要求不要相同的
3)、请编写程序完成需求。
传统方式解决盖房子需求问题分析:
1)、优点是比较好理解,简单易操作
2)、设计的程序结构,过于简单,没有设计缓存层对象,程序的扩展和维护不好,也就是说,这种设计方案,把产品(即:房子)和创作产品的过程(即:建房子流程)封装在一起,耦合性增强了。
3)解决方案:将产品和产品建造过程节藕====》建造者模式
建造者模式:
1)、建造者模式又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使整个抽象过程的不同实现方法可以构造出不同表现(属性)的对象
2)、建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建他们,用户不需要知道内部的具体构建细节
建造者模式的资格角色:
1)、Product(产品角色):一个具体的产品对象
2)、Builder(抽象建造者):创建一个Product对象的各个部件指定的接口
3)、ConcreteBuilder(具体建造者):实现接口,构建和装配各个部件
4)、Director(指挥者):构建一个使用Builder接口的对象。它主要适用于创建一个复杂对象。它主要有两个作用,一是:隔离了客户与对象的产生过程,二是:负责控制产品对象的生产过程。
建造者模式在JDK的应用:
1)、java.lang.StringBuilder中的建造者模式
2)、Appendable接口定义了多个append方法(抽象方法),即Appendable为抽象建造者,定义了抽象方法。
AbstractStringBuilder 实现了Appendable接口放啊,这里的AbstractStringBuilder 已经是建造者,只是不能实例化。StringBuilder即充当了指挥者角色,又充当了具体的建造者。建造方法的实现是由AbstractStringBuilder 完成,而StringBuilder只是继承了AbstractStringBuilder 。建造者模式的注意事项和细节:
1)、客户端(使用程序)不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
2)、每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便的替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。
3)、可以更加精细的控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
4)、增加新的具体建造者无需修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,复合“开闭原则”
抽象工厂模式VS建造者模式:
抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类纬度的产品组合,采用抽象工厂模式不需要关心构建过程,只关心什么产品由什么工厂生产即可。而建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。
适配器模式:
1)、适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器模式(Wrapper)
2)、适配器模式属于结构型模式
3)、主要分为三类:适配器模式、对象适配器模式、接口适配器模式
类适配器模式:
Adpter类,通过继承src类,实现dst类接口,完成src-》dst的适配
类适配器模式注意事项和细节:
1)、Java是单继承机制,所以适配器需要继承src类这一点算是一个缺点,因为这要求dst必须是接口,有一定局限性;
2)、src类的方法在Adappter中都会暴露出来,也增加了使用成本。
3)、由于其继承了src类,所以它可以根据需求重写src类的放啊,是的Adater的灵活性增强了。
对象适配器模式:
1)、基本思路和类的适配器模式相同,只是将Adapter类做修改,不是继承src类,二是持有src类的实例,以解决兼容性的问题。即:持有src类,实现dst类接口,完成src→dst的适配
2)、根据“合成复用原则”,在系统中尽量使用关联关系来代替继承关系。
3)、对象适配器模式是适配器模式常用的一种
对象适配器注意事项和细节:
1)、对象适配器和类适配器其实算是同一种思想,只不过实现方式不同。根据合成复用原则,使用组合代替继承,所以它解决了类适配器必须继承src的局限性问题,也不再要求dst必须是接口。
2)、使用成本更低,更灵活。
接口适配器模式:
1)、一些书籍称为:适配器模式(Default Adapter Pattern)或缺省适配器模式。
2)、当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择的覆盖父类的某些方法来实现需求
3)、适用于一个接口不想使用其所有的方法的情况。
适配器模式在SpringMVC框架中的应用:
1)、SpringMVC中的HandlerAdapter,就是用了适配器模式
2)、使用HandlerAdapter的原因分析:可以看到处理器的类型不同,有多重实现方式,那么调用方式就不是确定的,如果需要直接调用Controller方法,需要调用的时候就不断使用if else来进行判断时哪一种子类然后执行。那么如果后面要扩展Controller,就得修改原来的代码,这样违背了OCP原则。
适配器模式的注意事项和细节:
1)、三种命名方式,是根据src是以怎样的形式给到Adapter(在Adapter里的形式)来命名的。
2)、适配器:以类给到,在Adapter里,就是将src当作类,继承
对象适配器:以对象给到,在Adapter里,将src作为一个对象,持有
接口适配器:以接口给到,在Adapter里,将src作为一个接口,实现
3)、Adapter模式最大的作用还是将原本不兼容的接口融合在一起工作
4)、实际开发中,实现起来不拘泥于我们讲解的是那种经典模式。
侨接模式:
手机操作问题:
现在对不同手机类型、不同品牌实现操作编程(比如:开机、关机、上网、打电话等)
传统方案解决手机操作问题分析:
1)、扩展性问题(类爆炸),如果我们再增加手机的样式(旋转式),就需要增加各个品牌手机的类,同样如果我们增加一个手机品牌,也要在各个手机样式类下增加。
2)、违反了单一职责原则,当我们增加手机样式时,要同事恶女家所有品牌的手机,这样增加了代码的维护成本。
3)、解决方案—使用桥接模式
桥接模式-基本介绍:
1)、桥接模式(Bridge模式)是指:将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变。
2)、是一种结构型设计模式
3)、Bridge模式给予类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责。它的主要特点是把抽象(Abstraction)与行为实现(Implementation)分离开来,从而可以保持各个部分的独立性以及应对他们的功能扩展
桥接模式解决手机操作问题:
桥接模式的注意事项和细节:
1)、实现了抽象和现实部分的分离,从而极大的提供了系统的灵活性,让抽象部分和现实部分独立开来,这有助于系统进行分层设计,从而产生更好的结构化系统。
2)、对于系统的高层部分,只需要知道抽象部分和现实部分的接口就可以了,其他的部分由具体业务来完成
3)、桥接模式替代多层集成方案,可以减少子类的个数,降低系统的管理和成分维护
4)、交接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计和编程。
5)、桥接模式要求正确识别系统中两个独立变化的维度,因此其适用范围由一定的局限性,即需要有这样的应用场景。
桥接模式其它应用场景:
1)、对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用
2)、常见的场景:JDBC驱动程序、
- 银行转账系统:
- 转账分类:网上转账、柜台转账、ATM转账
- 转账用户类型:普通用户、银卡用户、金卡用户
- 消息管理
- 消息类型:即时消息、延时消息
- 消息分类:手机短信、邮件消息、QQ消息
装饰者模式:
项目需求:
星巴克咖啡订单项目(咖啡馆):
1)、咖啡种类/单品咖啡:Espresso(意大利浓缩咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)
2)、调料:Milk、Soy(豆浆)、Chocolate
3)、要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便
4)、使用OO的来计算不同种类咖啡的费用:客户可以点单品咖啡,也可以单品咖啡+调料组合
解决星巴克咖啡订单问题分析:
1)、Drink是一个抽象类,表示饮料
2)、des就是对咖啡的描述,比如咖啡的名字
3)、cost()方法就是计算费用,Drink类中做成一个抽象方法
4)、Decaf就是单品咖啡、继承Drink,并实现cost
5)、Espress && Milk 就是单品咖啡 + 调料,这个组合很多
6)、问题:这样设计,会有很多类,当我们增加一个单品咖啡,或者一个新的调料时,类的数量就回倍增。就会出现类爆炸。
方案2-解决星巴克咖啡订单:
前面分析道方案1因为咖啡单品 + 调料组合会造成类的倍增,因此可以做成改进,将调料哪知道Drink类,这样就不会造成数量过多。从而提高项目的维护性
方案2可以控制类的数量,不至于造成很多的类,在增加或者删除调料种类时,代码的维护量还是很大。考虑到用户可以添加多分调料时,可以将hasMilk返回一个对应int。考虑使用装饰者模式。
装饰者模式定义:
1)、装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)
2)、这里提到了动态的将新功能附加到对象和ocp原则,在后面的应用实例上会轶代码的形式体现,请同学们注意体会。
- 作者:LiuJixue
- 链接:https://liujixue.cn/article/design_pattern
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。





