[toc]
Java设计模式
1. Java反射技术
反射技术能够配置类的全限定名、方法和参数、完成对象初始化操作、甚至反射某些方法。Spring IoC的基本原理也是反射技术。
Java反射内容繁多,本章重点讲解对象构建和方法的反射调用。Java中反射是通过包java.lang.reflect.*实现。
1.1 通过反射构建对象
无参构造:如ReflectSample类
1 2 3 4 5 public class ReflectSample { public void sayHello (String name) { System.out.println("Hello " + name); } }
通过反射方式构建对象
1 2 3 4 5 6 7 8 9 public ReflectSample getInstance () { ReflectSample obj = null ; try { obj = (ReflectSample) Class.forName("xxx.ReflectSample" ).newInstance(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return obj; }
Class.forName().newInstance()
是给类加载器注册一个类ReflectSample的全限定名,然后通过newInstance方法初始化一个对象。本例是一个无参构造的类。
有参构造:以ReflectSample2类为例:
1 2 3 4 5 6 7 8 9 10 11 public class ReflectSample2 { private String name; public ReflectSample2 (String name) { this .name = name; } public void sayHello (String name) { System.out.println("Hello " + name); } }
此时的反射构建对象:
1 2 3 4 5 6 7 8 9 public ReflectSample2 getInstance () { ReflectSample2 obj = null ; try { obj = (ReflectSample2) Class.forName("xxx.ReflectSample2").getConstructor(String.class).newInstance("Tom"); } catch (Exception e) { e.printStackTrace(); } return obj; }
本例中的反射,先用forName加载到类的加载器,然后通过getConstructor方法 ,它的参数可以是多个,这里的String.class表示只有一个参数类型为String的构建方法,再用newInstance方法生成对象,实际等价于obj = new ReflectSample2("Tom")
,只是使用了反射机制。
反射只要配置就可生成对象,可以降低耦合,比较灵活,但比较慢。
1.2 反射方法
使用反射方法前要先获取对象
1 2 3 4 5 6 7 8 9 10 11 public Object reflectMethod () { Object obj = null ; ReflectSample target = new ReflectSample(); try { Method method = ReflectSample.class.getMethod("sayHello", String.class); obj = method.invoke(target, "Tom" ); } catch (Exception e) { e.printStackTrace(); } return obj; }
对于ReflectSample.class.getMethod()
,若有对象但不知道类时,可以使用target.getClass().getMethod()
,getMethod中第一个参数是方法名,第二个参数是方法参数类型,是一个列表。
具体反射方法的执行是method.invoke()
实现,等价于target.sayHello("Tom")
,若反射的方法有多个参数,则invoke(target, obj1, obj2…)
即可。
1.3 反射实例
完整通过反射创建对象并调用方法,继续以ReflectSample为例
1 2 3 4 5 6 7 8 9 10 11 public Object reflect () { ReflectSample obj = null ; try { obj = (ReflectSample) Class.forName("xxx.ReflectSample" ).newInstance(); Method method = obj.getClass().getMethod("sayHello" , String.class ) ; method.invoke(obj, "Tom" ); } catch (Exception e) { e.printStackTrace(); } return obj; }
如此完全可以通过配置来完成对象和方法的反射,增强了Java的可配置性和可扩展性,IoC就是基于此法的典型例子。
2. 动态代理模式和责任链模式
动态代理是生成一个代理对象(占位),来代理真实对象,从而控制真实对象的访问
代理模式:调用者与代理对象联系,代理对象与真实对象联系,如此可在访问真实对象的前或后,加入其它逻辑,并且可以判断是否需要访问真实对象等
故代理分两步:代理对象和真实对象建立代理关系、实现代理对象的代理逻辑方法
Java有多种动态代理技术,Spring常用JDK(JDK自带功能)和CGLIB(第三方),MyBatis还使用了Javassist
2.1 JDK动态代理
是JDK自带功能,java.lang.reflect.*提供的方式,必须借助一个接口 才能产生代理对象:
1 2 3 4 5 6 7 8 9 10 11 12 public interface HelloWorld { public void syaHelloWorld () ; } public class HelloWorldImp implements HelloWorld { @override public void sayHelloWorld () { System.out.println("Hello World." ); } }
接下来需要将代理对象和真实对象绑定,并实现代理对象的代理逻辑方法。JDK动态代理中,要实现代理类必须实现java.lang.reflect.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 public class JdkProxyExample implements InvocationHandler { private Object target = null ; public Object bind (Object target) { this .target = target; return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this ); } @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { System.out.println("进入代理逻辑方法" ); System.out.println("在调度真实对象之前的服务" ); Object obj = method.invoke(target, args); System.out.println("在调度真实对象之后的服务" ); return obj; } }
第一步建立代理和真实对象联系时,Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this)
,第一个参数是真实对象的类加载器;第二个参数是要把生成的动态代理对象下挂在哪些接口,即最开始创建并实现的接口HelloWorld,则代理对象就可以通过HelloWorld proxy = xxx;
声明;第三个参数是定义实现方法逻辑的代理类,需要实现InvocationHandler的invoke方法,它就是代理逻辑方法的实现方法。
第二步实现代理逻辑方法。invoke实现代理逻辑,有3个参数:proxy代理对象,就是bind生成的对象;method当前调度的犯法;args调度方法的参数。
最后测试此动态代理:
1 2 3 4 5 6 7 public void testJdkProxy () { JdkProxyExample jdk = new JdkProxyExample(); HelloWold proxy = (HelloWorld) jdk.bind(new HelloWorldImp()); proxy.sayHelloWorld(); }
运行将会输出:
1 2 3 4 进入代理逻辑方法 在调度真实对象之前的服务 Hello World. 在调度真实对象之后的服务
2.2 CGLIB动态代理
JDK动态代理只能提供接口才能使用,若不能提供接口,就要用其他第三方技术,如CGLIB动态代理,它只要一个非抽象类就能实现动态代理。
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 public class CglibProxyExample implements MethodInterceptor { public Object getProxy (Class cls) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(cls); enhancer.setCallback(this ); return enhancer.create(); } @Override public Object intercept (Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.err.println("调用真实对象前" ); Object result = methodProxy.invokeSuper(proxy, args); System.err.println("调用真实对象后" ); return result; } }
使用了CGLIB的加强者Enhancer,通过设置超类的方法(setSuperclass),通过setCallback方法设置哪个类为它的代理类。其中 参数this意味着是当前对象,就要求用this对象实现接口MethodInterceptor的方法intercept,然后返回代理对象。如此,当前类的intercept方法就是代理逻辑方法。
测试代理:
1 2 3 4 5 public void testCGLIBProxy () { CglibProxyExample cpe = new CglibProxyExample(); ReflectSample obj = (ReflectSample)cpe.getProxy(ReflectSample.class ) ; obj.sayHello("Tom" ); }
输出:
1 2 3 调用真实对象前 Hello Tom 调用真实对象后
JDK与CGLIB动态代理都是用getProxy方法生成代理对象,制定代理的逻辑类。代理逻辑类要实现一个接口的一个方法,那么这个接口定义的方法就是代理对象的逻辑方法,可以控制真实对象的方法。
2.3 拦截器
由于动态代理的难以理解,通过设计者会设计一个拦截器接口供开发者使用,可将动态代理置入黑箱。
用JDK动态代理实现一个拦截器,首先需要定义拦截器接口Interceptor。
1 2 3 4 5 6 7 public interface Interceptor { public boolean before (Object proxy, Object target, Method method, Object[] args) ; public void around (Object proxy, Object target, Method method, Object[] args) ; public void after (Object proxy, Object target, Method method, Object[] args) ; }
参数proxy代理对象、target真实对象、method方法、args运行方法参数。
before方法返回boolean,在真实对象前调用,返回true时才反射真实对象的方法,若返回false则调用around方法。
在反射真实对象方法或around方法执行后,调用after方法。
接口实现类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class MyInterceptor implements Interceptor { @Override public boolean before (Object proxy, Object target, Method method, Object[] args) { System.err.println("反射方法前逻辑" ); return false ; } @Override public void after (Object proxy, Object target, Method method, Object[] args) { System.err.println("反射方法后逻辑。" ); } @Override public void around (Object proxy, Object target, Method method, Object[] args) { System.err.println("取代了被代理对象的方法" ); } }
本类实现了接口的所有方法,使用JDK动态代理,就能实现这些方法的适时调用逻辑。
在JDK动态代理中使用拦截器:
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 public class InterceptorJdkProxy implements InvocationHandler { private Object target; private String interceptorClass = null ; public InterceptorJdkProxy (Object target, String interceptorClass) { this .target = target; this .interceptorClass = interceptorClass; } public static Object bind (Object target, String interceptorClass) { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InterceptorJdkProxy(target, interceptorClass)); } @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { if (interceptorClass == null ) { return method.invoke(target, args); } Object result = null ; Interceptor interceptor = (Interceptor) Class.forName(interceptorClass).newInstance(); if (interceptor.before(proxy, target, method, args)) { result = method.invoke(target, args); } else { interceptor.around(proxy, target, method, args); } interceptor.after(proxy, target, method, args); return result; } }
首先在bind方法中用JDK动态代理绑定了真实对象,返回代理对象。
然后若没有设置拦截器,则直接反射真实对象的方法,随后结束。
若有拦截器,则通过反射生成拦截器,并使用。
测试拦截器:
1 2 3 4 public void testInterceptorJDKProxy () { HelloWorld proxy = (HelloWorld) InterceptorJdkProxy.bind(new HelloWroldImp(), "xxx.MyInterceptor" ); proxy.sayHelloWorld(); }
由于MyInterceptor中,before方法指定返回false,故输出:
1 2 3 反射方法前逻辑。 取代了被代理对象的方法。 反射方法后逻辑。
2.4 责任链模式
当一个对象在一条链上被多个拦截器拦截处理时,就把这样的设计模式称为责任链模式。此模式下,可将原始对象层层动态代理,形成嵌套结构。即首先建立多个拦截器接口的实现类,代表多个拦截器,先后进行多次代理,使用前一次的代理对象当做后一次的真实对象。如此运行的话,会生成如下结果:
1 2 3 4 5 拦截器2的before方法 拦截器1的before方法 Hello World,真实对象的方法 拦截器1的after方法 拦截器2的after方法
可见,责任链模式优点在于可加入新的拦截器,增加拦截逻辑,但缺点是会增加代理和反射,而它们的性能都不高。
3. 观察者模式 Observer
又称为发布订阅模式,是对象的行为模式。定义了一种一对多的依赖关系,让多个观察者对象同时监视被观察者状态,当被观察者变化时,通知所有观察者,并让它们主动更新自己。
如一个判断条件,决定很多后续执行的情况,若使用简单的if判断,则使得内容很多,需要大量catch以免后续执行之间的妨碍。而观察者模式更易于扩展,责任也更清晰。
本例为商家产品列表如有上新,则各个电商平台观察到后自动上新。
被观察者:需要继承java.util.Observable类,本例是商家的产品列表
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 ProductList extends Observable { private List<String> productList = null ; private static ProductList instance; private ProductList () {} public static ProductList getInstance () { if (instance == null ) { instance = new ProductList(); instance.productList = new ArrayList<String>(); } return instance; } public void addProductListObserver (Observer observer) { this .addObserver(observer); } public void addProudct (String newProduct) { productList.add(newProduct); System.err.println("产品列表新增了产品:" +newProduct); this .setChanged(); this .notifyObservers(newProduct); } }
构建方法私有化,单例模式,只能通过getInstance方法获得实例。
观察者:需要实现Observer接口的update方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class JingDongObserver implements Observer { @Override public void update (Observable o, Object product) { String newProduct = (String) product; System.err.println("发送新产品【" +newProduct+"】同步到京东商城" ); } } public class TaoBaoObserver implements Observer { @Override public void update (Observable o, Object product) { String newProduct = (String) product; System.err.println("发送新产品【" +newProduct+"】同步到淘宝商城" ); } }
测试本例:
1 2 3 4 5 6 7 8 public static void main (String[] args) { ProductList observable = ProductList.getInstance(); TaoBaoObserver taoBaoObserver = new TaoBaoObserver(); JingDongObserver jdObserver = new JingDongObserver(); observable.addObserver(jdObserver); observable.addObserver(taoBaoObserver); observable.addProudct("产品1" ); }
得到输出:
1 2 3 产品列表新增了产品:产品1 发送新产品【产品1】同步到淘宝商城 发送新产品【产品1】同步到京东商城
4. 工厂模式和抽象工厂模式
对于同一大类下的诸多类,用户只需知道自己需要什么类,具体实例化交由工厂实现并返回。
4.1普通工厂模式
有一Product接口,有5个类实现了接口,为Product1、Product2、…、Product5,并且由ProductFactory同一管理。用户只需使用工厂中的createProduct方法,并传入所需序号,工厂即可实例化并返回对象。
1 2 3 4 5 6 7 8 9 10 public class ProductFactory { public static Product createProduct (String productNo) { switch (productNo) { case "1" : return new Product1(); ... case "5" : return new Product5(); default : throw new NotSuppportedException("不支持此编号产品" ); } } }
4.2 抽象工厂模式
普通工厂,解决了一类对象创建问题,但有时对象复杂,有很多种,同时又细分了几个类别。此时若只用一个工厂,就会造成工厂内部实现逻辑繁杂,这就需要划分工厂,但仍希望留一个统一的工厂给使用者,再由这个总工厂接收任务后下发给具体完成对象创建的分工厂。这里的总工厂并不实际存在,是通过各分厂实现的,称为抽象工厂。如此封装,简化了调用者的使用过程。
抽象工厂模式可以向调用者提供一个接口,使其在不必指定产品的具体情况下,创建多个产品族中的产品对象。
为了统一,首先需要定义一个接口,每个工厂(包括虚拟工厂)都要实现这个接口:
1 2 3 public interface ProductFactoryInter { public Product createProduct (String productNo) ; }
随后创建两个分厂,用于实现工厂的具体实现方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 public class ProductFactory1 implements ProductFactoryInter { public Product createProduct (String productNo) { Product product = xxx; return product; } } public class ProductFactory2 implements ProductFactoryInter { public Product createProduct (String productNo) { Product product = xxx; return product; } }
创建总工厂/抽象工厂,并设定任务分发规则,即productNo编号,1开头则交由分厂1完成,2开头则交由分厂2完成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class ProductFactory implements ProductFactoryInter { public static Product createProduct (String productNo) { char ch = productNo.charAt(0 ); ProductFactoryInter factory = null ; if (ch == '1' ) { factory = new ProductFactory1(); } else if (ch == '2' ) { factory = new ProductFactory2(); } if (factory != null ) { return factory.createProduct(productNo); } return null ; } }
如此,就实现了抽象工厂与具体完成任务的分厂之间的联系。调用者只需通过抽象工厂发布任务,抽象工厂自动分发任务给分厂完成并返回结果。各分厂只需维护自己的代码即可。
5. 建造者模式 Builder
属于对象的创建模式,可以将一个产品的属性与产品的生产过程分开,从而使一个建造过程生成具有不同属性的对象。
适用于对象具有较多属性,导致一个产品族中有很多不同产品,使用new或工厂模式时,传入过多参数造成不便。
Builder模式是分步构建对象的模式,是逐个产品进行构建,用一个配置类对这些步骤进行统筹,然后将所有信息交给构建器完成构建对象。
本例中,用门票展示,门票包含普通成年人票、老人票、儿童票、军属票。
首先创建配置类 TicketHelper,帮助我们一步步完成构建对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class TickerHelper { public void buildAdult (String info) { System.out.println("输出模拟构建成人票逻辑," + info); } public void buildChild (String info) { System.out.println("输出模拟构建儿童票逻辑," + info); } public void buildElderly (String info) { System.out.println("输出模拟构建老人票逻辑," + info); } public void buildSoldier (String info) { System.out.println("输出模拟构建军属票逻辑," + info); } }
然后需要一个构建类 TicketBuilder:
1 2 3 4 5 6 public class TicketBuilder { public static Object builder (TicketHelper helper) { System.out.println("通过配置类TicketHelper构建门票信息" ); return null ; } }
完成门票构建:
1 2 3 4 5 6 TicketHelper helper = new TicketHelper(); helper.buildAdult("成人票" ); helper.buildChild("儿童票" ); helper.buildElderly("老人票" ); helper.buildSoldier("军属票" ); Object ticket = TicketBuilder.builder(helper);