设计模式之单例和代理模式(Java)
-
单例模式
-
核心:只能获取类的同一个实例。
-
实例:应用单例模式,设计SingletonObject类并进行测试。
-
关键点:
- 构造函数为私有;
- 代表该类单实例的引用为静态,初始化为null;
- 获取单实例的方法为公开静态,并且当单实例引用属性为null时重新构造新实例,否则直接返回该引用,确保只有一个实例被返回。
-
设计:
public class SingletonObject { private static SingletonObject instance; private SingletonObject(){ System.out.println("Created SingletonObject instance."); } public static SingletonObject getInstance() { if(instance == null) { instance = new SingletonObject(); } return instance; } }
-
测试:
public static void main(String[] args) { SingletonObject.getInstance(); SingletonObject.getInstance(); SingletonObject.getInstance(); }
-
运行结果:
Created SingletonObject instance.
观察到,SingletonObject构造方法只调用了一次。
-
-
代理模式
-
核心:
-
延迟加载
延迟加载的核心思想是:如果当前并没有使用这个组件,则不需要真正地初始化它,使用一个代理对象替代它的原有的位置,只要在真正需要的时候才对它进行加载。
使用代理模式的延迟加载是非常有意义的,首先,它可以在时间轴上分散系统压力,尤其在系统启动时,不必完成所有的初始化工作,从而加速启动时间;其次,对很多真实主题而言,在软件启动直到被关闭的整个过程中,可能根本不会被调用,初始化这些数据无疑是一种资源浪费。例如使用代理类封装数据库查询类后,系统的启动过程这个例子。若系统不使用代理模式,则在启动时就要初始化 DBQuery 对象,而使用代理模式后,启动时只需要初始化一个轻量级的对象 DBQueryProxy。
-
动态代理
动态代理是指在运行时动态生成代理类。即,代理类的字节码将在运行时生成并载入当前代理的 ClassLoader。
与静态处理类相比,动态类有诸多好处。首先,不需要为真实主题写一个形式上完全一样的封装类,假如主题接口中的方法很多,为每一个接口写一个代理方法也很麻烦。如果接口有变动,则真实主题和代理类都要修改,不利于系统维护;其次,使用一些动态代理的生成方法甚至可以在运行时制定代理类的执行逻辑,从而大大提升系统的灵活性。
(因为原网站说的比较深,就直接引用过来了,原文连接在此)
-
-
实例:设计一个数据查询代理类,代理真实的数据库查询对象进行查询操作。
-
思想:
- 延迟加载
- 设计数据库查询公共接口
- 设计真实的数据库查询类
- 设计数据库查询代理类;代理类实例化时不实例化真实的数据库查询类,而是在代理类执行查询操作时才进行实例化。
- 动态代理
- 设计数据库查询公共接口
- 设计真实的数据库查询类
- 设计用于动态创建代理类的InvocationHandler,并使用Proxy.newProxyInstance()方法获取代理类实例
- 延迟加载
-
设计:
-
延迟加载
interface IDBQuery:
public interface IDBQuery { String request(); }
class DBQuery:
public class DBQuery implements IDBQuery{ public DBQuery() { try { Thread.sleep(500); //模拟初始化时消耗资源需要等待一定时间 System.out.println("数据库连接初始化完成"); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public String request() { return "result from DBQuery"; } }
class ProxyDBQuery:
public class ProxyDBQuery implements IDBQuery{ private DBQuery realDBQuery = null; @Override public String request() { if(realDBQuery == null) { realDBQuery = new DBQuery(); } return realDBQuery.request(); } }
-
动态代理
interface IDBQuery:(同上)
class DBQuery:(同上)
class DBQueryHandler:
public class DBQueryHandler implements InvocationHandler{ private DBQuery realDBQuery = null; @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(realDBQuery == null) { realDBQuery = new DBQuery(); } if(method.getName().equals("request")) { return realDBQuery.request(); } else { return null; } } public static IDBQuery createProxy() { IDBQuery proxy = (IDBQuery) Proxy.newProxyInstance( ClassLoader.getSystemClassLoader(),new Class[]{IDBQuery.class},new DBQueryHandler() ); return proxy; } }
-
-
测试:
-
延迟加载:
代理实例创建完毕 数据库连接初始化完成 代理实例第一次查询结束 代理实例第二次查询结束
可以看到,代理实例创建时没有进行数据库连接操作,只有当第一次调用查询时才进行数据库连接操作,这样加快系统启动速度,避免在用户并不需要查询数据时造成的资源浪费。
-
动态代理:
动态代理类实例创建完成 数据库连接初始化完成 代理实例第一次请求 代理实例第二次请求
动态代理结果和延迟加载类似。动态代理的好处在于不需要写一个和接口形式完全一样的封装类,减少代码冗余,并且可以在运行时动态制定代理类的运行逻辑。
-
-