# 设计模式 **Repository Path**: Doream/DesignPattern ## Basic Information - **Project Name**: 设计模式 - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2022-01-27 - **Last Updated**: 2022-01-30 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 设计模式 ## 创建型模式 ==用于描述“怎样创建对象”,解耦合,创建与使用的分离== ### 单例模式 ### 原型模式 ==用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的对象== 结构: - 抽象原型类:例如规定原型对象必须实现clone()方法 - 具体原型类:实现clone()方法 - 客户端 JDK提供了抽象原型类Clonable来实现克隆,可以通过实现该接口的子类就是具体的原型类。 **Clonable对引用类型实现的是浅拷贝,且不会调用构造器** ### 工厂方法模式 结构: - 抽象工厂:提供创建产品的接口 - 具体工厂:实现抽象方法,完成具体产品的创建 - 抽象产品:定义产品规范 - 具体产品:具体的产品实现 优点: - 用户只需要具体的工厂就可以生产对应的产品 - 在系统增加新的产品时只需添加新的具体产品和对应工厂类,不需要修改原工厂代码 缺点: - 增加了系统复杂度 ### 抽象工厂模式 抽象工厂模式是工厂方法模式的升级版本,抽象工厂可以生产多级别的产品 结构: - 抽象工厂:提供创建产品的接口,包含多个创建产品的方法 - 具体工厂:实现抽象方法,完成具体产品的创建 - 抽象产品:定义产品规范 - 具体产品:具体的产品实现 优点: - 用户只需要具体的工厂就可以生产对应的产品族 缺点: - 产品族需要新增产品,所有的工厂都需要进行修改 使用场景: - 当需要创建的对象是一系列相互关联或相互依赖的产品族时 - 系统中有多个产品族,但每次只使用某一族的产品 - 所有的产品接口相同,客户端不依赖创建细节,面向接口编程,解耦 #### JDK源码工厂模式: ### 建造者模式 ==将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示== 分离了部件的构造(由Builder负责)和装配(由Director负责),从而构造出复杂的对象 结构: - 抽象建造者类(Builder):规定实现复杂对象的部分创建 - 具体建造者类(ConcreteBuilder):实现Builder接口 - 产品类(Product) - 指挥者类(Director):调用具体建造者类来创建复杂对象的各个部分,只负责保证对象各部分完整创建或按某种顺序创建 建造同一对象的不同属性,建造过程的抽象 使用场景: - 创建对象较复杂,由多个部件构成,其产品的各个部分经常面临着剧烈的变化,但将他们组合在一起的算法却相对稳定。 ```java public class House { private String basic; private String wall; private String roofed; public String getBasic() { return basic; } public void setBasic(String basic) { this.basic = basic; } public String getWall() { return wall; } public void setWall(String wall) { this.wall = wall; } public String getRoofed() { return roofed; } public void setRoofed(String roofed) { this.roofed = roofed; } @Override public String toString() { return "House{" + "basic='" + basic + '\'' + ", wall='" + wall + '\'' + ", roofed='" + roofed + '\'' + '}'; } } // =============== public abstract class HouseBuilder { protected House house = new House(); public abstract void buildBasic(); public abstract void buildWall(); public abstract void roofed(); public House buildHouse(){ return house; } } //======= public class CommonHouse extends HouseBuilder { @Override public void buildBasic() { System.out.println("普通房子打地基5米"); house.setBasic("5m地基"); } @Override public void buildWall() { System.out.println("普通房子砌墙"); house.setWall("水泥墙"); } @Override public void roofed() { System.out.println("普通房子封顶"); house.setRoofed("铁皮顶"); } } //=============== public class HouseDirector { HouseBuilder houseBuilder; public HouseDirector(HouseBuilder houseBuilder) { this.houseBuilder = houseBuilder; } public void setHouseBuilder(HouseBuilder houseBuilder) { this.houseBuilder = houseBuilder; } public House constructHouse(){ houseBuilder.buildBasic(); houseBuilder.buildWall(); houseBuilder.roofed(); return houseBuilder.buildHouse(); } } //======== public class Client { public static void main(String[] args) { HouseDirector houseDirector = new HouseDirector(new CommonHouse()); House normalHouse = houseDirector.constructHouse(); houseDirector.setHouseBuilder(new HighBuilding()); House highHouse = houseDirector.constructHouse(); System.out.println(normalHouse); System.out.println(highHouse); } } ``` ## 结构型模式 ==用于描述如何将类或对象按某种布局组成更大的结构,分为类结构型和对象结构型模式,前者采用继承机制来组织接口和类,后者采用组合或聚合来组合对象。对象结构型模式具有更大的灵活性。== ### 代理模式 结构: - 抽象主题类 - 真实主题类 - 代理类 静态代理:编译时 动态代理JDK:通过proxyFactory反射生成代理,通过实现相同接口实现 动态代理CGLB:底层采用ASM字节码生成框架,通过创建目标的子类完成代理 优点: - 代理模式起到中介和保护作用 - 可以扩展功能 - 客户端与目标对象分离,解耦 缺点: - 增加系统复杂度 使用场景: - 远程代理 - 防火墙代理 - 保护代理 ### 适配器模式 - 目标接口(Target) - 适配者类(Adaptee) - 适配器类(Adapter) ### 桥接模式 好处: - 提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统 ### 装饰模式 - 抽象构件角色:定义一个抽象接口以规范准备接收附加责任对象 - 具体构件角色:实现抽象构件,通过装饰角色为其添加一些职责 - 抽象装饰角色:继承或实现抽象构件,并包含具体构件实例,可以通过其子类扩展具体构件功能 - 具体装饰角色:实现抽象装饰的方法,给具体构件对象添加功能 好处: - 带来比继承更加灵活性的扩展功能,可以通过不同的组合获取具有不同行为多样化的结果。 - 有良好的扩展性,继承是静态的附加责任,装饰模式是动态的附加责任 - 装饰类和被装饰类可以独立发展,不会相互耦合 JDK中IO的包装类使用了装饰者模式 ### 外观模式 ==又名门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部细节,外观模式是迪米特法则的典型应用== 结构: - 外观角色:为多个子系统实现对外提供一个共同的接口‘ - 子系统角色:实现系统的部分功能,客户可以通过外观角色访问它 好处: - 降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响到调用它的客户类 - 对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易 缺点: - 不符合开闭原则,修改起来 Servlet中HttpRequest使用了外观模式 ### 享元模式 ==运用共享技术来有效地支持大量细粒度对象的复用,它通过共享已经存在的对象来大幅度减少需要创建的对象数量,避免大量相似对象的开销,从而提高系统资源的利用率。== 状态: - 内部状态:即不会随着环境的改变而改变的可共享部分 - 外部状态:随着环境的改变而改变的不可共享部分。 结构: - 抽象享元角色(Abstract Flyweight):通常是一个接口类,在抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法类设置外部数据(外部状态) - 具体享元角色(Concrete Flyweight):它实现了抽象享元类,称为享元对象;在具体享元类中为内部状态提供存储空间。通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。 - 非享元(Unsharable Flyweight):并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类;当需要一个非共享具体享元类对象时可以直接实例化创建。 - 享元工厂(Flyweight Factory):负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检查系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话则创建一个新的享元对象 优点: - 极大减少内存中相似或相同对象数量 ,节约系统资源,提供系统性能 - 享元模式中的外部状态相互独立,且不影响内部状态 缺点: - 为了使对象可以共享,需要将享元对象的部分状态外部化,分离内部状态和外部状态使程序逻辑复杂 ### 组合模式 ==又名部分整体模式,是用于把一组相似的对象当做一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。== 结构: - 抽象根节点(Component):定义系统各层次对象的共有方法和属性,可以预先定义一些默认行为和属性 - 树枝节点(Compostie):定义树枝节点的行为,存储子节点,组合树枝节点和叶子节点形成一个树形结构 - 叶子结点(Leaf):叶子节点对象,其下再无分支。是系统层次遍历的最小单位 优点: - 组合模式可以清除地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制。 - 客户端可以一致地使用一个组合结构或单个对象,不必关心处理的是单个对象还是整体结构,简化了客户端代码。 - 在组合模式中增加新的树枝节点和叶子节点都很方便,无需对现有类库进行修改,符合开闭原则。 - 组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子节点和树枝节点的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。 ## 行为型模式 ==用于类和对象之间相互协助完成单个对象无法完成的任务,它涉及算法与对象之间职责的分配。== ==行为模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合或聚合比继承关系耦合度低,满足合成复用原则,所以对象行为模式具有更大的灵活性。== ==其中模板方法模式和解释器模式属于类行为模式== ### 模板方法模式 定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重新定义该算法的某些特定步骤。。 结构: - 抽象类:负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。 - 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。 - 基本方法:是实现算法各个步骤的方法,是模板方法的组成部分。基本方法又可以分为三种: - 抽象方法:一个抽象方法由抽象类声明,由具体子类实现 - 具体方法:由抽象类或具体类实现,子类可重写或覆盖 - 钩子方法:在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重弄写的空方法两种。 一般钩子方法是用于判断的逻辑方法,这类方法名一般为isXxx,返回boolean。 - 具体子类:实现抽象类中定义的方法 优点: - 提高代码复用性 - 实现了反向控制,通过对子类的具体实现扩展不同的行为 缺点: - 对每个不同的实现需要定义一个新的子类 - 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,它提高了代码阅读的难度 适用场景: - 算法的整体步骤很固定,但其中个别部分易变时。 - 需要通过子类来决定父类算法中某个步骤是否执行。 InuputStream中定义了多个read()方法 ### 策略模式 ==该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响到客户。策略模式属于对象行为模式,它通过对算法进行封装,把算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行处理== 结构: - 抽象策略类:接口或抽象类,此角色给出所有的具体策略类所需的接口 - 具体策略类:实现了抽象策略定义的接口,提供具体的算法实现或行为 - 环境类:持有一个策略类的引用,最终给客户端调用 优点: - 策略之间可以自由切换 - 易于扩展 - 避免过多的if else 缺点: - 客户端必须知道所有的策略类 - 策略模式将造成产生很多策略类,可以通过享元模式在一定程度上减少对象数量 使用场景: - 一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中 - 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个条件分支移入它们各自的策略类中以代替这些条件语句 - 系统中各算法彼此完全独立,且要求对客户隐藏具体算法的实现 - 多个类只区别在表现行为不同,可以使用策略模式。 JDK中Arrays中sort()方法传入Comparator的子实现类对象使用了策略模式, 用第三方登录我觉得也可以使用策略模式 ### 命令模式 ==将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象存储、传递、调用、增加与管理== 结构: - 抽象命令类角色:定义命令的接口,声明执行的方法 - 具体命令角色:实现命令的接口;通常会持有接受者,并调用接受者的接口来完成命令要执行的操作 - 实现者/接受者:接收者,真正执行命令的对象,任何类都可能成为一个接收者,只要它能实现命令要求实现相应的功能 - 调用者/请求者:要求命令对象执行请求,通常会持有命令对象,是使用命令对象的入口。 优点: - 降低耦合度 - 增加或者删除命令非常方便 - 可以实现宏命令,命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令 - 方便实现Undo和Redo操作。可以与备忘录模式结合,实现Undo和Redo 缺点: - 可能会有过多的命令类 - 系统结构更加复杂 使用场景: - 调用者和接收者需要解耦 - 系统需要在不同的时间指定请求、请求排队和执行请求 - 需要支持Undo和Redo操作 Runnable担当命令的角色(抽象命令角色),Thread充当调用者 ### 职责链模式 ==又名责任链模式,为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,知道有对象处理为止== 结构: - 抽象处理者角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接 - 具体处理者角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则传给后继者 - 客户类角色:创建处理链,并向链头具体处理者对象提交请求 优点: - 降低了请求发送者和接收者的耦合度 - 可扩展性 - 增强了给对象指派职责的灵活性 - 简化了对象的连接 - 责任分担 缺点: - 不能保证每一个请求一定被处理 - 对比较长的职责链,请求的处理可能涉及多个处理对象,性能受到影响 - 可能由于职责链的错误设置导致程序出错(例如设置环状) 过滤器中FilterChain是责任链模式 ### 状态模式 ==对有状态的对象,把复杂的判断逻辑提取到对象中,允许状态对象在其内部状态发生改变时改变其行为== 结构: - 环境角色:也称为上下文,它定义了客户程序需要的接口,维护了一个当前状态,并将与状态相关的操作委派给当前对象来处理 - 抽象状态角色:定义了一个接口,用以封装环境对象中的特定状态所对应的行为 - 具体状态角色:实现接口 ### 观察者模式 ==又称发布-订阅模式,它定义了一种一对多的依赖关系,让多个观察者对象监听某一主题对象,这个主题对象在状态变化时,会通知所有的观察者对象,使它们能够自动更新自己== 结构: - Subject抽象主题:抽象主题角色把所有观察者对象统一保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象 - ConcreteSubject:具体主题,该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知 - Observer抽象观察者:定义接口 - ConcreteObserver:实现接口 JDK中提供Observable(抽象目标类)底层是vector和Observer(观察者接口), ### 中介者模式 ==又叫调停模式,定义一个中介角色来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立改变它们的交互== 优点: - 松散耦合 - 集中控制交互 - 一对多关联转变为一对一 缺点: - 当同事类太多时,中介者的职责将很大 ### 迭代器模式 ==提供一个对象用来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示== 结构: - 抽象聚合 - 具体聚合 - 抽象迭代器 - 具体迭代器 优点: - 支持以不同的方式遍历一个聚合对象,只需要用一个不同的迭代器来代替原有迭代器即可改变遍历算法 - 简化了聚合类(List) - 增加新的聚合类和迭代器都很方便 缺点: - 增加了类的个数 实际上JDK的单链集合集合都实现了迭代器 ### 访问者模式 ==封装了一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义这些元素的操作== 结构: - 抽象访问者 - 具体访问者 - 抽象元素 - 具体元素 - 对象结构角色 优点: - 扩展性好,不改变元素的情况下,为对象结构中的元素添加 新的功能 - 复用性好 - 分离无关行为 缺点: - 对象结构变化困难,每添加一个元素类,都要在每一个具体访问者类中添加响应的具体操作 - 违反了依赖倒转原则,访问者依赖具体类,没有依赖抽象类 ### 备忘录模式 ==又叫快照模式,在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态== 结构: - 发起人角色(Originator):记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里所有的信息 - 备忘录角色(Memento):负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人 - 管理者角色(Caretaker):对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改 - 窄接口:管理者和其他发起人对象只能看到备忘录的窄接口,这个窄接口只允许他把备忘录对象传给其他的对象 - 宽接口:发起人对象可以看到一个宽接口。这个宽接口允许它读取所有的数据 优点: - 提供了一种可以恢复状态的机制 - 实现了内部状态的封装 - 简化了发起人类 缺点: - 资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源 ### 解释器模式 结构: - 抽象表达式角色(Abstract Expression):定义解释器的接口,约定解释器的操作,主要包含解释方法interpret() - 终结符表达式角色(Terminal Expression):是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中每一个终结符都有一个具体终结表达式与之相对应 - 非终结符表达式角色(Nonterminal Expression):也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。 - 环境角色(Context):通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这列获取这些值 - 客户端(Client):主要任务时将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。 优点: - 易于改变和扩展文法 - 实现文法比较容易 - 增加新的解释表达式较为方便 缺点: - 对于复杂文法难以维护 - 执行效率低