FACADE模式
门面模式是对象的结构模式,外部与一个子系统的通信必须通过一个统一的门面对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。——-《JAVA与模式》
优点:
- 屏蔽细节
- 客户端调用优雅
- 保护某些不希望被客户端访问的public方法
1 | Before: |
Ps: HttpServlet中的方法doGet/doPost入参为HttpServletRequest和HttpServletResponse,这两个参数是从tomcat中传递而来,实际上是对原始的request和response进行了一层封装,也就是facade模式,之后才传递到servlet中,,目的就是屏蔽request和response中某些不希望被外部访问的方法
模板方法模式
常用于解决某些通用的部分中嵌有特异的部分
通用的抽象,特异的定义抽象方法由各个子类实现1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40Before:
class Luyu{
public void visitZoo() {
buyTicket();
seeLion();
seeHawk();
goHome();
}
}
class HH{
public void visitZoo() {
buyTicket();
seeCat();
seeDolphin();
goHome();
}
}
After:
abstract class Visitor{
abstract void seeMyFavorateAnimals();
public void visitZoo() {
buyTicket();
seeMyFavorateAnimals();
goHome();
}
}
class Luyu extends Visitor {
public void seeMyFavorateAnimals() {
seeLion();
seeHawk();
}
}
class HH extends Visitor {
public void seeMyFavorateAnimals() {
seeCat();
seeDolphin();
}
}
Ps 在spring中,对于抽象类的bean定义需要设置其abstract=”true”
单例模式
参考文章:https://www.cnblogs.com/java-my-life/archive/2012/03/31/2425631.html
饿汉模式1
2
3
4
5
6
7public class EagerSingleton {
private static EagerSingleton instance = new EagerSingleton();
private EagerSingleton(){}
public static EagerSingleton getInstance() {
return instance;
}
}
EagerSingleton类被加载的时候,会初始化静态变量,由JVM保证线程安全
优点:简单无须同步
缺点:不需要的时候也实例化,浪费空间
Ps:说点题外话,这里说是饿汉其实也没有那么饿,其实真正new实例是在触发初始化的时候,而不是一上来就new
类的生命周期:加载、验证、准备、解析、初始化、使用和卸载
其中在准备阶段为类变量分配内存并设置类变量初始值,而初始化阶段才会进行类变量的赋值操作,常见的触发初始化的条件是new、get/put static fields
当多个线程触发该类初始化时,由JVM保证类初始化的线程安全
懒汉模式1
2
3
4
5
6
7
8
9
10public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton(){}
public static synchronized LazySingleton getInstance(){
if(instance == null){
instance = new LazySingleton();
}
return instance;
}
}
优点:在需要的时候才去实例化且线程安全
缺点:每次调用getInstance方法获取实例时,都需要同步性能较差
双重检查加锁1
2
3
4
5
6
7
8
9
10
11
12
13
14public class Singleton {
private volatile static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null){
synchronized (Singleton.class) {
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
很经典的一个关于volatile关键字使用的例子,如果去掉该关键字,那么getInstance方法变为线程不安全
很可能Thread1在执行instance = new Singleton()的时候,发生了指令重排,真正的实例化尚未完成,但是instance已经不为null
这时候TThread2在第一重检查的时候通过了,直接返回了一个中间态的实例给到调用方,这是我们不能接受的。
优点:不需要每次都同步且线程安全
缺点:使用了volatile关键字使得虚拟机的一些优化手段被禁用,可能会对性能有影响
静态内部类模式1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public class Singleton {
private Singleton(){}
/**
* 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
* 没有绑定关系,而且只有被调用到时才会装载,从而实现了延迟加载。
*/
private static class SingletonHolder{
/**
* 静态初始化器,由JVM来保证线程安全
*/
private static Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}
优点:懒汉模式且由JVM保证保证线程安全
缺点:引入了静态内部类,代码稍显复杂
1)上述方法都存在一个问题,如果客户端通过反射使得私有构造函数变为可访问,之后实例化,这个时候会产生多个”单例”
此时一个方法是在构造函数中,对于创建的第二个实例抛出异常(AtomInteger可以保证)
2)单例在序列化的时候相对复杂,首先实现Serializable接口,每个域都要置为transient,还需要提供一个readResolve方法
枚举模式1
2
3
4
5Enum Singleton {
INSTANCE;
//类的方法
public void myMethod(){}
}
简洁优雅,有效抵挡反射、序列化攻击
代理模式-静态VS动态
优雅地修改某个类中的既有方法且不修改原有代码
静态代理
PART11
2
3interface ISubject {
void request();
}
1 | class SubjectImpl implements ISubject { |
1 | class SubjectProxy implements ISubject { |
上面就是一个标准的代理模式,但是这样可能会有一些问题,如果系统内还有其他接口也有request方法,而我们也想对其代理相同的逻辑,这时候需要再去创建一个代理类
换句话说有多少个接口,就需要创建多少个代理类,那这样我们的系统就显得过于臃肿了,这时候我们引出动态代理
动态代理模式
PART1 & PART2同上,被代理的对象一定要实现接口1
2
3
4
5
6
7
8
9
10
11
12
13
14class RequestInvocationHandler implements InvocationHandler {
private Object target;
public RequestInvocationHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy,Method method,Object[] args) thorws Throwable {
//只对request方法进行代理
if(method.getName.equals("request")){
System.out.print("before origin request");
method.invoke(obj,args);
System.out.print("after origin request");
}
}
}
这样上面这个handler就具有一定的通用性了,可以供所有接口使用。1
2
3
4ISubject subject = new SubjectImpl();
RequestInvocationHandler handler = new RequestInvocationHandler(subject);
ISubject proxy = (ISubject)Proxy.newProxyInstance(subject.getClass().getClassLoader(),subject.getClass().getInterfaces(),handler);
proxy.request();
使用动态代理最大的问题就是只能对实现了接口的类进行代理,使用广度有些受限
CGLIB
动态字节码:对目标对象进行继承扩展,为其生成相应的子类,而子类可以通过覆写来扩展父类的行为,只要将横切逻辑的实现放到子类中,然后让系统使用扩展后的目标对象的子类,就可以达到与代理模式相同的效果。1
2
3
4
5
6
7
8
9
10
11class RequestCallback implements MethodInterceptor {
public Obeject intercept(Obeject object, Method method, Object[] args, MethodProxy proxy) {
//只对request方法进行代理
if(method.getName.equals("request")){
System.out.print("before origin request");
method.invoke(obj,args);
System.out.print("after origin request");
}
return null;
}
}
使用方式1
2
3
4
5Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(SubjectImpl.class);
enhancer.setCallback(new RequestCallback());
SubjectImpl proxy = (SubjectImpl)enhancer.create();
proxy.request();
使用CGLIB对类进行扩展的唯一限制就是无法对final方法进行覆写
责任链模式-管道模型
多条流水线,相似度较高,但仍有部分不同的模块或者流水线的顺序不同
before:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class processLine0 {
public void process(){
module0();
module1();
module3();
}
}
class processLine1 {
public void process(){
module1();
module5();
module3();
module0();
}
}
after:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56//框架层面代码
//阀接口
public interface Valve {
public void invoke(UniParam uni);
}
//管道接口
public interface Pipeline {
public void addValve(Valve valve);
public void doInvoke(UniParam uni);
}
public class MyPipeline implements PipeLine {
private List<Valve> valves = Lists.newArrayList();
private Valve getFirst(){
Collections.isEmpty(valves) ? throw Exception : return valves.get(0);
}
public void addValve(Valve valve){
valves.add(valve);
}
public void doInvoke(UniParam uni){
int cur = 0;
while(cur <= valves.size()){
valves.get(cur++).invoke(uni);
}
}
}
//业务层面 自己实现
//X=0,1,3,5
public class ModuleX implements Valve {
public void invoke(UniParam uni){
//module自己的逻辑
doMyThing(uni);
}
}
class processLine0 {
public void process(){
PipeLine pl = new PipeLine();
Uniparam uni = new Uniparam();
pl.add(new Module0());
pl.add(new Module1());
pl.add(new Module3());
pl.doInvoke(uni);
}
}
class processLine1 {
public void process(){
PipeLine pl = new PipeLine();
pl.add(new Module1());
pl.add(new Module5());
pl.add(new Module3());
pl.add(new Module0());
pl.doInvoke(uni);
}
}
是不是灵活了许多
缺点是valve的invoke方法必须要是同一个模板(入参返回值相同)
策略模式
1 | interface Calculator { |
Spring中的bean的实例化过程采用的就是策略模式(reflect or CGLIB),默认是CGLIB