1.概念: 软件设计模式(Software Design Patten),又称设计模式,是一套被反复使用,多数人只晓的,经过分类编目的,代码设计经验的总结.它描述了在软件设计过程中一些不断重复的问题,以及该问题的解决方案.也就是说,它是解决特定问题的一系列套路,是前辈们的代码设计经验的总结,具有一定的普遍性,可以反复使用.其目的是为了提高代码的复用性,代码可读性以及代码的可靠性.
2.优点: 1)可以提高程序员的思维能力、编程能力和设计能力。
2)使程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期。
3)使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强。
3.核心原则
1)找出应用可能需要变化之处,把他们独立出来,不要和那些不需要变化的代码混在一起
2)针对接口编程,而不是针对实现编程
3)为了交互对象之间的松耦合设计而努力
设计模式需要遵循7大原则:开闭原则,里氏替换原则,依赖倒置原则,单一职责原则,接口隔离原则,迪米特法则,合成复用原则
定义:职责是指类变化的原因,如果一个类承担了太多的职责,那么类应该被拆分.如果类承担太多职责,那么存在以下两个缺点
1)一个职责的变化可能会削弱或者抑制这个类实现其他职责的能力
2)当客户需要对象的某一个职责时,不得不将其他不需要的职责都包含进来,从而造成冗余代码或者代码的浪费
优点:单一职责原则的核心就是控制类的粒度大小、将对象解耦、提高其内聚性。如果遵循单一职责原则将有以下优点.
1)降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多
2)提高类的可读性,复杂性降低,自然其可读性会提高
3)提高系统可维护性,可读性提高,那就更容易维护
4)变更引起的风险降低,变更是必然的,如果单一职责遵守的号,当修改一个功能时,可以显著降低其对其他功能的影响
实现方法:单一职责原则是最简单但又最难运用的原则,需要设计人员发现类的不同职责并将其分离,再封装到不同的类或模块中。而发现类的多重职责需要设计人员具有较强的分析设计能力和相关重构经验。
定义:将臃肿庞大的接口拆分成更小和更具体的接口,让接口质保函客户感兴趣对的方法.以上两个定义的含义是:要为各个类建立他们需要的专用接口,而不呀视图建立一个很庞大的接口供所有依赖来它的类去调用.
优点:接口隔离原则是为了约束接口,降低类对接口的依赖性,遵守接口依赖的隔离
1)将臃肿的接口分解为多个粒度小的接口,可以预防外来的变更的扩散,提高系统的灵活性和可维护性
2)接口隔离提高了系统的内聚性,减少对外交互,降低了系统的耦合性
3)如果接口的粒度大小定义合理,就能保证系统的稳定性.但是,如果定义过小,就会造成接口数量过多,是设计复杂化;定义过大,灵活性降低,无法提供定制服务,给整体项目带来无法预料的风险
4)使用多个专门的接口还能够体现对象的层次,因此可以通过接口的继承,实现总借口的定义
5)能减少项目工程中的代码冗余.过大的接口里面通常防止许多不用的方法,当实现这个接口的时候,被迫设计冗余的代码.
实现方法:在具体应用接口的隔离原则,应该根据以下几个规则来衡量
1)接口尽量小,但是要有限度,一个接口只服务于一个子模块或者业务逻辑
2)为依赖接口的类定制服务.只提供调用者需要的方法,屏蔽不需要方法
3)了解环境,拒绝盲从.每个项目或者产品都有选定的环境因素,环境不同,接口拆分的标准就不同深入了解业务逻辑
4)提高内聚,减少对外交互,使接口用最少的方法完成最多的事情.
接口隔离原则和单一和单一职责都是为了提高累的内聚性,降低他们之间的耦合性,体现了封装的思想,但二者是不同的:
1)单一接口原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离
2)单一职责主要是约束类,他针对于程序中的实现和细节,接口隔离原则主要是约束接口,主要是对抽象和程序整体框架的构建
定义:高层模块不应该依赖底层模块,两者都应该依赖抽象;抽象不应该依赖细节,细节应该依赖抽象.其核心思想就是面向接口编程,不要面向实现编程.
依赖倒置原则是实现开闭原则的重要途径之一,它降低了客户与实现模块之间的耦合
由于在软件设计中,细节具有多变性,而抽象则相对比较稳定,因此以抽象为基础搭建的框架要比以细节搭建的矿机要稳定的多,这里的抽象指的是接口或者抽象类,儿细节是指具体的实现类
使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何操作,把展现细节的任务交给他们的实现类去完成.
优点:
1)依赖倒置可以降低类之间的耦合性
2)依赖倒置原则可以提高系统的稳定性
3)依赖倒置原则可以减少并行开发引起的风险
4)依赖倒置可以提高代码的可读性和可维护性.
实现方法:依赖倒置原则的目的主要通过面向接口编程来降低类之间的耦合性,所以我们在实际编程中只要遵循以下4四点:
1)每个类尽量提供接口或者抽象类,或者两者皆有
2)变量的声明类型尽量是接口或者是抽象类
3)任何类都不应该从具体类派生
4)使用继承是遵循里氏替换原则
定义:继承必须确保超类所拥有的性质在子类中仍然成立.里氏替换原则主要阐述了有关继承的一些原则,也就是什么时候应该使用继承,什么时候不应该使用继承,其中蕴含的道理.里氏替换原则是集成复用的基础,他反映了基类与子类的关系,是对开闭原则的补充.是对开闭原则的补充,是实现抽象画的具体步骤的规范
里氏替换原则通俗来讲就是,子类可以扩展父类的原有的功能,但是不能改变父类原有的功能.也就是说,出添加新的方法外,尽量不要重写父类的方法
优点:
1)里氏替换原则是实现开闭原则之一
2)他克服了继承中重写父类造成的可复用性变差的缺点
3)它的动作正确性的保证,及类的扩展不会给已有的系统引入新的系统错误,降低了代码出错的可能性
4)加强程序的健壮性,同时变更时可以做到非常好的兼容性,提高程序的维护性,可扩展性,降低需求变更同时引入的风险
实现方法:
1)子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法
2)子类可以增加自己特有的方法
3)当子类的方法可以重载父类的方法时,方法的前置条件是(方法的输入参数)要比父类的方法更加宽松
4)当子类的方法实现父类的方法时,(重写/重载或者实现抽象方法),方法的后置条件(即方法的输出/返回值)要比父类的方法更加严格或相等
通过重写父类的方法来完成新的功能写起来简单,但是整个继承体系的可复用性会比较差,特别是运用多台比较频繁时,程序运行出错的概率会非常大.如果程序违背了里氏替换原则,则继承类的对象在基类出现的地方出现运行出错.这时其修正方法是:取消原来的继承关系,重新设计他们之间的关系.
定义:软件实体应当对扩展开放,对修改关闭.软件实体包括以下几个部分
1.项目中划分出的模块 2.类与接口 3.方法
优点:开闭原则的话,软件测试只需要对扩展的代码进行测试就可以了,因为原有的测试代码仍然能可以正常运行
1)对软件测试的影响:软件遵守开闭原则的话,软件测试只需要对扩展的代码测试就可以了,因为原有的测试代码仍然可以正常运行
2)可以提高代码的复用性:粒度越小,被复用的可能性就越大.在面向对象的程序设计中,根据原子和抽象编程可以提高代码的可复用性
3)遵守开闭原则的软件,其稳定性高和延续性强,从而易于扩展和维护
实现方法:可以通过"抽象约束,适应变化"来实现开闭原则,即通过接口或者抽象类为软件实体定义一个相对比较稳定的抽象层,而将相同的具体的实现类中.因为抽象灵活性好,适应性广,只要抽象的合理,可以基本的软件架构的稳定.当软件需要发生变化是,只需要重新派生一个实现类就可以了
定义:只与你的直接朋友交谈,不跟"陌生人"说话,如果两个软件实体无须直接通信,那么就不应该直接相互调用,可以通过第三方调用.其目的是降低类之间的耦合度,提高模块的相对独立性
迪米特法则中的朋友是指:当前对象本身,当前对象的成员变量,当前对象方法参数等,这些对象同当前对象存在关联,聚合或组合关系,可以直接访问这些对象的方法
优点:
1)降低了类之间的耦合度,提高了模块的相对独立性。
2)由于亲合度降低,从而提高了类的可复用率和系统的扩展性。
过度使用迪米特法则会使系统产生大量的中介类,从而增加系统的复杂性,使模块之间的通信效率降低。所以,在釆用迪米特法则时需要反复权衡,确保高内聚和低耦合的同时,保证系统的结构清晰。
实现方法:
1.从迪米特法则的定义和特点可知,它强调以下两点:
2.从依赖者的角度来说,只依赖应该依赖的对象。
从被依赖者的角度说,只暴露应该暴露的方法。
所以,在运用迪米特法则时要注意以下 6 点。
1.在类的划分上,应该创建弱耦合的类。类与类之间的耦合越弱,就越有利于实现可复用的目标。
2.在类的结构设计上,尽量降低类成员的访问权限。
3.在类的设计上,优先考虑将一个类设置成不变类。
4.在对其他类的引用上,将引用其他对象的次数降到最低。
5.不暴露类的属性成员,而应该提供相应的访问器(set 和 get 方法)。
6.谨慎使用序列化(Serializable)功能。
定义:它要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
优点:
通常类的复用分为继承复用和合成复用两种,继承复用虽然有简单和易实现的优点,但它也存在以下缺点。
1)继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为“白箱”复用。
2)子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。
3)它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。
采用组合或聚合复用时,可以将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能,它有以下优点。
1)它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为“黑箱”复用。
2)新旧类之间的耦合度低。这种复用所需的依赖较少,新对象存取成分对象的唯一方法是通过成分对象的接口。
3)复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。
实现方法:合成复用原则是通过将已有的对象纳入新对象中,作为新对象的成员对象来实现的,新对象可以调用已有对象的功能,从而达到复用。
设计模式分为3大类:
1)创建型模式(5种):工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式
2)结构性模式(7种):适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式
3)行为性模式(11种):策略模式,模板方法模式,观察者模式,迭代子模式,责任链模式,命令模式,备忘录模式,状态模式,访问者模式,中介者模式,解释器模式
1)单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。
2)原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
3)工厂方法(Factory Method)模式:定义一个用于创建产品的接口,由子类决定生产什么产品。
4)抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。
5)建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。
6)代理(Proxy)模式:为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。
7)适配器(Adapter)模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
8)桥接(Bridge)模式:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
9)装饰(Decorator)模式:动态的给对象增加一些职责,即增加其额外的功能。
10)外观(Facade)模式:为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问。
11)享元(Flyweight)模式:运用共享技术来有效地支持大量细粒度对象的复用。
12)组合(Composite)模式:将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性。
13)模板方法(TemplateMethod)模式:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
14)策略(Strategy)模式:定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。
15)命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。
16)职责链(Chain of Responsibility)模式:把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。
17)状态(State)模式:允许一个对象在其内部状态发生改变时改变其行为能力。
18)观察者(Observer)模式:多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为。
19)中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。
20)迭代器(Iterator)模式:提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
21)访问者(Visitor)模式:在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问。
22)备忘录(Memento)模式:在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它。
23)解释器(Interpreter)模式:提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。
定义:指一个类只有一个实例,且能自行创建这个实例的一种模式.例如,windows中只能打开一个任务管理器,这样可以避免因打开多个任务管理器而造成内存资源浪费,而出现各个窗口显示内容的不一致错误.
单例模式特点:
1)单例只有一个实例对象
2)该单例对象必须由单例类自行创建
3)单例类对外提供一个访问单例的全局点
单例的优点:
1)单例例模式1可以保证内存中只有一个实例,减少了内存的消耗.
2)可以避免对资源的多重占用
3)单例模式设置全局访问点,可以优化和共享资源的访问
单例的缺点:
1)单例模式一般没有借口,扩展困难,如果要扩展,则修改了原来的代码,没有第二种途径违背开闭原则
2)在并发测试中,单例模式不利于代码调试.在调试过程中,如果单例中代码还没有执行完,也不能模拟生成一个新的对象.
3)单例模式通常写在一个类中,如果功能设计不合理,和容易违背单一职责原则
单例的应用场景:
1)需要频繁创建一些类,使用单例模式可以降低系统的内存压力,减少gc
2)某类只要求生成一个对象的时候,比如一个班的班长,每个人的身份证号
3)某类创建实例需要消耗资源过多,或者实例化消耗较长,且经常使用
4)频繁访问数据库或者文件的对象
5)默写类需要频繁实例化,而创建的对象又频繁销毁的时候,不如多线程的线程池,网络的连接池
6)对于一些控制硬件级别的操作,或者从协同上讲是单一控制的操作,如果有多个实例,则系统会乱套
7)当对象需要被共享的场合.由于单例模式只允许创建一个对象,共享对象可以节省内存,并加快对象访问速度.比如web中的配置对象,数据库连接池
懒汉式
package com.itheima.pattern._01_单例._01_懒汉式;
public class LazySingleton {
private static volatile LazySingleton instance = null;
private LazySingleton() {
}
public static LazySingleton getInstance() {
if (instance==null){
synchronized (LazySingleton.class){
if (instance==null){
instance=new LazySingleton();
}
}
}
return instance;
}
}
饿汉式
package com.itheima.pattern._01_单例._02_饿汉式;
public class HungrySingleton {
private static final HungrySingleton instance=new HungrySingleton();
private HungrySingleton(){
}
public static HungrySingleton getInstance(){
return instance;
}
}
关系类图
按照实际业务划分,工厂模式有3种不同的实现方式,分别是简单工厂模式,工厂方法模式,抽象工厂模式
工厂模式的定义:定义一个创建产品的工厂接口,将产品对象的实际创建工作推迟到具体子类中,这满足创建型模式所要求的"创建与使用"分离的特点
优点:
1)用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。
2)灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。
3)典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替 换原则。
缺点:
1)类的个数容易过多,增加复杂度
2)增加了系统的抽象性和理解难度
3)抽象产品只能生产一种产品,此弊端可使用抽象工厂模式解决
应用场景: 客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL电视工厂、海信电视工厂等。
创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
客户不关心创建产品的细节,只关心产品的品牌
业务实现
//抽象产品:提供了产品的接口
interface Product {
public void show();
}
//具体产品1:实现抽象产品中的抽象方法
class ConcreteProduct1 implements Product {
public void show() {
System.out.println("具体产品1显示...");
}
}
//具体产品2:实现抽象产品中的抽象方法
class ConcreteProduct2 implements Product {
public void show() {
System.out.println("具体产品2显示...");
}
}
//抽象工厂:提供了厂品的生成方法
interface AbstractFactory {
public Product newProduct();
}
//具体工厂1:实现了厂品的生成方法
class ConcreteFactory1 implements AbstractFactory {
public Product newProduct() {
System.out.println("具体工厂1生成-->具体产品1...");
return new ConcreteProduct1();
}
}
//具体工厂2:实现了厂品的生成方法
class ConcreteFactory2 implements AbstractFactory {
public Product newProduct() {
System.out.println("具体工厂2生成-->具体产品2...");
return new ConcreteProduct2();
}
}
关系类图
定义:是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。
抽象工厂模式是工厂方法模式的升级版,工厂方法模式只生产一个等级结构的产品,而抽象工厂模式可以生产多个等级产品
使用抽象工厂模式一般需要满足一下条件: 1)系统中有多个产品族,每个具体工厂创建一组但属于不同等级结构的产品.2)系统一次只能消费某一族的产品,即同族产品一起使用
优点:
1)可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理
2)当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。
3)抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。
缺点:
当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度
业务实现
//抽象产品:动物类
public interface Animal {
public void show();
}
//抽象产品:植物类
public interface Plant {
public void show();
}
//抽象工厂:农场类
public interface Farm {
public Animal productAnimal();
public Plant productPlant();
}
//公共基类
public class Basic {
public Basic(String name) {
this.name = name;
}
public Basic() {
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//具体动物类1
public class Cattle extends Basic implements Animal {
public Cattle(String name){
super(name);
}
@Override
public void show() {
System.out.println("我是:"+this.getName());
}
}
//具体动物类2
public class Horse extends Basic implements Animal {
public Horse(String name){
super(name);
}
@Override
public void show() {
System.out.println("我是:"+this.getName());
}
}
//具体植物类1
public class Vegetables extends Basic implements Plant {
public Vegetables(String name){
super(name);
}
@Override
public void show() {
System.out.println("我是:"+this.getName());
}
}
//具体植物类2
public class Fruitage extends Basic implements Plant {
public Fruitage(String name){
super(name);
}
@Override
public void show() {
System.out.println("我是:"+this.getName());
}
}
//具体农场类1
public class SGfarm implements Farm {
@Override
public Animal productAnimal() {
Animal animal = new Cattle("牛");
return animal;
}
@Override
public Plant productPlant() {
Plant plant=new Vegetables("蔬菜");
return plant;
}
}
//具体农场类2
public class SRfarm implements Farm {
@Override
public Animal productAnimal() {
Animal animal = new Horse("马");
return animal;
}
@Override
public Plant productPlant() {
Plant plant=new Fruitage("水果");
return plant;
}
}
//客户端
public static void main(String[] args) {
Farm farm1=new SGfarm();
Farm farm2=new SRfarm();
System.out.println("农场1------>");
Animal animal = farm1.productAnimal();
Plant plant = farm1.productPlant();
animal.show();
plant.show();
System.out.println("农场2------>");
Animal animal1 = farm2.productAnimal();
Plant plant1 = farm2.productPlant();
animal.show();
plant1.show();
}
关系类图
定义:指将一个复杂的对象的构造与它表示分离,使同样构建过程不同的表示,这样的设计模式被称为建造者模式.它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成的.它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的.
优点:
1)封装性好,构建与组成部分必须相同,其限制了使用范围.
2)扩展性好,各个具体建造者相互独立,有利于系统的解耦
3)客户端不必知道产品组成细节,建造者可以创建过程逐渐细化,而不对其他模块产生任何影响,便于控制 细节风险
缺点:
1)产品组成部分必须相同,这限制了其使用范围
2)如果产品内部变化复杂,内部发生变化,则建造者需要同步修改,后期维护成本比较大
建造者模式与工厂模式的关注点不同:建造者模式注重零部件的组装过程,而过程方法模式各部分注重零部件的创建过程
业务实现
//产品:客厅
public class Parlour {
private String wall; //墙
private String TV; //电视
private String sofa; //沙发
public void setWall(String wall) {
this.wall = wall;
}
public void setTV(String TV) {
this.TV = TV;
}
public void setSofa(String sofa) {
this.sofa = sofa;
}
@Override
public String toString() {
return "Parlour{" +
"wall='" + wall + '\'' +
", TV='" + TV + '\'' +
", sofa='" + sofa + '\'' +
'}';
}
}
//抽象建造者
public abstract class Decorator {
//创建产品对象
protected Parlour product = new Parlour();
public abstract void buildWall();
public abstract void buildTV();
public abstract void buildSofa();
//返回产品对象
public Parlour getResult() {
return product;
}
}
//具体建造者
public class ConcreteDecorator extends Decorator {
public void buildWall() {
product.setWall("w1");
}
public void buildTV() {
product.setTV("TV1");
}
public void buildSofa() {
product.setSofa("sf1");
}
}
//指挥者:项目经理
public class ProjectManager {
private Decorator builder;
public ProjectManager(Decorator builder) {
this.builder = builder;
}
//产品构建与组装方法
public Parlour decorate() {
builder.buildWall();
builder.buildTV();
builder.buildSofa();
return builder.getResult();
}
}
//客户端
public static void main(String[] args) {
Decorator decorator=new ConcreteDecorator();
ProjectManager projectManager = new ProjectManager(decorator);
Parlour parlour = projectManager.decorate();
System.out.println(parlour);
}
关系类图
定义:用已经创建的一个对象的实例作为原型,通过复制该原型对象来创建一个和原型相同或类似的新对象.在这里,原型实例指定要创建的对象的种类,这种创建方式无须知道对象创建的细节.例如windowss系统的安装通常比较消耗时间,如果复制就会快很多,这里就不一一列举了.
优点:
1)java自带的原型模式基于内存二进制流的复制,在性能上比直接new一个对象更加优良
2)可以使用深克隆方式,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候恢复到某一历史状态
缺点:
1)需要为每一个类都配置一个clone方法
2)clone方法位于类的内部,当对已有类进行改造时,违背了开闭原则
3)当实现深克隆时,需要编写比较复杂的代码,而且对象之间存在多重嵌套时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来比较麻烦.因此,深克隆,浅克隆需要运用得当
业务实现
//具体原型类
public class Realizetype implements Cloneable {
private String name;
public String getName() {
return name;
}
public Realizetype(String name) {
this.name=name;
}
@Override
public String toString() {
return "Realizetype{" +
"name='" + name + '\'' +
'}';
}
public Object clone() throws CloneNotSupportedException {
System.out.println("具体原型复制成功!");
return (Realizetype) super.clone();
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Realizetype)){
return false;
}
Realizetype realizetype = (Realizetype) obj;
return this.name.equals(realizetype.getName());
}
}
//原型模式的测试类
public class PrototypeTest {
public static void main(String[] args) throws CloneNotSupportedException {
Realizetype obj1 = new Realizetype("宋祖儿");
Realizetype obj2 = (Realizetype) obj1.clone();
System.out.println(obj1);
System.out.println(obj2);
System.out.println(obj1.equals(obj2));
System.out.println(obj1==obj2);
}
}
浅拷贝只能实现对基本类型的处理,而深拷贝要求非基本类型也不能指向同一个内存地址
//原型类
public class DeepCloneableTarget implements Serializable, Cloneable {
private static final long serialVersionUID = 1L;
private String cloneName;
private String cloneClass;
//构造器
public DeepCloneableTarget(String cloneName, String cloneClass) {
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}
//因为该类的属性,都是String , 因此我们这里使用默认的clone完成即可
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
//深拷贝实现的类
public class DeepProtoType implements Serializable, Cloneable{
private static final long serialVersionUID = 2L;
public String name; //String 属性
public DeepCloneableTarget deepCloneableTarget;// 引用类型
public DeepProtoType() {
super();
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object deep = null;
//这里完成对基本数据类型(属性)和String的克隆
deep = super.clone();
//对引用类型的属性,进行单独处理
DeepProtoType deepProtoType = (DeepProtoType)deep;
deepProtoType.deepCloneableTarget = (DeepCloneableTarget)deepCloneableTarget.clone();
// TODO Auto-generated method stub
return deepProtoType;
}
//深拷贝 - 方式2 通过对象的序列化实现 (推荐)(利用底层的序列化机制)
public Object deepClone() {
//创建流对象
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
//序列化()
bos = new ByteArrayOutputStream();//将对象写入内存
oos = new ObjectOutputStream(bos);
oos.writeObject(this); //当前这个对象以对象流的方式输出
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
DeepProtoType copyObj = (DeepProtoType)ois.readObject();
return copyObj;
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
return null;
} finally {
//关闭流
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (Exception e2) {
// TODO: handle exception
System.out.println(e2.getMessage());
}
}
}
关系类图
定义:将一个类的接口转换成客户希望的另外一个接口,使得原本不兼容的而不能一起工作的哪些类能一起工作.适配器模式分为类结构模式和对象结构模式,前者类之间的耦合度比较高,且要求程序员了解现有库中的相关组件的内部结构,所以应用相对少些
优点:
1)客户端通过可以透明地调用目标接口.
2)复用了现存的类,程序员不需要再修改原有代码而重用现在适配者的类
3)将目标类与适配者解耦,解决了目标类和适配器接口不一致的问题
4)在很多业务场景中符合开闭原则
缺点:
1)适配器编写过程需要结合业务场景全面考虑,可能会增加系统的复杂性。
2)增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。
应用场景:
1)以前开发系统存在满足新系统功能需求的类,但是借口与新系统不一致
2)使用第三方提供的组件,但组件接口定义和自己要求的接口定义不同
业务实现(类适配器)
//适配接口
public interface IVoltage5V {
public int output5V();
}
//被适配者
public class Voltage220V {
//输出220V的电压
public int output220V() {
int src = 220;
System.out.println("电压=" + src + "伏");
return src;
}
}
//适配器类
public class VoltageAdapter extends Voltage220V implements IVoltage5V {
@Override
public int output5V() {
// TODO Auto-generated method stub
//获取到220V电压
int srcV = output220V();
int dstV = srcV / 44 ; //转成 5v
return dstV;
}
}
public class Phone {
//充电
public void charging(IVoltage5V iVoltage5V) {
if(iVoltage5V.output5V() == 5) {
System.out.println("电压为5V, 可以充电~~");
} else if (iVoltage5V.output5V() > 5) {
System.out.println("电压大于5V, 不能充电~~");
}
}
}
//客户端
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(" === 类适配器模式 ====");
Phone phone = new Phone();
phone.charging(new VoltageAdapter());
}
对象适配器
//适配器类
public class VoltageAdapter implements IVoltage5V {
private Voltage220V voltage220V; // 关联关系-聚合
//通过构造器,传入一个 Voltage220V 实例
public VoltageAdapter(Voltage220V voltage220v) {
this.voltage220V = voltage220v;
}
@Override
public int output5V() {
int dst = 0;
if(null != voltage220V) {
int src = voltage220V.output220V();//获取220V 电压
System.out.println("使用对象适配器,进行适配~~");
dst = src / 44;
System.out.println("适配完成,输出的电压为=" + dst);
}
return dst;
}
}
//客户端
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(" === 对象适配器模式 ====");
Phone phone = new Phone();
phone.charging(new VoltageAdapter(new Voltage220V()));
}
}
关系类图
定义:将抽象与实现分离使他们可以独立变化.这是用组合关系代替继承关系,从而降低了抽象与实现两个可变额度的耦合度
优点:
1)抽象与实现分离,扩展能力强
2)符合开闭原则,合成复用原则
3)其实现细节对客户透明
缺点:由于聚合关系建立在抽象层,要求开发者针对抽象化进行设计与编程,能正确地识别系统中两个独立变化的维度,这增加了系统的理解与设计难度.
当一个类中存在两种或者多种变化维度是,使用桥接模式可以解耦这些变化的维度,使高层代码
应用场景:
1)当一个类存在两个独立变化的维度,而且这两个维度都需要进行扩展
2)当一个系统不希望使用继承或因为继承导致系统类之间个数急剧增加
3)当一个系统需要在构件的抽象化角色与具体化角色之间增加更多的灵活性
桥接模式的一个常见使用场景就是替换继承。我们知道,继承拥有很多优点,比如,抽象、封装、多态等,父类封装共性,子类实现特性。继承可以很好的实现代码复用(封装)的功能,但这也是继承的一大缺点
因为父类拥有的方法,子类也会继承得到,无论子类需不需要,这说明继承具备强侵入性(父类代码侵入子类),同时会导致子类臃肿。因此,在设计模式中,有一个原则为优先使用组合/聚合,而不是继承
业务实现
//实现化角色:颜色
public interface Color {
String getColor();
}
//具体实现化角色:红色
public class Red implements Color {
public String getColor() {
return "red";
}
}
//具体实现化角色:黄色
class Yellow implements Color {
public String getColor() {
return "yellow";
}
}
//抽象化角色:包
public abstract class Bag {
protected Color color;
public void setColor(Color color) {
this.color = color;
}
public abstract String getName();
}
//扩展抽象化角色:挎包
public class HandBag extends Bag {
public String getName() {
return color.getColor() + "HandBag";
}
}
//扩展抽象化角色:钱包
public class Wallet extends Bag {
public String getName() {
return color.getColor() + "Wallet";
}
}
//客户端
public static void main(String[] args) {
Color color=new Red();
Bag bag=new Wallet();
bag.setColor(color);
System.out.println(bag.getName());
}
关系类图
定义:指在不改变现有对象结构的情况下,动态地给对象增加一些职责(即增加额外功能)的模式,它属于对象结构模式.
优点:
1)装饰器是继承的有力补充,比继承灵活,不改变原有对象的情况下,动态给一个对象扩展功能,即插即用
2)通过使用不用装饰类以及这些装饰类的排列组合,可以实现不同效果
3)装饰者模式完全遵守开闭原则
缺点:装饰者模式会增加许多子类,过度使用会增加程序的复杂性
应用场景:
1)当需要给一个现有类附加职责,而又不能采用生成子类的方法进行扩充是.例如,该类被隐藏或者该类是中级类或者采用继承方式产生大量的子类
2)当需要对一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰者模式却很好实现
3)当对象的功能要求可以动态地添加,也可以动态地撤销时
业务实现
//抽象构件角色
public interface Component {
public void operation();
}
//具体构件角色
public class ConcreteComponent implements Component {
public ConcreteComponent() {
System.out.println("创建具体构件角色");
}
public void operation() {
System.out.println("调用具体构件角色的方法operation()");
}
}
//抽象装饰角色
public abstract class Decorator implements Component {
private Component component;
public Decorator(Component component) {
this.component = component;
}
public void operation() {
component.operation();
}
}
//具体装饰角色
public class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Component component) {
super(component);
}
public void operation() {
super.operation();
this.addedFunction();
}
public void addedFunction() {
System.out.println("为具体构件角色增加额外的功能addedFunction()");
}
}
//客户端
public static void main(String[] args) {
Component p = new ConcreteComponent();
p.operation();
System.out.println("---------------------------------");
Component d = new ConcreteDecorator(p);
d.operation();
}
关系类图2
定义:有时叫整体-部分模式,它是一种将对象组合成树状的层次结构的模式,用来表示"整体-部分"的关系,使用户对单个对象和组合对象具有一致访问性,属于结构性设计模式
组合模式用来描述整体与部分的关系,他将对象阻止到树形结构中,顶层的节点被称为根节点,根节点下面可以包含树枝节点和叶子节点看做属于同一种数据类型(统一接口定义),让他们具备一致行为; 这样,在组合模式中,整个树形结构的对象都属于同一种类型,带来的好处就是用户不需要辨别树枝节点还是叶子节点,可以直接进行操作,给用户的使用带来极大的方便.
优点:
1)组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象还是组合对象,这简化了客户端代码;
2)更容易在组合体内加入新对象,客户端不会因为加入新对象而更改源码,满足开闭原则
缺点:
1)设计比较复杂,客户端需要花更多时间理清楚类之间的层次关系;
2)不容易限制容器中的构件
3)不容易用继承的方法来增加构件的新功能
应用场景:
1)在需要表示一个对象整体与部分的层次结构的场合。
2)要求对用户隐藏组合对象与单个对象的不同,用户可以用统一的接口使用组合结构中的所有对象的场合。
业务实现
//抽象构件
public interface Component {
public void add(Component c);
public void remove(Component c);
public Component getChild(int i);
public void operation();
}
//树枝构件
public class Composite implements Component {
private ArrayList<Component> children = new ArrayList<Component>();
public void add(Component c) {
children.add(c);
}
public void remove(Component c) {
children.remove(c);
}
public Component getChild(int i) {
return children.get(i);
}
public void operation() {
for (Object obj : children) {
((Component) obj).operation();
}
}
}
//树叶构件
public class Leaf implements Component {
private String name;
public Leaf(String name) {
this.name = name;
}
public void add(Component c) {
throw new RuntimeException();
}
public void remove(Component c) {
throw new RuntimeException();
}
public Component getChild(int i) {
throw new RuntimeException();
}
public void operation() {
System.out.println("树叶" + name + ":被访问!");
}
}
//客户端
public static void main(String[] args) {
Component c0 = new Composite();
Component c1 = new Composite();
Component leaf1 = new Leaf("1");
Component leaf2 = new Leaf("2");
Component leaf3 = new Leaf("3");
c0.add(leaf1);
c0.add(c1);
c1.add(leaf2);
c1.add(leaf3);
c0.operation();
}
关系类图
定义:又称作门面模式,是一种通过多个复杂的子系统提供一个一致的接口,而使这些子系统更容易被访问的模式,该模式对外有一个接口,外部应用程序不用关心内部子系统的具体细节,这样会大大降低应用程序的复杂度,提高了成
优点:"迪米特法则"的典型应用
1)降低了子系统客户端之间的耦合度,使得子系统的变化不会影响他们的客户类
2)对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统不会影响它的客户类
3)降低了大型软件的编译依赖性,简化了系统在不同系统之间的移植过程,因为编译一个子系统不会影响另一个子系统,也不会影响外观对象
缺点:
1)不能很好地限制客户在使用子系统类,很容易带来未知风险
2)增加新的子系可能需要修改客户端或者外观类代码,违背了开闭原则
应用场景:
1)对分层结构系统构建时,使用外观模式定义子系统每层的入口点可以简化子系统之间的依赖关系
2)当一个复杂系统的子系统很多时,外观模式可以为设计一个简单的接口提供外界访问
业务实现
//子系统角色
public class SubSystem01 {
public void method1() {
System.out.println("子系统01的method1()被调用!");
}
}
//子系统角色
public class SubSystem02 {
public void method2() {
System.out.println("子系统02的method2()被调用!");
}
}
//子系统角色
class SubSystem03 {
public void method3() {
System.out.println("子系统03的method3()被调用!");
}
}
//外观角色
public class Facade {
private SubSystem01 obj1 = new SubSystem01();
private SubSystem02 obj2 = new SubSystem02();
private SubSystem03 obj3 = new SubSystem03();
public void method() {
obj1.method1();
obj2.method2();
obj3.method3();
}
}
//客户端
public static void main(String[] args) {
Facade f = new Facade();
f.method();
}
关系类图
定义:运用共享技术来有效地支持大量细粒度对象复用.它通过已存在的对象来大幅度减少需要创建的对象数量,避免大量相似的开销,从而提高系统资源的利用率.
优点:相同对象只需要保存一份,这就降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力
缺点:
1)为了使对象可以共享,需要将一些不能共享的状态外部变化,这将增加程序的复杂性
2)读取享元模式的外部状态会使运行时间稍微变长
享元模式的本质是缓存共享对象,降低内存消耗。
应用场景:
1)系统中存在大量相同或相似的对象,这些对象耗费大量的内存资源。
2)大部分的对象可以按照内部状态进行分组,且可将不同部分外部化,这样每一个组只需保存一个内部状态。
3)由于享元模式需要额外维护一个保存享元的数据结构,所以应当在有足够多的享元实例时才值得使用享元模式。
业务实现
//抽象享元角色
public interface Flyweight {
public void operation(UnsharedConcreteFlyweight state);
}
//具体享元角色
public class ConcreteFlyweight implements Flyweight {
private String key;
public ConcreteFlyweight(String key) {
this.key = key;
System.out.println("具体享元" + key + "被创建!");
}
public void operation(UnsharedConcreteFlyweight outState) {
System.out.print("具体享元" + key + "被调用,");
System.out.println("非享元信息是:" + outState.getInfo());
}
}
//非享元角色
class UnsharedConcreteFlyweight {
private String info;
UnsharedConcreteFlyweight(String info) {
this.info = info;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
//享元工厂角色
public class FlyweightFactory {
private HashMap<String, Flyweight> flyweights = new HashMap<String, Flyweight>();
public Flyweight getFlyweight(String key) {
Flyweight flyweight = (Flyweight) flyweights.get(key);
if (flyweight != null) {
System.out.println("具体享元" + key + "已经存在,被成功获取!");
} else {
flyweight = new ConcreteFlyweight(key);
flyweights.put(key, flyweight);
}
return flyweight;
}
}
//客户端
public static void main(String[] args) {
FlyweightFactory factory = new FlyweightFactory();
Flyweight f01 = factory.getFlyweight("a");
Flyweight f02 = factory.getFlyweight("a");
Flyweight f03 = factory.getFlyweight("a");
Flyweight f11 = factory.getFlyweight("b");
Flyweight f12 = factory.getFlyweight("b");
f01.operation(new UnsharedConcreteFlyweight("第1次调用a。"));
f02.operation(new UnsharedConcreteFlyweight("第2次调用a。"));
f03.operation(new UnsharedConcreteFlyweight("第3次调用a。"));
f11.operation(new UnsharedConcreteFlyweight("第1次调用b。"));
f12.operation(new UnsharedConcreteFlyweight("第2次调用b。"));
}
关系类图
定义:由于某些原因需要给对象提供一个代理以控制对象的访问.这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象的中介
优点:
1)代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用
2)代理对象可以扩展目标对象的功能
3)代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性
缺点:
1)代理模式会造成系统设计中类数量的增加
2)在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢
3)增加了系统的复杂度
应用场景:
1)远程代理:这种方式通常为了隐藏目标存在不同地址的空间事实,方便客户端访问.例如,用户申请某些网盘空间时,会在用户的文件系统中建立一个虚拟的硬盘,用户访问虚拟硬盘时实际访问的是网盘空间
2)虚拟代理,这种方式通常用于要创建的目标对象开销很大时。例如,下载一幅很大的图像需要很长时间,因某种计算比较复杂而短时间无法完成,这时可以先用小比例的虚拟代理替换真实的对象,消除用户对服务器慢的感觉
3)安全代理,这种方式通常用于控制不同种类客户对真实对象的访问权限。
4)智能指引,主要用于调用目标对象时,代理附加一些额外的处理功能。例如,增加计算真实对象的引用次数的功能,这样当该对象没有被引用时,就可以自动释放它
5)延迟加载,指为了提高系统的性能,延迟对目标的加载。例如,Hibernate 中就存在属性的延迟加载和关联表的延时加载。
业务实现
//接口
public interface ITeacherDao {
void teach(); // 授课的方法
}
public class TeacherDao implements ITeacherDao {
@Override
public void teach() {
// TODO Auto-generated method stub
System.out.println(" 老师授课中 。。。。。");
}
}
//代理对象,静态代理
public class TeacherDaoProxy implements ITeacherDao{
private ITeacherDao target; // 目标对象,通过接口来聚合
//构造器
public TeacherDaoProxy(ITeacherDao target) {
this.target = target;
}
@Override
public void teach() {
// TODO Auto-generated method stub
System.out.println("开始代理 完成某些操作。。。。。 ");//方法
target.teach();
System.out.println("提交。。。。。");//方法
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建目标对象(被代理对象)
TeacherDao teacherDao = new TeacherDao();
//创建代理对象, 同时将被代理对象传递给代理对象
TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);
//通过代理对象,调用到被代理对象的方法
//即:执行的是代理对象的方法,代理对象再去调用目标对象的方法
teacherDaoProxy.teach();
}
关系类图
定义:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变改算法的结构情况下重定义改算法的某些步骤.
优点:
1)它封装了不变部分,他把认为是不变的部分的算法封装到父类中实现,而把可变算法由子类继承实现,便于子类继续扩 展
2)他在父类中提取了公共的部分代码,便于代码复用
3)部分方法是有子类实现的,因此子类方法可以通过扩展的方式增加相应的功能
缺点:
1)对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,间接地增加了系统实现的复杂度。
2)父类中的抽象方法由子类实现,子类的执行结果会
3)
应用场景:
1)算法整体步骤很固定,但是其中个别易变时,这个时候使用模板方法模式,将容易变得部分抽象出来,供子类实现
2)当多个子类存在公共行为时,可以将其提取到公共父类中可以避免代码重复,首先,要识别现有代码中的不同之处,并且将现有代码中的不同之处,并且将不同之处分离为新的操作.最后用一个调用这些最新的操作模板方法模式来替换这些不同的代码
3)当需要控制子类时,模板方法只在特定调用钩子方法,这样就允许这些点进行扩展
业务实现
//抽象类
public abstract class AbstractClass {
//模板方法
public void TemplateMethod() {
SpecificMethod();
abstractMethod1();
abstractMethod2();
}
//具体方法
public void SpecificMethod() {
System.out.println("抽象类中的具体方法被调用...");
}
//抽象方法1
public abstract void abstractMethod1();
//抽象方法2
public abstract void abstractMethod2();
}
//具体子类
public class ConcreteClass extends AbstractClass {
public void abstractMethod1() {
System.out.println("抽象方法1的实现被调用...");
}
public void abstractMethod2() {
System.out.println("抽象方法2的实现被调用...");
}
}
//客户端
public static void main(String[] args) {
AbstractClass tm = new ConcreteClass();
tm.TemplateMethod();
}
关系类图
定义:将一个请求封装成一个对象,使发出请求的责任和执行请求的责任分开.这样两者可以通过命令对象来进行沟通,这样方便将命令对象进行存储,传递,调用,增加和管理
优点:
1)通过引入中间件(抽象接口)降低系统的耦合度
2)扩展性好,增加或者删除命令比较方便.采用命令模式增加和删除不会影响其他类,且满足开闭原则
3)可以实现Undo与Redo操作.命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复
缺点:
1)可能产生大量的命令类,因为每一个具体操作都需要设计一个具体的命令类,这会增加系统的复杂性
2)命令模式的结果其实就是接收方的执行结果,但是为了以命令的形式进行架构,解耦请求与实现,引入了额外的类型结构(引入了请求方和抽象接口),增加理解上的困难.不过这也是设计模式的通病,抽象必然会额外增加类的数量,代码抽了李肯定比代码聚合更加难以理解
应用场景:
1)请求调用需要与请求接受者解耦时,命令模式可以使调用者和接受者不直接交互
2)系统随机请求命令经常增加,删除命令时,命令模式可以方便地实现这些功能
3)当系统随机需要执行一组操作是,命令模式可以定义宏命令实现该命令
4)当系统需要支持命令的撤销操作与恢复操作时,可以将命令对象存储起来,使用备忘录模式实现
业务实现
//抽象命令
public interface Command {
public abstract void execute();
}
//具体命令
public class ConcreteCommand implements Command {
private Receiver receiver;
ConcreteCommand() {
receiver = new Receiver();
}
public void execute() {
receiver.action();
}
}
//执行者
public class Receiver {
public void action() {
System.out.println("接收者的action()方法被调用...");
}
}
//调用者
public class Invoker {
private Command command;
public Invoker(Command command) {
this.command = command;
}
public void setCommand(Command command) {
this.command = command;
}
public void call() {
System.out.println("调用者执行命令command...");
command.execute();
}
}
//客户端
public static void main(String[] args) {
Command cmd = new ConcreteCommand();
Invoker ir = new Invoker(cmd);
System.out.println("客户访问调用者的call()方法...");
ir.call();
}
关系类图
定义:指多个对象之间存在一对多的依赖关系,当一个对象状态发生变化时,所有依赖它的所有对象都得到通知并自动更新.这种模式又称为发布订阅模式
优点:
1)降低了目标与观察者之间的耦合关系,两者关系之间的抽象耦合关系.符合依赖倒置原则
2)目标与观察者对象很多,通知的发布会花费很多时间,影响程序的效率
缺点:
1)目标与观察者之间的耦合关系,两者之间是抽象耦合关系.符合循环依赖引用
2)当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率
应用场景:
1)对象之间一对多关系,一个对象状态发生变化会影响其他对象
2)当一个抽象模型有两个方面,其中一个方面依赖另一方面,可将这二者封装在独立的对象中使他们可以独立地改变和复用
3)实现类似广播机制的功能,不需要知道具体的接听者,值需要分发广播,系统中感兴趣
4)多层级嵌套使用,形成一种链式触发机制,使得事件具备跨域(跨越两种观察者模式)通知
业务实现
//抽象观察者
public interface Observer {
void response(); //反应
}
//具体观察者1
class ConcreteObserver1 implements Observer {
public void response() {
System.out.println("具体观察者1作出反应!");
}
}
//具体观察者1
class ConcreteObserver2 implements Observer {
public void response() {
System.out.println("具体观察者2作出反应!");
}
}
//抽象目标
public abstract class Subject {
protected List<Observer> observers = new ArrayList<Observer>();
//增加观察者方法
public void add(Observer observer) {
observers.add(observer);
}
//删除观察者方法
public void remove(Observer observer) {
observers.remove(observer);
}
public abstract void notifyObserver(); //通知观察者方法
}
//具体目标
public class ConcreteSubject extends Subject {
public void notifyObserver() {
System.out.println("具体目标发生改变...");
System.out.println("--------------");
for (Object obs : observers) {
((Observer) obs).response();
}
}
}
关系类图
定义:将作用某种数据结构中各元素的操作分离出来封装成独立的类.使其不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构的每个元素提供多种访问方式.它对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式
优点:
1)扩展性好,能够在不修改对象结构中元素的情况下,对象结构中的元素添加新的功能
2)复用性好,可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用性
3)灵活性,访问者模式将数据结构作用于结构上的操作解耦,使得操作集合可相对自有地演化而不影响系统的数据结构
4)符合单一职责原则.访问者模式把相关的行为封装到一起,构成一个访问者,使得每一个访问者功能都比较单一
缺点:
1)增加新的元素很困难,在访问者模式中,每增加一个新的元素类,都要在每一个具体访问这类中增加相应的具体操作,这违背了"开闭原则"
2)破坏封装,访问者模式中具体元素对访问者公布细节,这破坏了类的封装性
3违反了依赖倒置原则.访问者模式依赖了具体类,而没有依赖抽象类
应用场景:
当系统中存在类型稳定一类的数据结构时,可以使用访问者模式方便地实现该类型所有数据结构的不同操作,而又不会产生任何副作用(脏数据),就是当集合中的不同类型金星多重操作时,使用访问者模式
1)对象结构很稳定,其操作算法经常变化的程序
2)对象结构的对象需要提供多种不同且不相关的操作,而且要避免让这些操作变化影响对象的数据结构
3)对象结构包含很多类型的对象,希望这些对象试试依赖一些具体类型的操作
业务实现
//抽象元素类
public interface Element {
void accept(Visitor visitor);
}
//具体元素A类
public class ConcreteElementA implements Element {
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String operationA() {
return "具体元素A的操作。";
}
}
//具体元素B类
public class ConcreteElementB implements Element {
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String operationB() {
return "具体元素B的操作。";
}
}
//抽象访问者
public interface Visitor {
void visit(ConcreteElementA element);
void visit(ConcreteElementB element);
}
//具体访问者A类
class ConcreteVisitorA implements Visitor {
public void visit(ConcreteElementA element) {
System.out.println("具体访问者A访问-->" + element.operationA());
}
public void visit(ConcreteElementB element) {
System.out.println("具体访问者A访问-->" + element.operationB());
}
}
//具体访问者B类
public class ConcreteVisitorB implements Visitor {
public void visit(ConcreteElementA element) {
System.out.println("具体访问者B访问-->" + element.operationA());
}
public void visit(ConcreteElementB element) {
System.out.println("具体访问者B访问-->" + element.operationB());
}
}
//对象结构角色
public class ObjectStructure {
private List<Element> list = new ArrayList<Element>();
public void accept(Visitor visitor) {
Iterator<Element> i = list.iterator();
while (i.hasNext()) {
((Element) i.next()).accept(visitor);
}
}
public void add(Element element) {
list.add(element);
}
public void remove(Element element) {
list.remove(element);
}
}
//客户端
public static void main(String[] args) {
ObjectStructure os = new ObjectStructure();
os.add(new ConcreteElementA());
os.add(new ConcreteElementB());
Visitor visitor = new ConcreteVisitorA();
os.accept(visitor);
System.out.println("------------------------");
visitor = new ConcreteVisitorB();
os.accept(visitor);
}
关系类图
定义:迭代器(Iterator)模式的定义:提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示
优点:
1)访问一个聚合对象的内容而无须暴露它的内部表示。
2)遍历任务交由迭代器完成,这简化了聚合类。
3)它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历。
4)增加新的聚合类和迭代器类都很方便,无须修改原有代码。
5)封装性良好,为遍历不同的聚合结构提供一个统一的接口
缺点:
增加了类的个数,这在一定程度上增加了系统的复杂性。
应用场景:
1)当需要为聚合对象提供多种遍历方式时。
2)当需要为遍历不同的聚合结构提供一个统一的接口时。
3)当访问一个聚合对象的内容而无须暴露其内部细节的表示时。
业务实现
//抽象迭代器
public interface Iterator {
Object first();
Object next();
boolean hasNext();
}
//具体迭代器
public class ConcreteIterator implements Iterator {
private List<Object> list = null;
private int index = -1;
public ConcreteIterator(List<Object> list) {
this.list = list;
}
public boolean hasNext() {
if (index < list.size() - 1) {
return true;
} else {
return false;
}
}
public Object first() {
index = 0;
Object obj = list.get(index);
return obj;
}
public Object next() {
Object obj = null;
if (this.hasNext()) {
obj = list.get(++index);
}
return obj;
}
}
//抽象聚合
public interface Aggregate {
public void add(Object obj);
public void remove(Object obj);
public Iterator getIterator();
}
//具体聚合
public class ConcreteAggregate implements Aggregate {
private List<Object> list = new ArrayList<Object>();
public void add(Object obj) {
list.add(obj);
}
public void remove(Object obj) {
list.remove(obj);
}
public Iterator getIterator() {
return (new ConcreteIterator(list));
}
}
//客户端
public static void main(String[] args) {
Aggregate ag = new ConcreteAggregate();
ag.add("中山大学");
ag.add("华南理工");
ag.add("韶关学院");
System.out.print("聚合的内容有:");
Iterator it = ag.getIterator();
while (it.hasNext()) {
Object ob = it.next();
System.out.print(ob.toString() + "\t");
}
Object ob = it.first();
System.out.println("\nFirst:" + ob.toString());
}
关系类图
定义:定义一个中介者对象来封装一系列对象之间的交行户,使原有之间的耦合 疏散,可以独立地改变他们之间的交互,中介者模式有叫做调停模式,是迪米特法则的典型应用
优点:
1)类之间各司其职,符合迪米特法则
2)降低了对象之间的耦合性,使得对象独立地复用
3)使对象之间一对多关系转变为一对一的关系,提高系统的灵活性,使得系统已于维护和扩展
缺点:
中介者模式将原本多个对象直接的相互依赖变成了中介者和多个同事类的依赖关系。当同事类越多时,中介者就会越臃肿,变得复杂且难以维护
应用场景:
1)当对象之间存在复杂的网状结构关系而导致依赖关系混乱且难以复用时。
2)当想创建一个运行于多个类之间的对象,又不想生成新的子类时。
业务实现
//抽象中介者
public abstract class Mediator {
public abstract void register(Colleague colleague);
public abstract void relay(Colleague cl); //转发
}
//具体中介者
public class ConcreteMediator extends Mediator {
private List<Colleague> colleagues = new ArrayList<Colleague>();
public void register(Colleague colleague) {
if (!colleagues.contains(colleague)) {
colleagues.add(colleague);
colleague.setMedium(this);
}
}
public void relay(Colleague cl) {
for (Colleague ob : colleagues) {
if (!ob.equals(cl)) {
((Colleague) ob).receive();
}
}
}
}
//抽象同事类
public abstract class Colleague {
protected Mediator mediator;
public void setMedium(Mediator mediator) {
this.mediator = mediator;
}
public abstract void receive();
public abstract void send();
}
//具体同事类
public class ConcreteColleague1 extends Colleague {
public void receive() {
System.out.println("具体同事类1收到请求。");
}
public void send() {
System.out.println("具体同事类1发出请求。");
mediator.relay(this); //请中介者转发
}
}
//具体同事类
public class ConcreteColleague2 extends Colleague {
public void receive() {
System.out.println("具体同事类2收到请求。");
}
public void send() {
System.out.println("具体同事类2发出请求。");
mediator.relay(this); //请中介者转发
}
}
定义: 不破坏封装的前提下,铺货一个对象的内部状态,并且在保存改对象之前
优点:
1)提供了一种可以恢复状态的机制.当用户需要时能够比较方便将数据恢复到某个历史状态
2)实现了内部状态的封装,除了创建它的发起人之外,其他对象能够访问这些状态信息
3)简化了发起人类,发起人不需要管理和保存内部状态的各个备份,所有状态信息都保存在备忘录信息,并由管理者进行管理,这符合单一职责2原则
缺点:
资源消耗比较大,如果要保存内部状态信息过多或者特别频繁,将会占用资源比较大的内存资源
应用场景:
1)需要保存与恢复数据的场景,如玩游戏时的中间结果的存档功能。
2)需要提供一个可回滚操作的场景,如 Word、记事本、Photoshop,Eclipse 等软件在编辑时按 Ctrl+Z 组合键,还有数据库中事务操作。
业务实现
//备忘录
public class Memento {
private String state;
//构造器
public Memento(String state) {
super();
this.state = state;
}
public String getState() {
return state;
}
}
public class Originator {
private String state;//状态信息
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
//编写一个方法,可以保存一个状态对象 Memento
//因此编写一个方法,返回 Memento
public Memento saveStateMemento() {
return new Memento(state);
}
//通过备忘录对象,恢复状态
public void getStateFromMemento(Memento memento) {
state = memento.getState();
}
}
//管理者
public class Caretaker {
//在List 集合中会有很多的备忘录对象
private List<Memento> mementoList = new ArrayList<Memento>();
public void add(Memento memento) {
mementoList.add(memento);
}
//获取到第index个Originator 的 备忘录对象(即保存状态)
public Memento get(int index) {
return mementoList.get(index);
}
}
//客户端
public static void main(String[] args) {
// TODO Auto-generated method stub
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
originator.setState(" 状态#1 攻击力 100 ");
//保存了当前的状态
caretaker.add(originator.saveStateMemento());
originator.setState(" 状态#2 攻击力 80 ");
caretaker.add(originator.saveStateMemento());
originator.setState(" 状态#3 攻击力 50 ");
caretaker.add(originator.saveStateMemento());
System.out.println("当前的状态是 =" + originator.getState());
//希望得到状态 1, 将 originator 恢复到状态1
originator.getStateFromMemento(caretaker.get(0));
System.out.println("恢复到状态1 , 当前的状态是");
System.out.println("当前的状态是 =" + originator.getState());
}
关系类图
定义:给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。
优点:
1)扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
2)容易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。
缺点:
1)执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。
2)会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护。
3)可应用的场景比较少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。
应用场景:
1)当语言的文法较为简单,且执行效率不是关键问题时。
2)当问题重复出现,且可以用一种简单的语言来进行表达时。
3)当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候,如 XML 文档解释。
业务实现
//抽象表达式类
public interface AbstractExpression {
public void interpret(String info); //解释方法
}
//终结符表达式类
public class TerminalExpression implements AbstractExpression {
public void interpret(String info) {
//对终结符表达式的处理
}
}
//非终结符表达式类
public class NonterminalExpression implements AbstractExpression {
private AbstractExpression exp1;
private AbstractExpression exp2;
public void interpret(String info) {
//非对终结符表达式的处理
}
}
//环境类
public class Context {
private AbstractExpression exp;
public Context() {
//数据初始化
}
public void operation(String info) {
//调用相关表达式类的解释方法
}
}
关系类图
定义:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
优点:
1)结构清晰,状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足单一职责原则
2)将状态转换显示化,减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
3)状态类职责明确,有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。
缺点:
1)状态模式的使用必然会增加系统的类与对象的个数。
2)状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。
3)状态模式对开闭原则的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源码,否则无法切换到新增状态,而且修改某个状态类的行为也需要修改对应类的源码。
应用场景:
1)当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。
2)一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。
业务实现
//环境类
public class Context {
private State state;
//定义环境类的初始状态
public Context() {
this.state = new ConcreteStateA();
}
//设置新状态
public void setState(State state) {
this.state = state;
}
//读取状态
public State getState() {
return (state);
}
//对请求做处理
public void Handle() {
state.Handle(this);
}
}
//抽象状态类
public abstract class State {
public abstract void Handle(Context context);
}
//具体状态A类
public class ConcreteStateA extends State {
public void Handle(Context context) {
System.out.println("当前状态是 A.");
context.setState(new ConcreteStateB());
}
}
//具体状态B类
public class ConcreteStateB extends State {
public void Handle(Context context) {
System.out.println("当前状态是 B.");
context.setState(new ConcreteStateA());
}
}
//客户端
public static void main(String[] args) {
Context context = new Context(); //创建环境
context.Handle(); //处理请求
context.Handle();
context.Handle();
context.Handle();
}
定义:该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理
优点:
1)多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句,如 if...else 语句、switch...case 语句。
2)策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。
3)策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。
4)策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。
5)策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。
缺点:
1)客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。
2)策略模式造成很多的策略类,增加维护难度。
应用模式:
1)一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中。
2)一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个条件分支移入它们各自的策略类中以代替这些条件语句。
3)系统中各算法彼此完全独立,且要求对客户隐藏具体算法的实现细节时。
4)系统要求使用算法的客户不应该知道其操作的数据时,可使用策略模式来隐藏与算法相关的数据结构。
5)多个类只区别在表现行为不同,可以使用策略模式,在运行时动态选择具体要执行的行为。
业务实现
public class StrategyPattern {
public static void main(String[] args) {
Context c = new Context();
Strategy s = new ConcreteStrategyA();
c.setStrategy(s);
c.strategyMethod();
System.out.println("-----------------");
s = new ConcreteStrategyB();
c.setStrategy(s);
c.strategyMethod();
}
}
//抽象策略类
interface Strategy {
public void strategyMethod(); //策略方法
}
//具体策略类A
class ConcreteStrategyA implements Strategy {
public void strategyMethod() {
System.out.println("具体策略A的策略方法被访问!");
}
}
//具体策略类B
class ConcreteStrategyB implements Strategy {
public void strategyMethod() {
System.out.println("具体策略B的策略方法被访问!");
}
}
//环境类
class Context {
private Strategy strategy;
public Strategy getStrategy() {
return strategy;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public void strategyMethod() {
strategy.strategyMethod();
}
}
定义:为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
优点:
1)降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
2)增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
3)增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
4)责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
5)责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
缺点:
1)不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
2)对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
3)职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
应用模式:
1)多个对象可以处理一个请求,但具体由哪个对象处理该请求在运行时自动确定。
2)可动态指定一组对象处理请求,或添加新的处理者。
3)需要在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。
业务实现
package chainOfResponsibility;
public class ChainOfResponsibilityPattern {
public static void main(String[] args) {
//组装责任链
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();
handler1.setNext(handler2);
//提交请求
handler1.handleRequest("two");
}
}
//抽象处理者角色
abstract class Handler {
private Handler next;
public void setNext(Handler next) {
this.next = next;
}
public Handler getNext() {
return next;
}
//处理请求的方法
public abstract void handleRequest(String request);
}
//具体处理者角色1
class ConcreteHandler1 extends Handler {
public void handleRequest(String request) {
if (request.equals("one")) {
System.out.println("具体处理者1负责处理该请求!");
} else {
if (getNext() != null) {
getNext().handleRequest(request);
} else {
System.out.println("没有人处理该请求!");
}
}
}
}
//具体处理者角色2
class ConcreteHandler2 extends Handler {
public void handleRequest(String request) {
if (request.equals("two")) {
System.out.println("具体处理者2负责处理该请求!");
} else {
if (getNext() != null) {
getNext().handleRequest(request);
} else {
System.out.println("没有人处理该请求!");
}
}
}
}