JAVA_23种设计模式
一、策略模式:The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
策略可以是具体的算法、某一个锦囊妙计。先定义一个接口,然后不同的策略都要实现这个接口,每一个策略对应到具体的实现了该接口的类。比如:接口中定义operate()方法,每一个具体的策略(实现了该接口的类)可以在operate方法中实现不同的算法,这有点像 统一格式的封装。
接着我们定义一个统一调度的锦囊将妙计包裹起来。锦囊(类)将之前声明的接口的引用作为私有的成员变量,并对外提供可以调用的方法对妙计进行赋值和调度。
—抽象策略角色: 策略类,通常由一个接口或者抽象类实现。
—具体策略角色:包装了相关的算法和行为。
—环境角色:持有一个策略类的引用,最终给客户端调用。
应用场景:
1、 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。
2、 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现。
3、 对客户隐藏具体策略(算法)的实现细节,彼此完全独立。
优点:
对策略角色进行了抽象,将公共部分进行提取。也就是抽象 类 或者 接口的意义。
锦囊对于策略的封装应用了组合。组合由于继承。试想一下,如果这里没有使用组合,而是采取继承的方式,那么锦囊(类)势必会有不同的子类具体去调用各个策略,使用者(锦囊)的子类与具体的策略一一对应,那么意义何在呢?好像完全没有道理啊!组合的优势在于锦囊没有变,妙计在变。锦囊也被抽象出来了。而且妙计放入锦囊的过程是动态加载的。虽然现实中3个妙计应该对应三个锦囊,但锦囊完全可以一样啊!没有必要一个锦囊对应一个妙计,每个妙计不同,每个锦囊也不同,最后选择的时候通过判别不同的锦囊进而判别对应的妙计,你也许会说这个更符合实际情况:比如先打开绿色的锦囊,危急关头打开红色的锦囊等等。但实际情况是锦囊妙计的实现,也就是我们程序员自己,即写锦囊的人和选择具体妙计给使用锦囊的人其实是一个人(程序员),我们熟知每一个妙计,负责为不同情景的客户提供帮助。而锦囊的作用则为我们程序员增添了程序员。实际上,锦囊为妙计蒙上了一层面纱,客户并不关心妙计,而在乎结果,你能否解决问题。
具体过程就好比:我看到一个客户的请求,分析情境后对应到一个idea,放到锦囊里,让妙计运行,问题解决了。客户看到结果,很高兴。
那么回归到最原始的方式,我们不使用组合,连继承都不使用。那么如何实现上述步骤呢?这就要求我们将整个的对于妙计的确立过程封装到锦囊里,锦囊里有所有的妙计,我们判别应该用哪一个妙计后,告诉锦囊应该用这个妙计,锦囊去运作妙计。这个过程的实现难免在锦囊中要实现各种逻辑判别,if,else,if,else……所以我们把判别的过程也抽象出来,锦囊就是锦囊,妙计就是妙计。程序员是调度者,客户就是问题制造者。
还有一点,妙计的增加很方便,我们按照其他妙计的格式(实现具体接口或者继承抽象类)写一个,用的时候放到妙计里就ok啦!
缺点:
程序员要对策略熟知。
你有没有想过,可能客户成千上万,问题就那么几个?所以妙计是可以共享的。我们可以 应用 享元设计 模式,让妙计共享。
科普:
组合和继承都能实现对类的扩展。
但在能使用组合的情况下尽量不使用继承。
为什么呢?
组合: 继承:
has-a is-a (代价大,要求高。要么全要,要么不要。)
运行期 决定 编译期 决定
不破坏封装(整体和局部松耦合) 破坏封装(子类依赖父类)
支持扩展(随用增加组合类) 只能继承一个父类
(必须包含父类所有方法,增加系统复杂性)
动态选择组合类方法 复用父类方法
那么什么时候使用继承呢?
使用多态。
复用 父类 方法,且父类很少改动。
处于聚合关系的两个类生命周期不同步,处于组合关系的两个类的生命周期同步;
小诗一首:
抽象妙计格式统一,
组合妙计锦囊神秘。
调度妙计动态人为,
共享妙计皆大欢喜!
二、代理模式:
即Proxy Pattern,23种常用的面向对象软件的设计模式之一。(设计模式的说法源自《设计模式》一书,原名《Design Patterns: Elements of Reusable Object-Oriented Software》。1995年出版,出版社:Addison Wesly Longman.Inc。该书提出了23种基本设计模式,第一次将设计模式提升到理论高度,并将之规范化。)
代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
代理角色与抽象角色都实现了相同的接口或抽象类,这保证了代理角色完全囊括了真实角色的所有接口,同时可以扩展属于自己的方法。通过组合的方式,代理角色对真实角色起到了很好的隔离和保护作用,是客户端与真实角色的中间人。就如同西门庆与王婆的关系。当然,西门庆可以与多个真实角色勾搭,这就是高扩展性。而相对于真实角色,它们无需关注具体的沟通问题,只需要打扮自己就ok了。
动态代理与静态代理:
我们试想一下,在整个代理的过程中,实际上我们不只有一个方案。我们可以让不同的王婆去代理不同的真实角色,供客户端享用。这就是为每一个不同的真实角色设置一个代理类。我们也可以让一个代理类代理多个真实的角色,即组合的方式,每一个类成员变量对应一个真实角色的引用。但上述方法都有一些缺点,前者是代理类过多,后者是代理类的内部实现过于臃肿繁琐。那么有没有更好的方法呢?目前为止我们所讨论的都是静态代理的范畴。它们都在编译期确立,有各自的字节码文件。接下来,我们要讨论一种更优的解决方案,也就是动态代理。代理类的引用是真实角色抽象出的接口或者抽象类,采用多态的方式运行期间对应真实角色。
总体来说,客户端就好比西门庆提需求,王婆就好比代理类 负责沟通调度,当然,在沟通的过程中要知根知底,所以真实角色与代理类 实现了相同的接口。而潘金莲等真实角色就为客户端提供真实的服务。
感觉与策略模式相比,在代码层面,王婆与锦囊的区别是:王婆与真实角色 知根知底,而锦囊与妙计之间却互不相识。
小诗一首:
抽象真实的抽象,具体抽象的具体。
客户需求找代理,代理组合引美女。
代理美女同接口,美女只需管自己。
沟通代理无蜚语,客户美女皆欢喜。
三、单 例 模式
数学与逻辑学中,singleton定义为“有且仅有一个元素的集合”。
单 例 模式 最初的定义出现于《设计模式》(艾迪生维斯理, 1994):“保证一个类仅有一个实例,并提供一个访问它的全局访问点。”
Java中 单 例 模式 定义:“一个类有且仅有一个实例,并且自行实例化向整个系统提供。”
Java单例 模式例子:
public class Singleton{
private Singleton(){}
private static volatile Singleton instance = null;
public static Singleton getInstance(){
if(instance == null){
synchronized(this){
instance = new Singleton();}
}
}
}
譬如每台计算机可以有若干个打印机,但只能有一个Printer Spooler, 以避免两个打印作业同时输出到打印机中。
Static:保证全局唯一。
Volatile:防止 多 进程 多线程创建错误问题。
显然单例模式的要点有三个;一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。
从具体实现角度来说,就是以下三点:一是单例模式的类只提供私有的构造函数,二是类定义中含有一个该类的静态私有对象,三是该类提供了一个静态的公有的函数用于创建或获取它本身的静态私有对象。
优点
1、实例控制
单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。
2、灵活性
因为类控制了实例化过程,所以类可以灵活更改实例化过程。
虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。
使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。
private static SingletonClass instance=null;
public static synchronized SingletonClass getInstance(){
instance=new SingletonClass();
// java允许我们在一个类里面定义静态类。比如内部类(nested class)。
//在java中,我们不能用static修饰顶级类(top level class)。
private static final Singleton instance = new Singleton();
//这里提供了一个供外部访问本class的静态方法,可以直接访问
public static Singleton getInstance(){
private static volatile Singleton instance=null;
public static Singleton getInstance(){
synchronized(Singleton.class){
//这个模式将同步内容下放到if内部,提高了执行的效率,不必每次获取对象时都进行同步,只有第一次才同步,创建了以后就没必要了。
//这种模式中双重判断加同步的方式,比第一个例子中的效率大大提升,因为如果单层if判断,在服务器允许的情况下,
//假设有一百个线程,耗费的时间为100*(同步判断时间+if判断时间),而如果双重if判断,100的线程可以同时if判断,理论消耗的时间只有一个if判断的时间。
//所以如果面对高并发的情况,而且采用的是懒汉模式,最好的选择就是双重判断加同步的方式。
如何选择:如果单件模式实例在系统中经常会被用到,饿汉式是一个不错的选择。
(2)多例类必须自己创建、管理自己的实例,并向外界提供自己的实例,因此,他的构造函数也是private的,这点跟单例模式是相同的
(4)多例模式往往具有一个聚集属性,通过向这个聚集属性登记已经创建过的实例达到循环使用实例的目的
五、工厂方法模式 factory method pattern
human = (Human)Class.forName(c.getName()).newInstance();
同时采用延迟初始化技术,用HashMap将创建过的对象保存起来。
if(humans.containsKey(c.getSimpleName())){
human = humans.get(c.getSimpleName());
human = (Human)Class.forName(c.getName()).newInstance(); // 放到 MAP 中
humans.put(c.getSimpleName(), human); }
简化锻造过程。在类初始化很消耗资源的情况比较实用,比如你要连接硬件 ,或者 是为了初始化一个类需要准备比较多条件(参数),通过这种方式可以很好的减少项目的复杂程度。
在编译期匹配方法称为静态分派(static dispatch)。
在运行期匹配方法称为动态分派(dynamic dispatch)。
宗量:接收者类型(调用方法的变量类型)和参数类型统称为宗量。
在分派过程中通过宗量的个数来决策调用的方法,java是动态单分派,静态多分派的语言(参考墨子骑马的故事)。多态仅考虑的是接收者的真实类型。我们也可以通过一些设计模式和方法间接实现java伪动态多分派。
六、抽象工厂模式(abstract factory pattern)
java推崇尽可能的将可以分割的代码片段化,不要写出过长的代码段。这是对高内聚低耦合的一种追求。
首先,我们考虑人的情况。将人所共有的行为抽象出来,哭啊,笑啊,闹啊!然后定义每一个人种的各自抽象类统统实现该接口。最后定义实体类继承其所属人种的抽象类。
门面模式,是指提供一个统一的接口去访问多个子系统的多个不同的接口,它为子系统中的一组接口提供一个统一的高层接口。使得子系统更容易使用。
地址信息,信件内容,投递,邮寄,对于客户而言,如果书写大量信件,会感觉比较麻烦。
现在邮局提供服务,只需要告诉邮局相关信息,就可以代替执行上述步骤,那么邮局在实现工程中,为了方便运作上述流程,则会将流程提取出来,封装到一个具体的类中,作为门面,直接与客户交互。
客户都是通过门面与原系统打交道的,可以在门面中记录相关行为。
在计算机编程中,适配器模式(有时候也称包装样式或者包装)将一个类的接口适配成用户所期待的。一个适配允许通常因为接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中。
-- 在这种适配器模式中,适配器容纳一个它包裹的类的实例。在这种情况下,适配器调用被包裹对象的物理实体。
-- 这种适配器模式下,适配器继承自已实现的类(一般多重继承)。
当客户在接口中定义了他期望的行为时,我们就可以应用适配器模式,提供一个实现该接口的类,并且扩展已有的类,通过创建子类来实现适配。
对象适配器”通过组合除了满足“用户期待接口”还降低了代码间的不良耦合。在工作中推荐使用“对象适配”。
案例:员工信息类设计不同,通过继承他方类通过super获取信心。通过实现我方接口将获取信息重新整合。接口是一种标准。
改变标准有两步,获取基础信息(通过类继承或者组合实例对象),重新整合(实现当下接口)
九、模板方法模式(template method pattern)
无处不在的Template Method:如果你只想掌握一种设计模式,那么它就是Template Method!
意图(Intent):定义一个操作中的算法骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。(——《设计模式》GoF)
或者,理解为重写父类的方法,再调用父类的方法,实现了改变父类方法。
编程的细节很重要!比如this、super等关键字尽量不省略。再如sequence等使用前调用clear方法等。注重细节,避免不必要的麻烦。高级语言就像文字一样,尽量清晰简明。
建造者模式给人的感觉有点像工厂模式,但它们的关注点不同。建造者模式关注的是基本方法的调用顺序。而工厂模式主要关注的是对象的创建。
建造者模式是设计模式的一种,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
如果系统中只需要一个具体的建造者的话,可以省略掉抽象建造者。
在具体建造者只有一个的情况下,如果抽象建造者角色已经被省略掉,那么还可以省略掉指导者角色,让Builder自己扮演指导者和建造者双重角色。
桥梁模式主要由抽象类、修正抽象类、实现类以及具体实现类组成 .
修正抽象类 , 扩展抽象类 , 修正或改变抽象类中指定的接口 .
实现类 , 提供实现化角色的接口 , 但不进行具体实现过程 , 该接口不一定给出与抽象类相同的接口 , 只是提供实现的方式 .
具体实现类 , 完成实现类中定义的实现接口的具体实现过程 .
修正抽象类(房地产公司)的引用应该是具体的抽象类产品的子类(房子)的类型。优点 : 隔离抽象和实现 , 使双方能够各自完成扩展 .
在面向对象程式设计的范畴中,命令模式(Command Pattern)是一种设计模式,它尝试以物件来代表实际行动。
命令接口实现对象,是“虚”的实现;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。
Client创建一个ConcreteCommand对象并指定他的Receiver对象
某个Invoker对象存储该ConcreteCommand对象
该Invoker通过调用Command对象的Execute操作来提交一个请求。若该命令是可撤销的,ConcreteCommand就在执行Execute操作之前存储当前状态以用于取消该命令
ConcreteCommand对象对调用它的Receiver的一些操作以执行该请求
命令模式的关键在于引入了抽象命令接口,且发送者针对抽象命令接口编程,只有实现了抽象命令接口的具体命令才能与接收者相关联。
模式优点
模式缺点
使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,因此某些系统可能需要大量具体命令类,这将影响命令模式的使用。
案例:客户项目协商
客户分别与需求组、美工组和代码组交流变更问题比较麻烦,所以直接与一个接头人交流,之后再由该接头人与各个组内成员进行协商。Command抽象类或者接口:组合需求组、美工组和代码组。然后concrete Command具体实现对应某一个变更。Invoker类组合command,负责直接与客户交流,获取命令并执行。可能存在的问题是concrete Command类可能会过多。
十三、 装饰模式【Decorator Pattern】
继承是一种强依赖关系,往往导致牵一发而动全身:子类的修改受掣于父类,而父类的修改又往往牵动着子类。如果真的是不会再变,那么再考虑使用继承吧!
那么,如果想在一个基础类上增添东西,推荐使用装饰。组合基础类,再添加修改。
案例:装饰成绩单
抽象基础类,具体基础类,抽象装饰器,不同的具体装饰器。其中抽象装饰器组合抽象基础类。
十四、迭代器模式【Iterator Pattern】
Iterator(迭代器)迭代器定义访问和遍历元素的接口
ConcreteIterator(具体迭代器)具体迭代器实现迭代器接口对该聚合遍历时跟踪当前位置
Aggregate(聚合)聚合定义创建相应迭代器对象的接口
ConcreteAggregate (具体聚合)
具体聚合实现创建相应迭代器的接口,该操作返回ConcreteIterator的一个适当的实例
它支持以不同的方式遍历一个聚合
迭代器简化了聚合的接口
在同一个聚合上可以有多个遍历
案例:项目预算整合
定义一个IProject接口,其中包含add、getInfo等方法。构造函数声明为private以便限定只能通过add方法添加项目信息。
IProject有返回IProjectIterator对象的方法,也就是关于自身的一个迭代器。IProjectIterator是一个实现了Iterator接口的实体类。我们可以不直接继承Iterator,而是间接通过一个自己定义的myIterator去继承Iterator,再通过间接继承myIterator去继承Iterator。这也是一种编程习惯。
十五、组合模式【Composite Pattern】
抽象构件角色(Component):定义参加组合的对象的共有方法和属性,可以定义一些默认的行为或属性;
比如我们例子中的 getInfo 就封装到了抽象类中。
叶子构件(Leaf):叶子对象,其下再也没有其他的分支。
树枝构件(Composite):树枝对象,它的作用是组合树枝节点和叶子节点;
案例:公司部门树形结构
透明模式和安全模式:
透明模式是把用来组合使用的方法放到抽象类中,比如add(),remove()以及getChildren等方法(顺便说一下,getChildren一般返回的结果为Iterable的实现类,很多,大家可以看 JDK 的帮助),不管叶子对象还是树枝对象都有相同的结构,通过判断是getChildren 的返回值确认是叶子节点还是树枝节点,如果处理不当,这个会在运行期出现问题的,不是很建议的方式;安全模式就不同了,它是把树枝节点和树叶节点彻底分开,树枝节点单独拥有用来组合的方法,这种方法比较安全。
组合模式解耦了客户程序与复杂元素内部结构,从而使客户程序可以像处理简单元素一样来处理复杂元素。
如果你想要创建层次结构,并可以在其中以相同的方式对待所有元素,那么组合模式就是最理想的选择。
组合模式,将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。掌握组合模式的重点是要理解清楚 “部分/整体” 还有 ”单个对象“ 与 "组合对象" 的含义。
十六、观察者模式【Observer Pattern】
案例:李斯监视韩非
被观察者组合观察者,当自己有一举一动时,notifyAll方式通知所有观察者。
观察者模式有一个变种叫做发布/订阅模型(Publish/Subscribe)。
使用观察者模式也有两个重点问题要解决:
广播链的问题。如果你做过数据库的触发器,你就应该知道有一个触发器链的问题,比如表 A 上写了一个触发器,内容是一个字段更新后更新表 B 的一条数据,而表 B 上也有个触发器,要更新表 C,表 C 也有触发器…,完蛋了,这个数据库基本上就毁掉了!我们的观察者模式也是一样的问题,一个观察者可以有双重身份,即使观察者,也是被观察者,这没什么问题呀,但是链一旦建立,这个逻辑就比较复杂,可维护性非常差,根据经验建议,在一个观察者模式中多出现一个对象既是观察者也是被观察者,也就是说消息多转发一次(传递两次),这还是比较好控制的;异步处理问题。这个 EJB 是一个非常好的例子,被观察者发生动作了,观察者要做出回应,如果观察者比较多,而且处理时间比较长怎么办?那就用异步呗,异步处理就要考虑线程安全和队列的问题,这个大家有时间看看 Message Queue,就会有更深的了解。
我们在来回顾一下我们写的程序,观察者增加了, 我就必须修改业务逻辑C l i e n t 程序,这个是必须得吗 ? 回顾一下我们以前讲到工厂方法模式的时候用到C l a s s U t i l s 这个类 ,其中有一个方法就是根据接口查找到所有的实现类,问题解决了吧!我可以查找到所有的观察者,然后全部加进来,以后要是新增加观察者也没有问题呀,程序那真是一点都不用改了!
十七、责任链模式【Chain of Responsibility Pattern】
责任链有一个缺点是大家在开发的时候要注意:调试不是很方便,特别是链条比较长,环节比较多的时候,由于采用了类似递归的方式,调试的时候逻辑可能比较复杂。
案例:古代妇女三重四德
受 众 数 量 不 同 。 观 察 者 广 播 链 式 可 以 1 : N 的 方 式 广 播 , 而 责 任 链 则 要 求 是 的 1 : 1 的 传 递 , 必 然 有 一 个且只有一个类完成请求的处理;请求内容不同。观察者广播链中的信息可以在传播中改变,但是责任链中的请求是不可改变的;处 理 逻 辑 不 通 。 观 察 者 广 播 链 主 要 用 于 触 发 联 动 动 作 , 而 责 任 链 则 是 对 一 个 类 型 的 请 求 按 照 既 定 的 规则进行处理。
十八、访问者模式【Visitor Pattern】
@Override public void accept(IVisitor visitor){ visitor.visit(this); }
public class Client {
public static void main(String[] args) { for(Employee emp:mockEmployee()){ emp.accept(new Visitor()); } }
抽象访问者(Visitor):抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是 visit方法的参数定义哪些对象是可以被访问的;
具体访问者(ConcreteVisitor):访问者访问到一个类后该怎么干(哎,这个别读歪了),要做什么事情;
抽象元素(Element):接口或者抽象类,声明接受那一类型的访问者访问,程序上是通过 accept 方法中的参数来定义;
具体元素:(ConcreteElement):实现 accept 方法,通常是 visitor.visit(this),基本上都形成了一个套路了;
结构对象(ObjectStruture):容纳多个不同类、不同接口的容器,比如 List、Set、Map 等,在项目中,一般很少抽象出来这个角色;
迭代器模式只能访问同类或同接口的数据,(当然了,你使用 instanceof 的话,能访问所有的数据,这个不争论),而访问者模式是对迭代器模式的扩充,可以遍历不同的对象,然后执行不同的操作,也就是针对访问的对象不同,执行不同的操作。访问者模式还有一个用途,就是充当拦截器(Interceptor)角色。
访问者关注了其他类的内部细节,这是迪米特法则所不建议的;还有一个缺点就是,具体角色的增加删除修改都是比较苦难的,就上面那个例子,你想想,你要是想增加一个成员变量,比如年龄 age,Visitor 就需要修改,如果 Visitor 是一个还好说,多个呢?业务逻辑再复杂点呢?访问者模式是有缺点的,是事物都有缺点,但是这仍然掩盖不了它的光芒,访问者模式结合其他模式比如模版方法模式、状态模式、解释器模式、代理模式等就会非常强大,这个我们放在模式混编中来讲解。
统计功能,多个访问者,拦截器。
访问者仿佛是一个移动的游标,遍历访问诸多为其提供接口的对象。
十九、状态模式【State Pattern】
在某种程度上,继承实现的多态可以替换switch语句。
案例:电梯状态转换
抽象类LiftState组合Context文本。每一个具体的状态如OpenningState等继承LifeState,并内部实现逻辑化的状态转换。Context中聚合所有电梯状态和当前状态LiftState。每一个电梯的具体状态的转换的实现都是通过引用super.context.去调用的。状态转换就是动态绑定的过程。
当一个对象内在状态改变时允许其改变行为,这个对象看起来像是改变了其类。
二十、原型模式【Prototype Pattern】
implements Cloneable
@Override public Mail clone(){ Mail mail =null; try { mail = (Mail)super.clone(); } catch (CloneNotSupportedException e) { // TODO Auto-generated catch block e.printStackTrace(); } return mail; }
案例:群发邮件广告
对象拷贝时,类的构造函数是不会被执行的。
对象拷贝时确实构造函数没有被执行,这个从原理来讲也是可以讲得通的,Object 类的 clone 方法的原理是从内存中(具体的说就是堆内存)以二进制流的方式进行拷贝,重新分配一个内存块,那构造函数没有被执行也是非常正常的了。
浅拷贝和深拷贝问题:
深拷贝:同时拷贝原始对象中的引用和数组成员。
浅拷贝:仅拷贝基本类型成员,包括string、int等。String要看作基本数据类型,不要当作引用哦!
Java做了一个偷懒的拷贝动作,Object类提供的方法clone只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址,这种拷贝就叫做浅拷贝,确实是非常浅,两个对象共享了一个私有变量,你改我改大家都能改,是一个种非常不安全的方式,在实际项目中使用还是比较少的。你可能会比较奇怪,为什么在 Mail 那个类中就可以使用使用 String 类型,而不会产生由浅拷贝带来的问题呢?内部的数组和引用对象才不拷贝,其他的原始类型比如int,long,String(Java 就希望你把 String 认为是基本类型,String 是没有 clone 方法的)等都会被拷贝的。
实现深拷贝要自己实现:
thing = (Thing)super.clone();
this.arrayList = (ArrayList<String>)this.arrayList.clone();
注意:Clone 与 final 两对冤家。对象的 clone 与对象内的 final 属性是由冲突的。
原型模式适合在什么场景使用?一是类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等;二是通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式;三是一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone的方法创建一个对象,然后由工厂方法提供给调用者。原型模式先产生出一个包含大量共有信息的类,然后可以拷贝出副本,修正细节信息,建立了一个完整的个性对象。
二十一、中介者模式【Mediator Pattern】
案例:进货、销售和库存
灵感:星型网络
AbstractMeditator是一个抽象类,其组合了所有的同事,Mediator是具体实现,其execute相当于mvc中的controller,具体的逻辑实现都在其中,可能会变得比较臃肿哦!
AbstractColleague是组合了AbstractMeditator,所有具体的同事都继承该类,有些行为是自身的,有些行为则必须依靠mediator去执行:super.mediator.一般情况中介者只有一个。
二十二、解释器模式【Interpreter Pattern】
Interpreter(解释器)模式是一种特殊的设计模式,它建立一个解释器(Interpreter),对于特定的计算机程序设计语言,用来解释预先定义的文法。简单地说,Interpreter模式是一种简单的语法解释器构架。
实例应用:正则表达式
由于我们使用具体的终止符和非终止符去解释文法,所以会比较易于编写。
可以比较方便的修改和扩展文法规则。相对于优点来说,它的缺点也非常明显,那就是由于我们几乎针对每一个规则都定义了一个类,所以如果一个文法的规则比较多,那对于文法的维护工作也会变得非常困难。递归都是在必要条件下使用的,它导致调试非常复杂。
给定一个语言, 定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
- AbstractExpression 抽象解释器
具体的解释任务由各个实现类完成,具体的解释器分别由TerminalExpression和NonterminalExpression完成。
- TerminalExpression终结符表达式
实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符。具体到我们例子就是VarExpression类,表达式中的每个终结符都在堆栈中产生了一个VarExpression对象。
- NonterminalExpression 非终结符表达式
文法中的每条规则对应于一个非终结表达式,具体到我们的例子就是加减法规则分别对应到AddExpression和SubExpression两个类。非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。
- Context 环境角色
具体到我们的例子中是采用HashMap代替。
抽象表达式通常只有一个方法,终结符表达式比如:加减乘除。非终结符表达式比如:常量变量。
二十三、亨元模式【Flyweight Pattern】
面向对象技术可以很好地解决一些灵活性或可扩展性问题,但在很多情况下需要在系统中增加类和对象的个数。当对象数量太多时,将导致运行代价过高,带来性能下降等问题。享元模式正是为解决这一类问题而诞生的。
在享元模式中可以共享的相同内容称为 内部状态(Intrinsic State),而那些需要外部环境来设置的不能共享的内容称为 外部状态(Extrinsic State),其中外部状态和内部状态是相互独立的,外部状态的变化不会引起内部状态的变化。由于区分了内部状态和外部状态,因此可以通过设置不同的外部状态使得相同的对象可以具有一些不同的特征,而相同的内部状态是可以共享的。也就是说,享元模式的本质是分离与共享 : 分离变与不变,并且共享不变。把一个对象的状态分成内部状态和外部状态,内部状态即是不变的,外部状态是变化的;然后通过共享不变的部分,达到减少对象数量并节约内存的目的。
在享元模式中通常会出现工厂模式,需要创建一个享元工厂来负责维护一个享元池(Flyweight Pool)(用于存储具有相同内部状态的享元对象)。在享元模式中,共享的是享元对象的内部状态,外部状态需要通过环境来设置。在实际使用中,能够共享的内部状态是有限的,因此享元对象一般都设计为较小的对象,它所包含的内部状态较少,这种对象也称为细粒度对象。
享元模式的目的就是使用共享技术来实现大量细粒度对象的复用。
享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于 享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种 对象结构型模式。
Flyweight: 享元接口,通过这个接口传入外部状态并作用于外部状态;
ConcreteFlyweight: 具体的享元实现对象,必须是可共享的,需要封装享元对象的内部状态;
UnsharedConcreteFlyweight: 非共享的享元实现对象,并不是所有的享元对象都可以共享,非共享的享元对象通常是享元对象的组合对象;
FlyweightFactory: 享元工厂,主要用来创建并管理共享的享元对象,并对外提供访问共享享元的接口;
public class FlyweightFactory
{
private HashMap flyweights = new HashMap();
public Flyweight getFlyweight(String key)
{
if(flyweights.containsKey(key))
{
return (Flyweight)flyweights.get(key);
}
else
{
Flyweight fw = new ConcreteFlyweight();
flyweights.put(key,fw);
return fw;
}
}
}
https://blog.csdn.net/justloveyou_/article/details/55045638
二十四、备忘录模式【Memento Pattern】
备忘录模式是一种软件设计模式:在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
Originator(发起人):负责创建一个备忘录Memento,用以记录当前时刻自身的内部状态,并可使用备忘录恢复内部状态。Originator可以根据需要决定Memento存储自己的哪些内部状态。
Memento(备忘录):负责存储Originator对象的内部状态,并可以防止Originator以外的其他对象访问备忘录。备忘录有两个接口:Caretaker只能看到备忘录的窄接口,他只能将备忘录传递给其他对象。Originator却可看到备忘录的宽接口,允许它访问返回到先前状态所需要的所有数据。
Caretaker(管理者):负责备忘录Memento,不能对Memento的内容进行访问或者操作。
发起人存储自己的状态,通过将自己的状态转移保存到备忘录中。同时,管理者以组合的方式将备忘录长久的保存起来。以窄接口的方式向外提供发起人保存的状态。而发起人则是宽接口的方式访问自己的备忘录。
二十五、
单一职责原则:一个类干一件事。
迪米特原则:只与直接朋友通信。
开放原则:扩展开放,修改关闭。
依赖倒置原则:具体依赖抽象,抽象不依赖具体。
里氏替换原则:子类可以替换父类出现。
接口隔离原则:接口专一化,细粒度化。
合成聚合复用原则:通常组合优于继承。
二十六、
代码示例:
1.Simple_Factory_Pattern:
https://blog.csdn.net/GZHarryAnonymous/article/details/82694886
2.Decorator_Pattern:
https://blog.csdn.net/GZHarryAnonymous/article/details/82711190
3.Strategy_Pattern:
https://blog.csdn.net/GZHarryAnonymous/article/details/82697416
4.Proxy_Pattern:
https://blog.csdn.net/GZHarryAnonymous/article/details/82730334
5.Factory_Pattern:
https://blog.csdn.net/GZHarryAnonymous/article/details/82763400
6.Prototype_Pattern:
https://blog.csdn.net/GZHarryAnonymous/article/details/82768714
7.Template_Method_Pattern:
https://blog.csdn.net/GZHarryAnonymous/article/details/82806781
8.Facade_Pattern:
https://blog.csdn.net/GZHarryAnonymous/article/details/82810695
9.Builder_Pattern:
https://blog.csdn.net/GZHarryAnonymous/article/details/82811678
10.Observer_Pattern:
https://blog.csdn.net/GZHarryAnonymous/article/details/82814286
11.Abstract_Factory_Pattern:
https://blog.csdn.net/GZHarryAnonymous/article/details/82827666
12.State_Pattern:
https://blog.csdn.net/GZHarryAnonymous/article/details/82829778
13.Adapter_Pattern:
https://blog.csdn.net/GZHarryAnonymous/article/details/82830055
14.Memento_Pattern:
https://blog.csdn.net/GZHarryAnonymous/article/details/82830632
15.Composite_Pattern:
https://blog.csdn.net/GZHarryAnonymous/article/details/82834285
16.Iterator_Pattern:
https://blog.csdn.net/GZHarryAnonymous/article/details/82834682
17.Singleton_Pattern:
https://blog.csdn.net/GZHarryAnonymous/article/details/82835349
18.Bridge_Pattern:
https://blog.csdn.net/GZHarryAnonymous/article/details/82843710
19.Command_Pattern:
https://blog.csdn.net/GZHarryAnonymous/article/details/82844099
20.Chain_Of_Responsibility_Pattern:
https://blog.csdn.net/GZHarryAnonymous/article/details/82844680
21.Mediator_Pattern:
https://blog.csdn.net/GZHarryAnonymous/article/details/82845154
22.Flyweight_Pattern:
https://blog.csdn.net/GZHarryAnonymous/article/details/82845681
23.Interpreter_Pattern:
https://blog.csdn.net/GZHarryAnonymous/article/details/82845970
24.Visitor_Pattern:
https://blog.csdn.net/GZHarryAnonymous/article/details/82847938
参考书籍:
https://download.csdn.net/download/gzharryanonymous/10687349
JAVA_23种设计模式相关推荐
- Python七大原则,24种设计模式
七大设计原则: 1.单一职责原则[SINGLE RESPONSIBILITY PRINCIPLE]:一个类负责一项职责. 2.里氏替换原则[LISKOV SUBSTITUTION PRINCIPLE ...
- JS中的7种设计模式
第九章Refactoring to OOP Patterns 重构为OOP模式 7种设计模式: 1,模版方法模式(template method) 2,策略模式(strategy) 3,状态模式(st ...
- java设计模式中不属于创建型模式_23种设计模式第二篇:java工厂模式定义:工厂模式是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式...
23种设计模式第二篇:java工厂模式 定义: 工厂模式是 Java 中最常用的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 工厂模式主要是为创建对象提供过渡接口, ...
- Python培训常识:Python面试中常被问到的几种设计模式要知道
学习Python技术大家都是为了日后能够找到适合自己的工作岗位,那么除了要学习好Python技术外,对于面试环节的问题也要有所了解,本期小编为大家介绍的Python培训教程就算关于Python面试中常 ...
- 9种设计模式在Spring中的运用,一定要非常熟练!
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:iCoding91 https://blog.csdn.ne ...
- Java开发中的23种设计模式详解(转)
设计模式(Design Patterns) --可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了 ...
- 封装成vla函数_不知道怎么封装代码?看看这几种设计模式吧!
为什么要封装代码? 我们经常听说:"写代码要有良好的封装,要高内聚,低耦合".那怎样才算良好的封装,我们为什么要封装呢?其实封装有这样几个好处: 封装好的代码,内部变量不会污染外部 ...
- java 工厂模式的写法_Java23种设计模式之抽象工厂模式
概述 抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂.该超级工厂又称为其他工厂的工厂.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. ...
- 23 种设计模式实战 pdf(很全)
今天分享一份学习资料:23 种设计模式实战教程.pdf,助你快速上手设计模式,写出各种高端代码,文末附下载地址. 设计模式一般分为三大类: 实战教程: 教程共 96 页PDF,太全了!纯粉丝福利,非广 ...
最新文章
- C++点操作符和箭头操作符
- maven上传本地仓库
- 数据库schema 是什么
- codeforces 7.22 E Permutation Shift
- 子集和问题 算法_子集问题 主要是去重算法
- KMP算法———模板
- Java 网络实例三(获取URL响应头的日期信息、获取URL响应头信息、解析URL、ServerSocket和Socket通信实例)
- volatile关键字与synchronization关键字的区别?
- Dxg——Raspberry Pi Pico python 开发笔记整理分类合集【所有的相关记录,都整理在此】
- 【csdn积分】获得方式大全
- AXure交互设计指南
- java 网络五子棋游戏_基于JAVA的网络五子棋游戏
- win10误删的注册表能还原吗_win10恢复系统注册表,win10删除注册表怎么还原
- PhotoshopCC 2018(19.1.3)绿色精简/增强无需注册安装直接用
- 基于Java+SpringBoot+Vue前后端分离商城系统设计与实现
- 关于版本号的基本介绍
- 实现php Curl 调用不同项目中方法
- bss与data的区别
- C# 10分钟入门基于WebOffice实现在线编辑文档,实时保存到服务器(所有office,兼容WPS)
- 《聆听宇宙的歌唱》——超越故乡