几种常用设计模式
一、创建型模式(在创建对象的过程中尽量隐藏创建细节,不直接使用new)
1、单例模式(Singleton Pattern)
核心作用:
保证一个类只有一个实例,并且提供一个访问该实例的全局访问点,这个类,他虽然有构造方法,但是把它设定为private,不能被外界使用。这个类只存在一个实例,保存在这个类自己的一个静态字段里,如果要用这个类的实例属性、实例方法,都通过这个静态字段访问这个唯一的实例来实现。
常用场景:
Windows系统的任务管理器(只能打开一个)、回收站、项目中读取配置文件的类、日志应用、数据库连接池、操作系统的文件系统、Spring中的每个bean默认是单例的便于SpringIOC容器管理、每个Servlet、SpringMVC控制器对象
优点:
1、只生成一个实例,减少了系统开销,当一个对象的产生需要较多资源时,如读取配置文件、产生其他依赖对象时,则可以在应用启动时创建一个单例对象,然后永久驻留内存;
2、单例模式可以在系统设置全局的访问点,优化共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理
常见的实现方式:
①饿汉式:(线程安全、调用效率高,不能延时加载:用不用都会在类初始化时立即加载,因此一个缺点就是可能加载了没用到,那就浪费资源了)
class Singleton { //天生线程安全:类加载时已经创建对象而且只创建一次,因此线程安全
private static 【final】 Singleton instance = new Singleton(); //类初始化时,立即加载这个对象,
private Singleton () { } //私有化构造器
public static Singleton getInstance () { //定义一个方法返回这个实例,使用这个单例时就可以通过调用该方法获得对象:Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance();
return instance;
}
}
②懒汉式:(线程安全、调用效率不高(加锁了synchronized ),可以延时加载:随用随加载)
class Singleton { //线程不安全,需要加锁,因为如果A调用getInstance ()时刚进去就被挂起,随后B也进去,两者都判断instance为null,这样就会创建两次实例对象,违背了单例模式
private static Singleton instance = null; //初始化为空,延时加载该对象,
private Singleton () { } //私有化构造器
public static synchronized Singleton getInstance () { //调用该方法时先判断是否有该实例对象,没有才创建
if (instance == null ) {
instance = new Singleton ();
}
return instance;
}
}
其他(了解):
③双重检测锁式: (由于JVM底层内部模型问题,偶尔会出问题不建议使用)
④静态内部类式: (线程安全、调用效率高,可以延时加载)
⑤枚举单例: (线程安全、调用效率高,不能延时加载)
2、工厂模式:
核心本质:
实例化对象,将对象的创建即new操作交给工厂方法来;将选择实现类、创建对象统一管理和控制,从而实现调用者跟实现类解耦
应用场景:
Spring中IOC容器创建管理Bean对象、Hibernate中SessionFactory创建Session
①简单工厂模式(Factory Pattern):
也叫静态工厂模式,用来生产同一等级结构中的任意产品(对象)。(对于增加新的类,需要修改已有代码)
实现过程:正常的是我们直接new我们需要的对象,但这样耦合度太高(如果需要new很多类对象会导致关联的类太多),因此我们可以将对象的创建交给一个工厂,他里面有方法可以new对象,我们只需要调用该方法传参数(对象类名)即可,只需要关注这个工厂即可
简单工厂一如下:
public class CarFactory{ //该工厂用来返回“奥迪”、“奔驰”对象(都实现了Car接口),增加其他车的对象,需要修改代码
public static Car createCar (String type){
Car c = null;
if("奥迪".equals(type)){
c = new Audi ();
}else if("奔驰".equals(type)){
c = new BenChi ();
}
return c;
}
}
简单工厂二如下:
public class CarFactory{ //该工厂用来返回“奥迪”、“奔驰”对象(都实现了Car接口),增加其他车的对象,需要修改代码
public static Car createAuDi () {
return new Audi ();
}
public static Car createBenChi () {
return = new BenChi ();
}
}
②工厂方法模式:
增加工厂类;写一个工厂接口(里面有createCar方法),为每一个Car类(AuDi)创建一个对应的工厂类(AuDiFactory)去实现这个工厂接口,创建对象方式:Car c1 = new AuDiFactory().createCar(); 当需要扩增时,直接添加一个对应的Car的CarFactory工厂类即可,不修改源码
③抽象工厂模式(Abstract Factory Pattern):
工厂方法模式升级版(产品种类更多),用来生产不同产品族的全部产品。(对于增加新的产品,无能为力;支持增加产品族);在有多个业务品种、业务分类时,该模式产生需要的对象非常好
过程描述:
定义一个CarFactory(接口)——>两个实现类:LowCarFactory/HighCarFactory ;里面都有两个方法:CreateEngine()/CreateTire()生产发动机和轮胎,这两个产品组成一个产品族,即构成一辆完整汽车
定义Tire(方法:revolve();)和Engine(方法:run();start();)接口——>分别两个实现类LowTire()&HighTire()// LowEngine()&HighEngine(),
客户(Client)生产一辆汽车:低端汽车:低端轮胎+低端发动机 高端:高端轮胎+高端发动机 中端:低+高
高端汽车: CarFactory factory = new HighCarFactory(); //接口(汽车工厂)引用指向实现类对象(高端汽车工厂被创建)
Engine e = factory.createEngine(); //调用实现类对象中创建Engine对象(该对象是Engine接口的实现类对象LowEngine)的方法
e.run() ; //执行方法:跑的飞快
e.start() ; //执行方法:一秒启动
3、代理模式:Spring两大核心之一AOP的核心实现机制
核心作用:
通过代理,控制对对象的访问,可以详细控制访问某个(某类)对象的方法,在调用这个方法前做前置处理,调用这个方法后作后置处理
应用场景:AOP:
前置通知(before 作用:用于配置前置通知。指定增强的方法在切入点方法之前执行)、
后置通知(after-returning 切入点方法正常执行之后。它和异常通知只能有一个执行)
异常通知(after-throwing 切入点方法执行产生异常后执行。它和后置通知只能执行一个)
最终通知(after 说明:无论切入点方法执行时是否有异常,它都会在其后面执行。)
环绕通知(around 它是spring框架为我们提供的一种可以在代码中手动控制增强代码什么时候执行的方式。注意:通常情况下,环绕通知都是独立使用的)
①静态代理(静态定义代理类):
定义一个抽象角色(接口): 定义代理角色和真实角色的公共对外方法
代理角色(代理类:): 实现抽象角色(接口),代理真实角色,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作
真实角色(供代理角色调用): 实现抽象角色(接口),定义真实角色要实现的业务逻辑,供代理角色调用,真正的业务逻辑所在处
②动态代理(动态生成代理类):
抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样可以更加灵活和统一的处理众多方法
JDK自带的动态代理:
java.lang.reflect.Proxy 作用:动态生成代理类和对象
java.lang.reflect.InvocationHandler(处理器接口)
用法:创建一个代理类实现该接口,覆盖invoke方法,然后用户Client类用代理类对象调用某一个方法时就会执行真实角色的这个方法:proxy.sing ();
class Handler implements InvocationHandler {
@Override
public Object invoke(Object proxy , Method method , Object [ ] args) {
System.out.println(“此处编写真正业务执行前 要控制的代码”); //灵活和统一的处理众多抽象角色中(接口)声明的方法
System.out.println(“签合同”);
System.out.println(“订机票”);
method.invoke (真实角色 , args);//“唱歌” //真正业务要执行的方法:唱歌
System.out.println(“此处编写真正业务执行后 要控制的代码”); //灵活和统一的处理众多方法
System.out.println(“收钱”);
return null;
}
}
可以通过invoke(调用)方法实现对真实角色的代理访问
每次通过Proxy生成代理类对象时都要指定对应的处理器对象
4、观察者模式:
类如广播机制或者CF游戏:当一个对象(目标对象)状态变化时,及时告知一系列观察对象(观察者对象Observer),令他们及时作出响应,比如射击游戏中A玩家移动了,那么这个信息会先传到服务器,再告知其他玩家并显示出来移动了