单例模式
单例模式是设计模式中最简单的一种设计模式,常用于数据库连接池、Spring默认的bean等
单例模式是一种创建型模型,这个词我们后面还会提到。
一个类负责创建自己的对象,但是需要确保只有一个对象被创建了,这个类提供了一种访问它对象的唯一方式。同时需要隐藏自己的构造方法。
一般单例模式都是支持两种实现思路,一种是饿汉式,一种是懒汉式,饿汉式就是在类加载时就实例化一个对象,而懒汉式则是需要第一次使用的时候才实例化对象,但是懒汉式的实现方式一般需要通过双重校验也就是double cheack的方式来实现。
饿汉模式
饿汉式的实现是比较简单的,也就是在类加载的时候就实例化对象,但是存在一个问题,这个类加载了但是我没有使用,会有额外的空间浪费。
1 2 3 4 5 6 7 8
| public class SingletonHungry { private static final SingletonHungry singletonHungry = new SingletonHungry(); private SingletonHungry(){} public static SingletonHungry getSingletonHungry(){ return singletonHungry; } }
|
懒汉模式
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
| public class Singleton {
private static volatile Singleton singleton;
private Singleton(){}
public static Singleton getSingleton(){ if(singleton==null){
synchronized (Singleton.class){ if (singleton==null){ singleton = new Singleton(); } } } return singleton; }
public static void main(String[] args) { Singleton singleton1 = Singleton.getSingleton(); Singleton singleton2 = Singleton.getSingleton(); System.out.println(singleton1==singleton2); } }
|
代理模式
代理模式的核心是,允许通过代理对象控制对被代理对象的访问。主要用于在访问对象时添加额外的功能。
代理模式在Java中可以分为静态代理和动态代理,本质上的目的都是一致的。
只不过静态代理需要手动编写代理类,代理类需要实现与目标相同的接口,在内部需要持有一个目标对象的引用。
动态代理则是运行时动态生成的,不需要为每个对象手动编写代理类,但是目标对象至少必须实现一个接口。
接下来我们就来尝试以对方法加log的任务为例,实现两种代理。
共用代码
两种代码都需要有一个接口和对接口的实现,所以在这里定义一个接口和并给出它的一个实现。
其实写到动态代理那块的时候,我才意识到这个Method接口的取名并不太妙,因为Java反射包里就有一个类叫Method,但是事已至此,实在是懒得改了,就先这样。
1 2 3 4 5
| package Proxy;
public interface Method { public void method(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package Proxy;
import java.util.concurrent.TimeUnit;
public class MethodImpl implements Method{ @Override public void method() { System.out.println("Dealing with method"); try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("Finished dealing"); } }
|
静态带来
这时候我们就实现一个方法,来代理这个MethodImpl,目的是要给它加上执行时间的log,当然为了演示简单起见,我们直接打印在控制台上了
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
| package Proxy;
import java.util.Date;
public class StaticProxy implements Method{ private Method target;
public StaticProxy(Method target){ this.target = target; }
@Override public void method() { Date dateStart = new Date(); target.method(); Date dateFinished = new Date(); long timeCost = dateFinished.getTime()-dateStart.getTime(); System.out.println("Cost time:"+timeCost); }
public static void main(String[] args) { Method staticProxy = new StaticProxy(new MethodImpl()); staticProxy.method(); } }
|
静态代理其实存在一个问题,灵活性较低,因为每当需要为一个新的服务类型提供代理时,都需要手动去编写相应的代理类。
但是Java为我们提供了动态代理类的实现方式。
动态代理
动态代理的实现其实是依赖Java反射包下的Proxy
类的newProxyInstance()
方法,需要实现一个InvocationHandler
接口,并重写invoke
方法
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
| package Proxy;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.util.Date;
public class MyDynamicProxy {
static class MyDynamicProxyHandler implements InvocationHandler{ Object target; public MyDynamicProxyHandler(Object target){ this.target = target; } @Override public Object invoke(Object proxy, java.lang.reflect.Method method, Object[] args) throws Throwable { Date dateStart = new Date(); Object res = method.invoke(target,args); Date dateFinished = new Date(); long timeCost = dateFinished.getTime()-dateStart.getTime(); System.out.println("Cost time:"+timeCost); return res; } }
public static Object getProxy(Object method){ return Proxy.newProxyInstance( method.getClass().getClassLoader(), method.getClass().getInterfaces(),new MyDynamicProxyHandler(method) ); }
public static void main(String[] args) { Method staticProxy = (Method) MyDynamicProxy.getProxy(new MethodImpl()); staticProxy.method(); } }
|
装饰者模式
装饰者模式与代理模式看起来似乎有一点相似,但是两者的侧重点不同。
- 代理模式更侧重于控制对对象的访问,可能包括权限验证、日志记录、延迟加载等功能,而不会改变原有对象的行为。
- 装饰者模式则专注于在不改变对象接口的前提下,动态地为其添加新的行为或功能,更加注重功能的扩展性和灵活性。
我们做一个简单的实现就可以理解了。
首先还是定义一个接口,我希望它能提供method方法
1 2 3 4 5
| package Decorate;
public interface MyMethod { public void method(); }
|
然后是对它的实现
1 2 3 4 5 6 7 8
| package Decorate;
public class OriginMethod implements MyMethod{ @Override public void method() { System.out.println("It is the origin method"); } }
|
接下来我们要实现装饰器,首先是装饰器要继承原始接口
1 2 3 4
| package Decorate;
public interface MyMethodDecorate extends MyMethod{ }
|
然后实现装饰器1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package Decorate;
public class DecorateMethod implements MyMethodDecorate{ MyMethod originMethod;
public DecorateMethod(MyMethod originMethod){ this.originMethod = originMethod; }
@Override public void method() { System.out.println("I decorate it with first decoration!"); originMethod.method(); } }
|
然后实现装饰器2并调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package Decorate;
public class DecorateMethod2 implements MyMethodDecorate{ MyMethod originMethod;
public DecorateMethod2(MyMethod originMethod){ this.originMethod = originMethod; }
@Override public void method() { System.out.println("I decorate it with second decoration!"); originMethod.method(); }
public static void main(String[] args) { MyMethod originMethod = new OriginMethod(); originMethod = new DecorateMethod(originMethod); originMethod = new DecorateMethod2(originMethod); originMethod.method(); } }
|
1 2 3
| I decorate it with second decoration! I decorate it with first decoration! It is the origin method
|
装饰器模式和代理模式的核心差距就在于,装饰器把它装饰成一个功能更多的,更加多样化的内容,而代理模式的核心是控制对代理方法的访问。