设计模式
2021-02-25 16:27:02 3 举报
AI智能生成
常见的设计模式
作者其他创作
大纲/内容
结构型
这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。
区别
注重意图的区别:https://blog.csdn.net/jiangxiayouyu/article/details/112427294
装饰者模式(Decorator Pattern)
定义
不改变其结构的情况下,允许动态地向一个现有的对象添加新的功能。
不改变原有的方法签名(实现同一个接口)的情况下, 对原对象的功能进行增强。
允许多层装饰
优点
动态扩展,即插即用。
装饰层次多种组合,可以有不同的使用场景。
缺点
从 接口或父类衍生出的子类比较多。
多层装饰情况下,导致程序复杂。
使用场景
Java IO API
类图
抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。
抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
具体装饰(ADecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
分支主题
代理模式(Proxy Pattern)
由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
优点
代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
代理对象可以扩展目标对象的功能;
代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性
缺点
代理模式会造成系统设计中类的数量增加(动态代理)
在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
增加了系统的复杂度;
使用 场景
注重意图:控制访问,即保护目标对象。
增强目标对象的功能,但是是非业务性或主干功能。一般这块性质的功能可以复用。
类图
图
静态代理
静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。
需要注意的是,代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法。
缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.
动态代理
JDK
1.代理对象,不需要实现接口
2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
3.动态代理也叫做:JDK代理,接口代理
注意:对于从 Object 中继承的方法,JDK Proxy 会把 hashCode()、equals()、toString() 这三个非接口方法转发给 InvocationHandler,其余的 Object 方法则不会转发,详见 JDK Proxy 官方文档。
Cglib
使用以目标对象子类的方式类实现代理。在内存中构建一个子类对象从而实现对目标对象功能的扩展.
JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的类,就可以使用Cglib实现.
CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。
代理的类不能为final,否则报错
目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.
区别
JDk动态代理:被代理对象必须实现一个接口
Cglib代理:不必实现接口,通过创建被代理对象的子类实现,因此要求无法代理final类和父类的final方法。
cglib创建的代理对象性能比jdk创建的高,但是cglib创建对象所花费的时间却很多。因此cglib适用于不需要频繁创建对象的场景。
在对JDK动态代理与CGlib动态代理的代码实验中看,1W次执行下,JDK7及8的动态代理性能比CGlib要好20%左右。
适配器模式(Adapter Pattern)
将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
优点
客户端通过适配器可以透明地调用目标接口。
复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类。
将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题。
在很多业务场景中符合开闭原则。
缺点
适配的复杂度,取决于实现接口。
类结构型适配器的耦合程度更高,要求对组件有一定了解,需要用到多重继承。
使用场景
以前开发的系统存在满足新系统功能需求的类,但其接口同新系统的接口不一致。
使用第三方提供的组件,但组件接口定义和自己要求的接口定义不同。
类图
分支主题
外观、门面模式(Facade Pattern)
定义
一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。
该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。
优点
降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类。
对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。
降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象。
缺点
不能很好地限制客户使用子系统类,很容易带来未知风险。
增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”
使用场景
对分层结构系统构建时,使用外观模式定义子系统中每层的入口点可以简化子系统之间的依赖关系。
当一个复杂系统的子系统很多时,外观模式可以为系统设计一个简单的接口供外界访问。
当客户端与多个子系统之间存在很大的联系时,引入外观模式可将它们分离,从而提高子系统的独立性和可移植性。
类图
分支主题
桥接模式(Bridge Pattern)
定义
将抽象与实现分离,使它们可以独立变化。
用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
优点
抽象与实现分离,扩展能力强
符合开闭原则
符合合成复用原则
其实现细节对客户透明
使用场景:当一个类内部具备两种或多种变化维度时,使用桥接模式可以解耦这些变化的维度,使高层代码架构稳定。
当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时。
当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时。
当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性时。
分支主题
类图
组合模式(Composite Pattern)
一种将对象组合成树状的层次结构的模式,用来表示“整体-部分”的关系,使用户对单个对象和组合对象具有一致的访问性。
组合模式一般用来描述整体与部分的关系,它将对象组织到树形结构中。
会把树枝节点和叶子节点看作属于同一种数据类型(用统一接口定义),让它们具备一致行为。
优点
组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;
更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”;
缺点
设计较复杂,客户端需要花更多时间理清类之间的层次关系;
不容易限制容器中的构件;
不容易用继承的方法来增加构件的新功能;
使用场景
在需要表示一个对象整体与部分的层次结构的场合。
要求对用户隐藏组合对象与单个对象的不同,用户可以用统一的接口使用组合结构中的所有对象的场合。
简单使用的,客户端直接使用具体子类(Leaf和Composite),仅使用组合的层次作用。
类图
抽象构件(Component)角色:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。(总的抽象类或接口,定义一些通用的方法,比如新增、删除)
树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于继承或实现抽象构件。
树枝构件(Composite)角色 / 中间构件:是组合中的分支节点对象,它有子节点,用于继承和实现抽象构件。它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法。
平衡透明性和安全性:类型安全,屏蔽组合细节。
享元模式(Flyweight Pattern)
定义
运用共享技术来有效地支持大量细粒度对象的复用。
它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。
优点
对象可共享,减少重复创建,资源浪费
缺点
用不到的时候,可能依旧驻留在内存,即生命周期是长于外部对象的。
区别
与缓存:与一般软件架构中的缓存不同,享元是为了避免重复创建,而缓存是为了提高访问速度。
与池化:虽然两者都为了避免重复创建,但是使用时,池化的资源一般只能被单独使用,而享元的单元是可以同时被多个对象共享的。
与单例:数量不同,控制访问的粒度也就不同了。
使用场景
行为型
这些设计模式特别关注对象之间的通信。
模板方法模式(Template Pattern)
定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤
优点
它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
它在父类中提取了公共的部分代码,便于代码复用。
部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。
缺点
对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,间接地增加了系统实现的复杂度。
父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。
由于继承关系自身的缺点,如果父类添加新的抽象方法,则所有子类都要改一遍。
使用场景
算法的整体步骤很固定,但其中个别部分易变时,这时候可以使用模板方法模式,将容易变的部分抽象出来,供子类实现。
当多个子类存在公共的行为时,可以将其提取出来并集中到一个公共父类中以避免代码重复。首先,要识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
当需要控制子类的扩展时,模板方法只在特定点调用钩子操作,这样就只允许在这些点进行扩展。
中介者模式(Mediator Pattern)
命令模式(Command Pattern)
定义
将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。
两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。
优点
通过引入中间件(抽象接口)降低系统的耦合度。
扩展性良好,增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,且满足“开闭原则”。
可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
方便实现 Undo 和 Redo 操作。命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复。
可以在现有命令的基础上,增加额外功能。比如日志记录,结合装饰器模式会更加灵活。
缺点
产生大量的具体命令类
使用场景
当系统的某项操作具备命令语义,且命令实现不稳定(变化)时,可以通过命令模式解耦请求与实现。
使用抽象命令接口使请求方的代码架构稳定,封装接收方具体命令的实现细节。
接收方与抽象命令呈现弱耦合(内部方法无需一致),具备良好的扩展性。
类图
抽象命令类(Command)角色:声明执行命令的接口,拥有执行命令的抽象方法 execute()。
具体命令类(Concrete Command)角色:是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
实现者/接收者(Receiver)角色:执行命令功能的相关操作,是具体命令对象业务的真正实现者。
调用者/请求者(Invoker)角色:是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。
分支主题
策略模式(Strategy Pattern)
定义了一系列算法族,分别封装起来,让其之间可以相互替换;且让算法的变化独立于使用算法的客户,算法的变化不影响到客户。
优点
算法族可重用,减少重复代码;
避免冗长、难维护的条件语句;
运行时可替换客户的算法。
缺点
策略实现类可能造成类膨胀;
策略的选择,不如条件语句的一览无遗。
使用场景
策略模式的点在于作为行为模式,可以理解为抽象了一个非具体的物象:物体的行为或功能。
而这个行为是可以替换的,比如厨房的制作能力,实际可以制作蔬菜、肉类、海鲜等。
常用于替换 switch、if...else.. 这样的条件语句。
常与工厂模式一起使用,利用工厂模式管理策略,方便选择策略。
类图
抽象策略(Strategy)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。
具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现。
环境(Context)类:持有一个策略类的引用,最终给客户端调用。
分支主题
状态模式(State Pattern)
定义
对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
将状态相关的行为委托给状态,由状态类代表对象的状态。
可以由状态类或对象类控制。
状态类如果不依赖Context,可以被多个共享,或者单例控制。
优点
结构清晰,状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”。
将状态转换显示化,减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
状态类职责明确,有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。
缺点
状态模式的使用必然会增加系统的类与对象的个数。
状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。
状态模式对开闭原则的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源码,否则无法切换到新增状态,而且修改某个状态类的行为也需要修改对应类的源码。
使用场景
当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。
一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。
类图
责任链模式(Chain Of Responsibility Pattern)
为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
优点
降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
缺点
不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
使用场景
多个对象可以处理一个请求,但具体由哪个对象处理该请求在运行时自动确定。
可动态指定一组对象处理请求,或添加新的处理者。
需要在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。
类图
分支主题
解释器模式(Interpreter Pattern)
观察者模式(Observer Pattern)
定义
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
优点
弱化目标对象和观察者对象之间的依赖,易用但又低耦合。
降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则。
缺点
目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。
使用
在 Java 中,通过 java.util.Observable 类和 java.util.Observer 接口定义了观察者模式,只要实现它们的子类就可以编写观察者模式实例。
Observable类
void addObserver(Observer o)
void notifyObservers(Object arg) 方法:推的模式。调用向量中的所有观察者对象的 update() 方法,通知它们数据发生改变,得到通知时,已经拿到变化的数据。通常越晚加入向量的观察者越先得到通知。
void notifyObservers() 方法:拉的模式,通知的模式同上。但是没有实际拿到变化数据,需要自己从被观察者拿。
void setChange() 方法:用来设置一个 boolean 类型的内部标志位,注明目标对象发生了变化。当它为真时,notifyObservers() 才会通知观察者。
Observer 接口是抽象观察者,它监视目标对象的变化,当目标对象发生变化时,观察者得到通知,并调用 void update(Observable o,Object arg) 方法,进行相应的工作。
arg 一般代表着实际需要的变化数据。
类图
抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
分支主题
备忘录模式(Memento Pattern)
迭代器模式(Iterator Pattern)
提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
优点
访问一个聚合对象的内容而无须暴露它的内部表示。
遍历任务交由迭代器完成,这简化了聚合类。
它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历。
增加新的聚合类和迭代器类都很方便,无须修改原有代码。
封装性良好,为遍历不同的聚合结构提供一个统一的接口。
缺点
增加了类的个数,这在一定程度上增加了系统的复杂性。
使用场景(Java 集合类的迭代器)
当需要为聚合对象提供多种遍历方式时。
当需要为遍历不同的聚合结构提供一个统一的接口时。
当访问一个聚合对象的内容而无须暴露其内部细节的表示时。
类图
访问者模式(Visitor Pattern)
模式之间的关系
设计原则
多用组合,少用继承
封装变化
针对接口编程,而不是针对实现
交互对象之间的松耦合
开闭原则
依赖抽象,不依赖具体
变量不持有具体类的引用。可以通过工厂来屏蔽掉具体类的实例化。
不让类派生自具体类
不覆盖基类中已实现的方法
最少知识原则:只和必须的类联系。
依赖倒置
类只有一个改变的理由
创建型
这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。
建造者模式(Builder Pattern)
定义
将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示。
将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。
它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的。
优点
封装性好,构建和表示分离。
扩展性好,各个具体的建造者相互独立,有利于系统的解耦。
客户端不必知道产品内部组成的细节,建造者可以对创建过程逐步细化,而不对其它模块产生任何影响,便于控制细节风险。
缺点
产品的组成部分必须相同,这限制了其使用范围。
如果产品的内部变化复杂,如果产品内部发生变化,则建造者也要同步修改,后期维护成本较大。
使用
Lombok
相同的方法,不同的执行顺序,产生不同的结果。
多个部件或零件,都可以装配到一个对象中,但是产生的结果又不相同。
产品类非常复杂,或者产品类中不同的调用顺序产生不同的作用。
初始化一个对象特别复杂,参数多,而且很多参数都具有默认值。
与工厂的区别
建造者模式更加注重方法的调用顺序,工厂模式注重创建对象。
创建对象的力度不同,建造者模式创建复杂的对象,由各种复杂的部件组成,工厂模式创建出来的对象都一样
关注重点不一样,工厂模式只需要把对象创建出来就可以了,而建造者模式不仅要创建出对象,还要知道对象由哪些部件组成。
建造者模式根据建造过程中的顺序不一样,最终对象部件组成也不一样。
工厂模式(Factory Pattern)
定义
定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。
优点
屏蔽对象实例化的细节。
方便作实例化的扩展,比如缓存实例、缓存中间对象等。
易扩展新的产品
缺点
新增产品,就需要新的产品类和工厂类,成对出现,大大增加了类的数量。
使用场景
复杂对象的实例化;
对象创建的多种情况。比如都是 new 类名(),重载很难体现不同构造方法之间的区别。通过工厂方法,可以明确不同重载下的适用场景。
简单工厂(静态工厂方法)
不是一个真正的模式,只是工厂模式的一种简单使用。
只负责生产一组确定的产品。
一个工厂,一个产品抽象类。
分支主题
工厂模式
是简单工厂的进一步抽象,方便在不改变原有代码的情况下,引入新的产品及对应工厂。该模式下,工厂和产品往往成对出现。
多个工厂,一个产品抽象类。
分支主题
抽象工厂模式(Abstract Factory Pattern)
定义
将考虑多等级产品的生产,将同一个具体工厂所生产的位于不同等级的一组产品称为一个产品族。
多个工厂,多个产品抽象类
优点
可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。
抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。
缺点
当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。
使用场景
系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
系统一次只可能消费其中某一族产品,即同族的产品一起使用。
分支主题
分支主题
单例模式(Singleton Pattern)
定义
一个类只有一个实例
单例只能自己创建、自己管理。
对外提供该实例的唯一全局访问点。
优点
只有一个实例,减少内存损耗;
避免多重访问,尤其是复杂的实例化过程。
控制对实例的访问,便于内部优化。
缺点
扩展困难
违背单一职责,只有类的功能,又负责管理实例。
使用场景
唯一对象;
实例化、实例销毁过程复杂,非常损耗性能
实例访问控制严格,入口收束。
实现
类图
饿汉式
懒汉式
双重检测
实现
问题:由于指令重排,创建单实例的底层三步骤会被重排列。造成的结果是即使线程1的在创建实例的过程中,线程2显示实例已创建,并使用。从而可能造成多程序出错。
解决方案:可以通过对单例变量添加volatile关键字,防止指令重排。
静态内部类
利用classloader的加载机制来实现懒加载,并保证构建单例的线程安全。
问题:无法防止通过反射或反序列化重复创建对象。
通过反射获取构造器对象,将构造器设为可见,然后通过构造器重复创建实例即可。
第一种:使用枚举,将类改为枚举类型
第二种:实现Serializable接口,定义readResolve方法
枚举实现
枚举的序列化机制,序列化时只输出name,反序列化时使用valueOf方法重新获得枚举对象,不会重新创建一个对象。
反射机制自身保证了不会去创建枚举对象。反射机制中的Construct.newInstance方法保证了当创建对象是枚举类型是,会抛出异常.
类加载器不同的问题
重写单例类的getClass方法,指定该类的类加载器。
CAS 实现
原型模式(Prototype Pattern)
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。
优点
Java 自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。
可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
缺点
需要为每一个类都配置一个 clone 方法
clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。
当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。
分类
浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
使用场景
1、资源优化场景。
2、类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
3、性能和安全要求的场景。
4、通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
5、一个对象多个修改者的场景。
6、一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
7、在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。
0 条评论
下一页