设计模式参考网址1:http://c.biancheng.net/view/1354.html
设计模式参考网址2:https://www.runoob.com/design-pattern/strategy-pattern.html
总体来说设计模式分为三大类:
创建型模式 5
---------工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式 7
---------适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式 11
---------策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
最常用
---------工厂模式,单例模式,建造者模式,代理模式。
问题1:什么是单列模式?
---------保证一个类只有一个实例,并且提供一个访问该全局访问点
问题2:单列的应用场景?
---------数据库连接池
---------任务管理器
---------回收站
---------Web应用的配置对象的读取
---------计数器
---------HttpApplication 也是单位例的典型应用
---------等等…
问题3:单列的优缺点?
优点:
--------- 1.在单例模式中,活动的单例只有一个实例,可以确保所有的对象都访问一个实例
--------- 2.单例模式具有一定的伸缩性,类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。
--------- 3.提供了对唯一实例的受控访问。
--------- 4.由于在系统内存中只存在一个对象,因此可以节约系统资源,避免对共享资源的多重占用
缺点:
--------- 1.不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误
--------- 2.由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
--------- 3.单例类的职责过重,在一定程度上违背了“单一职责原则”。
--------- 4.滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;
--------- 5、如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。
问题4:我该使用什么模式?
--------- 如果不需要延迟加载单例,可以使用枚举或者饿汉式,相对来说枚举性好于饿汉式。
--------- 如果需要延迟加载,可以使用静态内部类或者懒韩式,相对来说静态内部类好于懒韩式。
类初始化时,会立即加载该对象,线程天生安全,调用效率高
优点:
不会被反射入侵
/**
* 饿汉式
*/
public class SingletonDemo01 {
/**
* 类初始化时,会立即加载该对象,线程天生安全,调用效率高
*/
private static SingletonDemo01 singletonDemo01 = new SingletonDemo01();
private SingletonDemo01() {
System.out.println("初始化");
}
/**
* 获取类的实例对象
*/
public static SingletonDemo01 getInstance() {
return singletonDemo01;
}
}
测试调用
public static void main(String[] args) {
SingletonDemo01 s1 = SingletonDemo01.getInstance();
SingletonDemo01 s2 = SingletonDemo01.getInstance();
System.out.println(s1 == s2);
}
类初始化时,不会初始化该对象,真正需要使用的时候才会创建该对象。
优点:
使用时才初始化
/**
* 懒汉式
*/
public class SingletonDemo02 {
/**
* 类初始化时,不会初始化该对象,真正需要使用的时候才会创建该对象。
*/
private static SingletonDemo02 singletonDemo02;
private SingletonDemo02() {
System.out.println("初始化");
}
/**
* 当实例对象为空,创建实例对象,会有线程安全问题,两个线程同一时间执行会重复创建对象
*/
public synchronized static SingletonDemo02 getInstance() {
if (singletonDemo02 == null) {
singletonDemo02 = new SingletonDemo02();
}
return singletonDemo02;
}
}
测试调用
public static void main(String[] args) {
SingletonDemo02 s1 = SingletonDemo02.getInstance();
SingletonDemo02 s2 = SingletonDemo02.getInstance();
System.out.println(s1 == s2);
}
/**
* 使用枚举实现单例模式
* 优点:实现简单、枚举本身就是单例,由jvm从根本上提供保障!避免通过反射和反序列化的漏洞
* 缺点没有延迟加载
*/
public class User {
/**
* 使用/获取枚举创建的实例方法
*/
public static User getInstance() {
return SingletonDemo04.INSTANCE.getInstance();
}
/**
* 使用枚举创建实例
*/
private static enum SingletonDemo04 {
INSTANCE;
// 枚举元素为单例
private User user;
/**
* 创建实例
*/
private SingletonDemo04() {
System.out.println("初始化");
user = new User();
}
/**
* 返回实例
*/
public User getInstance() {
return user;
}
}
}
测试调用
public static void main(String[] args) {
User u1 = User.getInstance();
User u2 = User.getInstance();
System.out.println(u1 == u2);
}
优势:兼顾了懒汉模式的内存优化(使用时才初始化)以及饿汉模式的安全性(不会被反射入侵)。
劣势:需要两个类去做到这一点,虽然不会创建静态内部类的对象,但是其 Class 对象还是会被创建,而且是属于永久带的对象
/**
* 静态内部类方式
*/
public class SingletonDemo03 {
private SingletonDemo03() {
System.out.println("初始化..");
}
/**
* 创建实例
*/
public static class SingletonClassInstance {
private static final SingletonDemo03 singletonDemo03 = new SingletonDemo03();
}
/**
* 调用返回实例
*/
public static SingletonDemo03 getInstance() {
return SingletonClassInstance.singletonDemo03;
}
}
测试调用
public static void main(String[] args) {
SingletonDemo03 s1 = SingletonDemo03.getInstance();
SingletonDemo03 s2 = SingletonDemo03.getInstance();
System.out.println(s1 == s2);
}
就是在懒汉式的基础上在加一把锁,处理线程安全问题
public class SingletonDemo04 {
private SingletonDemo04 singletonDemo04;
private SingletonDemo04() {
}
public SingletonDemo04 getInstance() {
if (singletonDemo04 == null) {
//锁
synchronized (this) {
if (singletonDemo04 == null) {
singletonDemo04 = new SingletonDemo04();
}
}
}
return singletonDemo04;
}
}
在构造函数中,只能允许初始化化一次即可
private static boolean flag = false;
private SingletonDemo04() {
if (flag == false) {
flag = !flag;
} else {
throw new RuntimeException("单例模式被侵犯!");
}
}
public static void main(String[] args) {
}
说明:
相当于是一个工厂中有各种产品,创建在一个类中,客户无需知道具体产品的名称,只需要知道产品类所对应的参数即可。但是工厂的职责过重,而且当类型过多时不利于系统的扩展维护。
创建示例流程:
1、创建业务接口类,编写一个接口方法
2、创建N个接口实现类,并实现接口方法
3、创建Factory 工厂类,并根据不同的参数判断,来判断调用接口实现方法
4、消费测试
说明:
工厂方法模式Factory Method,又称多态性工厂模式。在工厂方法模式中,核心的工厂类不再负责所有的产品的创建,而是将具体创建的工作交给子类去做。该核心类成为一个抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。
创建示例流程:
1、创建业务接口,添加业务逻辑方法接口
2、创建实现业务接口的方法类,(图中示例为3个)
3、创建工厂接口,添加接口方法,返回值为业务接口类名
4、创建实现工厂接口的方法类,返回值为每个工厂对应的接口实现类名 , (图中示例为3个)
5、消费者创建根据需求创建工厂实例,调用具体工厂接口实现类的方法获得返回的具体工厂实例
6、调用业务实现接口
---- ,
每个工厂下的业务逻辑实现类可以和简单工厂一样有无数个,a品种苹果,b品种苹果,c…
说明:
抽象工厂简单地说是工厂的工厂,抽象工厂可以创建具体工厂,由具体工厂来产生具体产品。
创建示例流程:
1、创建业务逻辑的通用接口抽象类,并创建抽象方法,1、生产发动机,2、生产座椅
2、创建产品族a 和 b 的抽象类 --> 继承通用接口抽象类
3、创建产品族a 和 b 的实现类,并按需实现抽象类接口 (如图,暂时可以定义2个)
4、创建抽象工厂,并创建抽象方法,1、生产发动机,2、生产座椅(方法返回值=产品族接口名)
5、创建具体工厂a 和 b 继承抽象工厂,并返回具体的工厂实例对象( return new 产品族具体实现类())
6、根据需求调用获取工厂对象,通过工厂对象调用具体的实现类
-------- 如:
我现在需要生产一辆车,现在有两个厂家的零件
我需要a 厂商的发动机和 b 厂商的座椅
1.通过工厂直接获得a 厂商的发动机实例对象 ( new 生产发动机a() )
2.通过工厂直接获得b 厂商的座椅实例对象 ( new 生产座椅b() )
3.调用抽象接口业务逻辑的具体实现就ok了
说明:
建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性
如:
1、去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"。
2、JAVA 中的 StringBuilder。
应用场景:
1、需要生成的对象具有复杂的内部结构。
2、需要生成的对象内部属性本身相互依赖。
与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。
创建示例流程:
这里以游戏开发中人物的构造过程为例。在游戏中创建一个形象时,需要对每个部位进行创建。简化而言,需要创建头部,身体和腿部。
1、创建角色类 Role
2、创建建造者抽象接口,提供如图提供4个方法
3、创建具体建造者
4、创建指挥官
------- 1、在客户端 new 指挥官对象的时候需创建建造者对象( 默认构造器中new 建造者抽象接口())
------- 2、创建-建造拼装各身体部位任务方法
5、客户端创建建造者对象传入指挥官对象中,指挥官类中初始化建造者对象( 4.1), 并执行拼装任务
说明:
原型模式是一个创建型的模式。原型二字表明了改模式应该有一个样板实例,用户从这个样板对象中复制一个内部属性一致的对象,这个过程也就是我们称的克隆。被复制的实例就是我们所称的“原型”,这个原型是可定制的。原型模式多用于创建复杂的或者构造耗时的实例,因为这种情况下,复制一个已经存在的实例可使程序运行更高效。
应用场景:
(1)类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等,通过原型拷贝避免这些消耗。
(2)通过new产生的一个对象需要非常繁琐的数据准备或者权限,这时可以使用原型模式(克隆)。
(3)一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用,即保护性拷贝。
spring的克隆:
Spring框架中的多例就是使用原型。
Spring提供的 BeanUtils.copyProperties(a,b) 克隆方法是浅拷贝,a拷贝到b ,更新克隆数据会破坏持久层数据,而且是采用反射机制实现的,对程序的效率也会有影响,因此一定要谨慎使用,可以使用json 方式来深拷贝
说明:
类似于 个人 --> 中介 --> 房东
个人所有需求都需要通过中介,谈好了直接中介通知房东,房东ok了直接和中介签合同就完了
应用场景:
– 1、事务
– 2、spring 环绕通知
– 3、spring 切点切面
– 4、日志打印等等
说明:
每个类都需要添加一个代理类,每个方法也需要单独处理,不现实
实现流程:
1、代理类继承和实现类一样的接口
2、代理类调用实现类的接口方法,并在调用方法前后做处理
如图:
创建代理类 xxxProxy,消费者只能调用xxxProxy,不能直接调用实现类
说明:
1.代理对象,不需要实现接口
2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
3.动态代理也叫做:JDK代理,接口代理
原理:
是根据类加载器和接口创建代理类(此代理类是接口的实现类,所以必须使用接口 面向接口生成代理,位于java.lang.reflect包下)
代码示例:
/**
* 每次生成动态代理类对象时,实现了InvocationHandler接口的调用处理器对
*/
public class InvocationHandlerImpl implements InvocationHandler {
// 这其实业务实现类对象,用来调用具体的业务方法
private Object target;
// 通过构造函数传入目标对象
public InvocationHandlerImpl(Object target) {
this.target = target;
}
/**
* 处理调用方法前后任务
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
System.out.println("调用开始处理");
// 执行调用的方法
result = method.invoke(target, args);
System.out.println("调用结束处理");
return result;
}
}
使用:
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException,
IllegalAccessException, IllegalArgumentException, InvocationTargetException {
// 被代理的对象
IUserDao userDao = new UserDao();
InvocationHandlerImpl invocationHandlerImpl = new InvocationHandlerImpl(userDao);
// 获取类加载器
ClassLoader loader = userDao.getClass().getClassLoader();
// 获取接口
Class>[] interfaces = userDao.getClass().getInterfaces();
// 主要装载器、一组接口及调用处理动态代理实例
IUserDao newProxyInstance = (IUserDao) Proxy.newProxyInstance(loader, interfaces, invocationHandlerImpl);
// 调用代理类的 save方法
newProxyInstance.save();
}
说明:
使用cglib[Code Generation Library]实现动态代理,并不要求委托类必须实现接口,底层采用asm字节码生成框架生成代理类的字节码
pom.xml
cglib
cglib
3.3.0
代码示例
public class CglibProxy implements MethodInterceptor {
private Object targetObject;
/**
* 这里的目标类型为Object,则可以接受任意一种参数作为被代理类,实现了动态代理
*/
public Object getInstance(Object target) {
// 设置需要创建子类的类
this.targetObject = target;
Enhancer enhancer = new Enhancer();
//创建虚拟子类
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
/**
* 处理调用方法前后任务
*/
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("开启事物");
Object result = proxy.invoke(targetObject, args);
System.out.println("关闭事物");
// 返回代理对象
return result;
}
}
使用:
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
UserDao userDao = (UserDao) cglibProxy.getInstance(new UserDao());
userDao.save();
}
说明:
在设计模式中,适配器模式(英语:adapter pattern)有时候也称包装样式或者包装(wrapper)。将一个类的接口转接成用户所期待的。一个适配使得因接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中。
适配器分类:
类适配器、对象适配、接口适配方式
类适配器方式采用继承方式,对象适配方式使用构造函数传递
应用场景:
1、OutputStreamWriter:是Writer的子类,将输出的字符流变为字节流,即:将一个字符流的输出对象变为字节流的输出对象。
2、InputStreamReader:是Reader的子类,将输入的字节流变为字符流,即:将一个字节流的输入对象变为字符流的输入对象。
3、SpringMVC 适配器
创建示例流程:
1、创建安卓,苹果充电接口
2、创建impl 实现安卓,苹果充电接口
3、创建适配器类,继承苹果充电接口,并添加构造器,传入安卓充电接口对象使其支持安卓充电口(obj = new 安卓充电接口())
4、添加手机充电类,添加充电方法
---- 4.1、使用安卓充电口给苹果手机充电,安卓充电接口 obj = new 适配器(new 安卓充电实现())
---- 4.2、使用安卓充电口给安卓手机充电,安卓充电接口 obj = new 安卓充电实现()
---- 4.3、使用苹果充电口给苹果手机充电,苹果充电接口 obj = new 苹果充电实现()
说明介绍:
组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。
意图:将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
主要解决:
它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
优点:
1、高层模块调用简单。
2、节点自由增加。
缺点:
在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。
使用场景:
1、树形菜单
2、文件、文件夹的管理
说明:
桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。
主要解决:
在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活。
优点:
1、抽象和实现的分离。
2、优秀的扩展能力。
3、实现细节对客户透明。
缺点:
桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
应用场景:
1、如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。
2、对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。
3、一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
说明:
隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。
这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性。
这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用。
创建示例流程:
这个太简单,就不写了
就是控制住只调用一个方法,就可以完成所有的操作,类似于封装
意图:
动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
主要解决:
一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。
优点:
装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
缺点:
多层装饰比较复杂。
使用场景:
1、扩展一个类的功能。
2、动态增加功能,动态撤销。
主要解决:
1、在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。
如何解决:
用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象。
应用实例:
1、JAVA 中的 String,如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面。
2、数据库的数据池。
优点:
1、大大减少对象的创建,降低系统的内存,使效率提高。
2、对象复用
缺点:
提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。
使用场景:
1、系统有大量相似对象。
2、需要缓冲池的场景。
3、如图大转盘抽奖活动
注意事项:
1、注意划分外部状态和内部状态,否则可能会引起线程安全问题。
2、这些类必须有一个工厂对象加以控制。
说明:
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
使用场景:
1、一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
2、一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
3、一个对象必须通知其他对象,而并不知道这些对象是谁。
4、需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制
注意点
1、JAVA 中已经有了对观察者模式的支持类。
2、避免循环引用。
3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
如下:
实现了修改/获取对象类的属性,自动触发a,b,c 实现方法,也可以不通过set,get,定义任意方法触发,类似于事件监听
说明:
提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。
应用实例:
JAVA 中的 iterator。
使用场景:
1、访问一个聚合对象的内容而无须暴露它的内部表示。
2、需要为聚合对象提供多种遍历方式。
3、为遍历不同的聚合结构提供一个统一的接口。
说明:
在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
主要解决:
if…else 所带来的复杂和难以维护。
应用场景:
1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
2、一个系统需要动态地在几种算法中选择一种。
3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
应用实例:
1、诸葛亮的锦囊妙计,每一个锦囊就是一个策略。
2、旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。
3、JAVA AWT 中的 LayoutManager。
优点:
1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
缺点:
1、策略类会增多。 2、所有策略类都需要对外暴露。
说明:
命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
使用场景:
认为是命令的地方都可以使用命令模式,比如: 1、GUI 中每一个按钮都是一条命令。 2、模拟 CMD。
优点:
1、降低了系统耦合度。
2、新的命令可以很容易添加到系统中去。
缺点:
使用命令模式可能会导致某些系统有过多的具体命令类。
如下图示例:
有一个烧烤师傅,可以烤牛肉串和羊肉串,那么比喻为 a命令和 b命令
有一个客人
想吃牛肉串,就把烤牛肉串添加到命令集并下达命令给烧烤师傅,
想吃羊肉串,就把烤羊肉串添加到命令集并下达命令给烧烤师傅,
俩都想吃,就烤牛肉+烤羊肉添加到命令集并下达命令给烧烤师傅
并可以在命令调用类中添加日志记录等
说明:
在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。
在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。
优点:
1、封装了转换规则。
2、枚举可能的状态,在枚举状态之前需要确定状态种类。
3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
缺点:
1、状态模式的使用必然会增加系统类和对象的个数。
2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
3、状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
使用场景:
1、行为随状态改变而改变的场景。
2、条件、分支语句的代替者。
注意事项:
在行为受状态约束的时候使用状态模式,而且状态不超过 5 个。
说明:
处理某个流程的代码已经都具备,但是其中某个节点的代码暂时不能确定。因此,我们采用模板方法模式,将这个节点的代码实现转移给子类完成。即:处理步骤在父类中定义好,具体的实现延迟到子类中定义。
说白了,就是将一些相同操作的代码,封装成一个算法的骨架。核心的部分留在子类中操作,在父类中只把那些骨架做好。
应用场景:
1、数据库访问的封装
2、Junit单元测试
3、servlet中关于doGet/doPost方法的调用
4、Hibernate中模板程序
5、spring中JDBCTemplate,
6、HibernateTemplate等等
创建示例流程:
1、创建模板方法抽象类,并编写如图,1,2,3,4方法,其中1,3,4为具体方法,并在抽象类实现,2为抽象方法,由子类实现
2、创建子类,继承模板方法抽象类,并根据不同的对接方式实现不同的对接流程
3、业务层创建对象,new 具体实现类,抽象类对象接收,获得具体实现的对象
4、依次调用1,2,3,4方法,3,4,步骤都是在不同业务逻辑的后方,可以在抽象层在定义一个方法5,直接调用3,4 方法,依次调用1,2,5方法也是一样的
说明:
在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。
意图:
主要将数据结构与数据操作分离。
主要解决:
稳定的数据结构和易变的操作耦合问题。
何时使用:
需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,使用访问者模式将这些封装到类中。
如何解决:
在被访问的类里面加一个对外提供接待访问者的接口。
关键代码:
在数据基础类里面有一个方法接受访问者,将自身引用传入访问者。
应用实例:
您在朋友家做客,您是访问者,朋友接受您的访问,您通过朋友的描述,然后对朋友的描述做出一个判断,这就是访问者模式。
优点:
1、符合单一职责原则。
2、优秀的扩展性。
3、灵活性。
缺点:
1、具体元素对访问者公布细节,违反了迪米特原则。
2、具体元素变更比较困难。
3、违反了依赖倒置原则,依赖了具体类,没有依赖抽象。
使用场景:
1、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类。
注意事项:
访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器。
如下示例:
访问者:cto / ceo
员工职位:程序员,测试员
目的:
cto,查看程序员代码数量 和 测试员bug 数量
ceo 查看程序员功能模块是否编写完成 和 测试员是否测试完功能模块
说明:
中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。中介者模式属于行为型模式。
何时使用:
多个类相互耦合,形成了网状结构。
应用实例:
1、中国加入 WTO 之前是各个国家相互贸易,结构复杂,现在是各个国家通过 WTO 来互相贸易。 2、机场调度系统。
3、MVC 框架,其中C(控制器)就是 M(模型)和 V(视图)的中介者。
优点:
1、降低了类的复杂度,将一对多转化成了一对一。
2、各个类之间的解耦。
3、符合迪米特原则。
**缺点:**中介者会庞大,变得复杂难以维护。
非中介者模式设计
使用了中介者模式
如下图
实现一个智能家居场景
打开任意一个设备,联动打开其他所有关联设备
关闭任意一个设备,联动关闭其他所有关联设备
说明:
备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。
意图:
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
使用场景:
1、浏览器回退:浏览器一般有浏览记录,当我们在一个网页上点击几次链接之后,可在左上角点击左箭头回退到上一次的页面,然后也可以点击右箭头重新回到当前页面
2、数据库备份与还原:一般的数据库都支持备份与还原操作,备份即将当前已有的数据或者记录保留,还原即将已经保留的数据恢复到对应的表中
3、编辑器撤销与重做:在编辑器上编辑文字,写错时可以按快捷键 Ctrl + z 撤销,撤销后可以按 Ctrl + y 重做
4、虚拟机生成快照与恢复:虚拟机可以生成一个快照,当虚拟机发生错误时可以恢复到快照的样子
5、Git版本管理:Git是最常见的版本管理软件,每提交一个新版本,实际上Git就会把它们自动串成一条时间线,每个版本都有一个版本号,使用 git reset --hard 版本号 即可回到指定的版本,让代码时空穿梭回到过去某个历史时刻
6、棋牌游戏悔棋:在棋牌游戏中,有时下快了可以悔棋,回退到上一步重新下
如下图:
实现棋牌游戏悔棋
说明:
避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
示例:
公司采购安排的项目:公司内部要采购物品的话,不同的价格则需要不同阶层的领导签字,决策人有组长、部长、副总、总裁
使用传统的方法可是用switch语句将不同的价格区分不同的决策人,但是这样所有的逻辑都在一个处理类中较复杂,且如果中间增加或减少了决策人拓展不便
为了实现对象之间更好的解耦,则引入责任链模式,将请求从低到高一层一层的往下传,如果本人执行不了,就传递给下一层执行,直到有对象可以处理为止
应用场景:
mybatis
servlet的 Filter
dubbo
安全框架诸如Spring security、apache shiro
erp 系统进消销存
说明:
解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。
关键代码:
构建环境类,包含解释器之外的一些全局信息,一般是 HashMap。
优点:
1、可扩展性比较好,灵活。
2、增加了新的解释表达式的方式。
3、易于实现简单文法。
缺点:
1、可利用场景比较少。
2、对于复杂的文法比较难维护。
3、解释器模式会引起类膨胀。
4、解释器模式采用递归调用方法。
使用场景:
1、可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
2、一些重复出现的问题可以用一种简单的语言来进行表达。
3、一个简单语法需要解释的场景。
注意事项:
可利用场景比较少,JAVA 中如果碰到可以用 expression4J 代替。
说明:
根据不同的过滤规则,过滤掉某一个集合对象里面的某些数据,包括其不限于List,map,数组,等
过滤器模式(Filter Pattern)或标准模式(Criteria Pattern)是一种设计模式,
这种模式允许开发人员使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来。
这种类型的设计模式属于结构型模式,它结合多个标准来获得单一标准。