常用设计模式
设计模式六大原则
- 开闭原则:对拓展开放,对修改关闭。(接口和抽象类)
- 里氏替换原则:任何基类可以出现的地方,子类一定可以出现。
- 依赖倒转原则:面向接口编程,依赖于抽象而不依赖与具体。
- 接口隔离原则:使用多个隔离的接口比使用单个接口要好,降低类之间的耦合。
- 迪米特原则(最少知识原则):一个软件实体应当尽可能少的与其他实体发生相互作用。
- 合成复用原则:尽量采用合成/聚合的方式,而不是使用继承。
单例模式
懒汉式
当需要时才会被实例化,没实例化时节约内存。
需要使用synchronized关键字实现同步来解决线程不安全问题,会降低效率。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class SingletonLazy01 {
private static SingletonLazy01 instance;
private SingletonLazy01() { }
public static synchronized SingletonLazy01 getInstance() { if (instance == null) { instance = new SingletonLazy01(); } return instance; } }
|
懒汉式-优化
上面的案例使用静态同步函数来进行同步,导致每次调用 getInstance() 方法时,都需要进行线程锁定判断,在多线程高并发访问情况下,将会导致系统性能大幅度降低。将静态同步函数改为同步代码块来提高效率。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class SingletonLazy02 {
private static SingletonLazy02 instance;
private SingletonLazy02() { }
public static SingletonLazy02 getInstance() { if (instance == null) { synchronized (SingletonLazy02.class) { instance = new SingletonLazy02(); } } return instance; } }
|
懒汉式-双重检查锁定
上面的案例中,当两个线程都进入getInstance()方法并通过instance == null判断后,线程1拿到锁进行实例化,实例化完成后线程2也拿到锁又会再次实例化导致线程不安全。因此需要再进行一次判断保证线程安全。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class SingletonLazy03 {
private static SingletonLazy03 instance;
private SingletonLazy03() {
}
public static SingletonLazy03 getInstance() { if (instance == null) { synchronized (SingletonLazy03.class) { if (instance == null) { instance = new SingletonLazy03(); } } } return instance; } }
|
饿汉式
当class被加载时就会初始化,天生线程安全。
1 2 3 4 5 6 7 8 9 10 11
| public class SingletonHungry {
private static final SingletonHungry instance = new SingletonHungry();
private SingletonHungry() { }
public static SingletonHungry getInstance() { return instance; } }
|
工厂模式
实现创建者和调用者分离。
简单工厂模式
该模式对对象创建管理方式最为简单,因为其仅仅简单的对不同类对象的创建进行了一层薄薄的封装。该模式通过向工厂传递类型来指定要创建的对象,其UML类图如下:
以下通过火箭发射案例来说明该模式:
火箭抽象类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class AbstractRocket implements IFight {
@MetaData(name = "名称", desc = "") private String name; @MetaData(name = "型号", desc = "") private String model; @MetaData(name = "高度", desc = "") private Double height; @MetaData(name = "重量", desc = "") private Double weight;
@Override public void fly() { } }
|
神舟火箭类:
1 2 3 4 5 6 7 8 9 10
| public class SZRocket extends AbstractRocket {
@MetaData(name = "是否载人", desc = "") private boolean isManned;
@Override public void fly() { System.out.println(this.getClass().getName() + "--- fly"); } }
|
长征火箭类:
1 2 3 4 5 6 7 8 9 10
| public class CZRocket extends AbstractRocket {
@MetaData(name = "有效载荷", desc = "") private Double payload;
@Override public void fly() { System.out.println(this.getClass().getName() + "--- fly"); } }
|
火箭发射工厂:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class RocketLaunchSimpleFactory {
public void launch(String rocketType) { IFight rocket = null; switch (rocketType) { case ROCKET_TYPE_SZ: rocket = new SZRocket(); break; case ROCKET_TYPE_CZ: rocket = new CZRocket(); break; default: break; } rocket.fly(); } }
|
发射!
1 2 3 4 5
| public static void main(String[] args) { RocketLaunchSimpleFactory rocketSimpleFactory = new RocketLaunchSimpleFactory(); rocketSimpleFactory.launch(RocketLaunchSimpleFactory.ROCKET_TYPE_SZ); rocketSimpleFactory.launch(RocketLaunchSimpleFactory.ROCKET_TYPE_CZ); }
|
工厂方法模式
和简单工厂模式中工厂负责生产所有产品相比,工厂方法模式将生成具体产品的任务分发给具体的产品工厂,其UML类图如下:
火箭创建抽象工厂类:
1 2 3 4 5 6
| public class AbstractRocketCreateFactory {
public AbstractRocket create() { return null; } }
|
神舟火箭创建工厂类:
1 2 3 4 5 6 7
| public class SZRocketCreateFactory extends AbstractRocketCreateFactory {
@Override public SZRocket create() { return new SZRocket(); } }
|
长征火箭创建工厂类:
1 2 3 4 5 6 7
| public class SZRocketCreateFactory extends AbstractRocketCreateFactory {
@Override public SZRocket create() { return new SZRocket(); } }
|
发射!
1 2 3 4 5 6
| public static void main(String[] args) { AbstractRocketCreateFactory szFactory = new SZRocketCreateFactory(); AbstractRocketCreateFactory czFactory = new CZRocketCreateFactory(); szFactory.create().fly(); czFactory.create().fly(); }
|
抽象工厂模式
抽象工厂模式通过在AbstarctFactory中增加创建产品的接口,并在具体子工厂中实现新加产品的创建。
代理模式
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。
SpringAOP基于代理模式实现(JDK动态代理和CGLIB代理)。
静态代理
需要自己写代理类。在程序运行之前,代理类.class文件就已经被创建了。
使用下面的案例来说明:
卖房接口类:
1 2 3
| public interface ISellHouse { void sell(); }
|
房东类:
1 2 3 4 5 6
| public class HouseOwner implements ISellHouse { @Override public void sell() { System.out.println("房东卖房"); } }
|
房屋中介类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class HouseProxy implements ISellHouse {
private HouseOwner houseOwner;
public HouseProxy(HouseOwner houseOwner) { this.houseOwner = houseOwner; }
@Override public void sell() { System.out.println("中介介入"); houseOwner.sell(); } }
|
运行:
1 2 3 4
| private static void staticProxy() { HouseProxy houseProxy = new HouseProxy(new HouseOwner()); houseProxy.sell(); }
|
动态代理
动态代理是在程序运行时通过反射机制动态创建的。
JDK动态代理
通过Java的反射机制实现代理类
使用下面的案例来说明:
JDK动态代理类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class JdkDynamicProxy implements InvocationHandler {
private Object target;
public JdkDynamicProxy(Object target) { this.target = target; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("JDK动态代理"); Object invoke = method.invoke(target, args); return invoke; } }
|
运行:
1 2 3 4 5 6 7
| private static void jdkDynamicProxy() { HouseOwner houseOwner = new HouseOwner(); JdkDynamicProxy jdkDynamicProxy = new JdkDynamicProxy(houseOwner); ISellHouse proxyInstance = (ISellHouse) Proxy.newProxyInstance(houseOwner.getClass().getClassLoader(), new Class[]{ISellHouse.class}, jdkDynamicProxy); proxyInstance.sell(); }
|
注意Proxy.newProxyInstance()方法接受三个参数:
ClassLoader loader
:指定当前目标对象使用的类加载器,获取加载器的方法是固定的
Class<?>[] interfaces
:指定目标对象实现的接口的类型,使用泛型方式确认类型
InvocationHandler
:指定动态处理器
,执行目标对象的方法时,会触发事件处理器的方法
CGLIB代理
其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。
CGLIB动态代理类:
1 2 3 4 5 6 7 8
| public class CglibDynamicProxy implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("CGLIB动态代理"); Object invokeSuper = methodProxy.invokeSuper(o, args); return invokeSuper; } }
|
运行:
1 2 3 4 5 6 7 8 9
| private static void cglibDynamicProxy() { CglibDynamicProxy cglibDynamicProxy = new CglibDynamicProxy(); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(HouseOwner.class); enhancer.setCallback(cglibDynamicProxy); ISellHouse sellHouse = (ISellHouse) enhancer.create(); sellHouse.sell(); }
|
-
CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。
-
对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。
-
由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。
注:ASM是Java字节码控制