常用设计模式

常用设计模式

设计模式六大原则

  • 开闭原则:对拓展开放,对修改关闭。(接口和抽象类)
  • 里氏替换原则:任何基类可以出现的地方,子类一定可以出现。
  • 依赖倒转原则:面向接口编程,依赖于抽象而不依赖与具体。
  • 接口隔离原则:使用多个隔离的接口比使用单个接口要好,降低类之间的耦合。
  • 迪米特原则(最少知识原则):一个软件实体应当尽可能少的与其他实体发生相互作用。
  • 合成复用原则:尽量采用合成/聚合的方式,而不是使用继承。

单例模式

懒汉式

当需要时才会被实例化,没实例化时节约内存。

需要使用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 {

/**
* @param rocketType 火箭类型
* @return void
* @description 发射
* @author zhuweitung
* @date 2020/9/10
*/
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);
//JDK动态代理通过反射机制生成代理
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();
//使用asm框架生成代理类
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字节码控制


常用设计模式
https://blog.kedr.cc/posts/511749417/
作者
zhuweitung
发布于
2020年9月10日
许可协议