深入浅出面向对象设计模式(Java)

设计模式是什么

设计模式是面向对象的一种思想。

设计模式的基本原则?

在这里插入图片描述

  • 单一职责原则
  • 开放封闭原则
  • 里氏替换原则
  • 接口隔离原则
  • 依赖翻转原则

基本分类和为什么分为3类?

创建型(怎么优雅创建对象)
结构性(对象的结构)
行为型(运行效果)(主要使用多态)

另一种分类方式
在这里插入图片描述
在这里插入图片描述

创建型

工厂方法(Factory Method)

意图、思想

官方:定义一个用于创建对象的接口,并控制返回那个类的实例。
基本思想:我需要一辆汽车,我不用关心汽车是怎么创造出来的,只需要去工厂提货。
分类:创造型、构造型。

如果你不想让客户代码决定实例化那个类时,常常可以使用工厂模式。

经典范例:迭代器

工厂模式使得客户端代码无须关心使用哪个类的实例。无论是哪一个集合类都是调用iterator返回一个迭代器。

        List<String> list = new ArrayList<>();
        Iterator<String> it1 = list.iterator();
        //
        Set<String> set = new HashSet<>();
        Iterator<String> it2 = set.iterator();
    public Iterator<E> iterator() {
        return new Itr();
    }
    public Iterator<E> iterator() {
        return map.keySet().iterator();
    }

我们使用集合类的迭代器只需要调用iterator,具体类型由实现类定义。

案例1

public class CarFactory {

    public Car getCar(int type) {
        if (type == 1) {
            return new BenzCar("奔驰", 7);
        }
        if (type == 2) {
            return new BmwCar("宝马", 7);
        }
        throw new IllegalArgumentException("类型错误");
    }
}
public interface Car {

    void run();
}
public class BenzCar implements Car {

    String name;
    Integer total;

    public BenzCar(String name, Integer total) {
        this.name = name;
        this.total = total;
    }

    @Override
    public void run() {
        System.out.println("Benz run fast .. ");
    }

    @Override
    public String toString() {
        return "BenzCar{" +
                "name='" + name + '\'' +
                ", total=" + total +
                '}';
    }
}
public class BmwCar implements Car {

    String name;
    Integer total;

    public BmwCar(String name, Integer total) {
        this.name = name;
        this.total = total;
    }


    @Override
    public void run() {
        System.out.println("Bmw run fast too ..");
    }

    @Override
    public String toString() {
        return "BmwCar{" +
                "name='" + name + '\'' +
                ", total=" + total +
                '}';
    }
}

我需要汽车的时候只需要去CarFactory 里面告诉他们类型就可以得到对应的汽车。

案例2

JDK 中的工厂设计模式

抽象工厂 (Abstract Factory)

意图、思想

官方:抽象工厂又名工具箱,其意图是允许创建一组相关或相互依赖的对象。

单例模式

意图、思想

官方:确保一个类有且仅有一个实例,并为它提供一个全局访问点。
大白话:对于一个类,有且只有一个实例。
分类:创建型、职责型。

常见问题

怎么样阻止其他开发人员创建新的实例?
为什么要延迟初始化一个单例,而不是声明时初始化?

饿汉式

怎么理解什么是饿汉式,我已经饿的受不了了,我必须立即吃饭。
定义:在类加载的时候就会创建单例对象。
优点:简单,无线程安全问题。
缺点:浪费空间,不够灵活。

public class Hungry {

    public final static Hungry INSTANCE = new Hungry();

    private Hungry() {
    }
}

饿汉式 静态代码块

public class HungryStatic {

    public final static HungryStatic INSTANCE;

    static {
        INSTANCE = new HungryStatic("例如从配置文件读入");
    }

    private HungryStatic(String config) {
    }

    private String config;

    public String getConfig() {
        return config;
    }

    public void setConfig(String config) {
        this.config = config;
    }
}

枚举

优点:线程安全,不会被反射破坏。

public enum Enum {
    
    INSTANCE;
    
}

懒汉式 (线程不安全)

定义:在使用的时候判断如果没有创建过则创建。
优点:节约空间。
缺点:存在线程安全问题。

public class Lazy {

    private static Lazy instance;

    private Lazy() {
    }

    public static Lazy getInstance() {
        if (instance == null) {
            instance = new Lazy();
        }
        return instance;
    }
}

多个线程同时进行 instacne == null 判断,会导致多个线程同时创建对象。

懒汉式 双检锁 DCL

public class LazySafe {

    private static volatile LazySafe instance;

    private LazySafe() {
    }

    public static LazySafe getInstance() {
        if (instance == null) {
            synchronized (LazySafe.class) {
                if (instance == null) {
                    instance = new LazySafe();
                }
            }
        }
        return instance;

    }
}

为什么需要第二次判断

假设有10个线程同时进来争抢锁,线程一拿到锁,其他9个线程进入阻塞状态等待锁释放继续争抢,如果没有第二次判断,每个线程拿到锁后都会创建一个新的对象,加入第二次判断防止后来的线程创建。

为什么要加 volatile

由于指令重排序,new 对象的过程可能会发生先指向空对象但是没有完成初始化的情况。

一句话总结就是防止拿到 instance == null 的 对象。

反编译一条 new 指令可以拿到以下的结果。
在这里插入图片描述
步骤4 初始化对象,步骤7 指针指向该对象,由于 cpu存在指令重排序,先完成指针指向的话就会产生null对象的情况,所以需要禁止指令重排序保证对象初始化之后在完成指向的动作。

懒汉式 静态内部类

原理 : 静态内部类的延迟初始化,且类只会加载一次。

public class LazyInner {

    private LazyInner() {
    }

    private static class Inner {
        private static final LazyInner INSTANCE = new LazyInner();
    }

    public static LazyInner getInstance() {
        return Inner.INSTANCE;
    }

}

The “Double-Checked Locking is Broken” Declaration

如何破坏单例?

多线程破坏单例

在普通的懒汉式单例中可以被多线程破坏。

在这里插入图片描述

错误示例

将获取实例的锁升级到方法级别。

可以保证线程安全,但是方法性能太低,任意读请求都需要排队依次加锁获取对象。

典型的锁粒度过粗的问题。

在这里插入图片描述

正确示例

使用双检锁,或者内部类的形式保证线程安全。

指令重排序破坏单例

双检锁不使用 volatile 关键字修饰,就会产生重排序情况的产生。

克隆破坏单例

反序列化破坏单例

反射破坏单例

结论 只有枚举可以防止通过反射来破坏。

破坏实例

我们以Runtime类为例来演示使用反射破坏单例。

  		Class<Runtime> aClass = Runtime.class;
        Constructor<Runtime> cons = aClass.getDeclaredConstructor();
        cons.setAccessible(true);
        Runtime runtime = cons.newInstance();
        Runtime runtime2 = cons.newInstance();
        System.out.println(runtime);
        System.out.println(runtime2);
        ----
java.lang.Runtime@568db2f2
java.lang.Runtime@378bf509

解决方案

优先使用枚举来解决反射破坏单例的问题。
在这里插入图片描述

我们来获取一下枚举类中的构造器

        Class<Enum> aClass = Enum.class;
        Constructor<?>[] constructors = aClass.getDeclaredConstructors();
        // Constructor<Enum> cons = aClass.getDeclaredConstructor();
        Constructor<Enum> cons2 = aClass.getDeclaredConstructor(String.class, int.class);
        // cons.setAccessible(true);
        cons2.setAccessible(true);
        // Enum e1 = cons.newInstance();
        // Enum e2 = cons.newInstance();
        Enum e3 = cons2.newInstance();
        Enum e4 = cons2.newInstance();

可以看到里面会生成一个String , int 参数类型的构造器
在这里插入图片描述
idea 反编译结果跟预期明显不同。
在这里插入图片描述
javap 反编译 class 文件后发现也没有相应的构造方法
在这里插入图片描述
我们更换反编译工具,使用 jad 进行后续的测试

jad的安装和使用

jad下载地址

在这里插入图片描述
将 jad.exe 放在 jdk 的 bin 目录下即可
在这里插入图片描述

执行命令 jad xxxx.class文件得到反编译后的源代码。

jad 反编译后成功看到Enum类中 String 和 int 参数的构造函数。

在这里插入图片描述

单例在java的应用场景

Runtime

饿汉式实现单例。
在这里插入图片描述

结构型

代理 (proxy)

参考链接1

参考链接2

意图

通过提供一个代理(proxy)或者占位符来控制对该对象的访问。

案例1

这里有一个接口 Msg

public interface Msg {

    void sendMsg(String msg);

    Msg work(String str);

}

这里有一个Msg的实现类

public class MsgImpl implements Msg {

    @Override
    public void sendMsg(String msg) {
        System.out.println(msg);
    }

    @Override
    public Msg work(String str) {
        System.out.println(str);
        return this;
    }
}

正常的调用过程如下

public class Client {
    public static void main(String[] args) {
        Msg msg = new MsgImpl();
        msg.sendMsg("abcdefg");
        msg.work("w1").work("w2");
    }
}

某一天我想统计一下每一个方法耗时,最基本的我直接修改实现类中的代码,如果这个类有一百个方法,那么。。。或者这个类是其他 jar 包提供的,我们就不能通过修改代码的形式来实现。

静态代理

我们直接把实现类的对象丢进来执行原来的方法,我们在增强我们想要的功能。

public class MsgProxy implements Msg {

    private Msg msg;

    public MsgProxy(Msg msg) {
        this.msg = msg;
    }

    @Override
    public void sendMsg(String str) {
        System.out.println("start ...");
        msg.sendMsg(str);
        System.out.println("end ...");
    }

    @Override
    public Msg work(String str) {
        return null;
    }
}

如果我们想统计 100 个类呢? 创建 100 个静态代理么,如果你不嫌麻烦的话当然可以。

那么有没有更加简单的方法呢?

动态代理

核心接口 InvocationHandler 用来控制代理对象的动作,也就是具体实现。
核心方法 Proxy.newProxyInstance 用来创建代理对象。

第一步定义处理器,代理对象的所有调用都会分发到这个方法中。

public class ActionInvocationHandle implements InvocationHandler {

    Object o;

    public ActionInvocationHandle(Object o) {
        this.o = o;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long st = System.currentTimeMillis();
        Object result = method.invoke(o, args);
        System.out.println(System.currentTimeMillis() - st);
        return result;
    }
}

第二步 创建代理对象

        Msg msg2 = new MsgImpl();
        Msg proxy2 = (Msg) Proxy.newProxyInstance(Msg.class.getClassLoader(),
                new Class[]{Msg.class}, 
                new ActionInvocationHandle(msg2));
        proxy2.sendMsg("sdadsa");

第三步 查看输出结果

sdadsa
1000

封装创建对象过程

在处理器中新增一段代码

public class ActionInvocationHandle implements InvocationHandler {

    Object o;

    public ActionInvocationHandle(Object o) {
        this.o = o;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long st = System.currentTimeMillis();
        TimeUnit.SECONDS.sleep(1);
        Object result = method.invoke(o, args);
        System.out.println(System.currentTimeMillis() - st);
        return result;
    }

    public static <T> T createProxy(T target, Class<T> targetClass) {
        if (!targetClass.isInterface()) {
            throw new IllegalStateException("targetInterface必须是接口类型!");
        } else if (!targetClass.isAssignableFrom(target.getClass())) {
            throw new IllegalStateException("target必须是targetInterface接口的实现类!");
        }
        ClassLoader classLoader = target.getClass().getClassLoader();
        return (T) Proxy.newProxyInstance(classLoader,
                target.getClass().getInterfaces(),
                new ActionInvocationHandle(target));
    }
}

创建的过程简化为下面这样

        Msg msg2 = new MsgImpl();
        Msg proxy3 = ActionInvocationHandle.createProxy(msg2, Msg.class);
        proxy3.sendMsg("xxxxxxxxx");

如何获取Proxy0.class文件

以jdk1.8为例

        //生成$Proxy0的class文件
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

获取到的源码如下所示

public final class $Proxy0 extends Proxy implements Msg {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void sendMsg(String var1) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.example.miccommon.designpatterns.proxy.Msg").getMethod("sendMsg", Class.forName("java.lang.String"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

Proxy 类

Proxy 重要方法

在这里插入图片描述

//获取处理器
    public static InvocationHandler getInvocationHandler(Object proxy)
        throws IllegalArgumentException
//获取代理class
    public static Class<?> getProxyClass(ClassLoader loader,
                                         Class<?>... interfaces)
        throws IllegalArgumentException
//判断是否
    public static boolean isProxyClass(Class<?> cl)
//创建代理对象
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException

cglib

添加依赖

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

jar包目录如下
在这里插入图片描述
我们重点看一下 proxy 模块

在这里插入图片描述
这有一个类 Service1

public class Service1 {
    public void m1() {
        System.out.println("我是m1方法");
    }

    public void m2() {
        System.out.println("我是m2方法");
    }
}

使用 cglib 来代理 Service1 这一个类

public class Cglib01 {

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Service1.class);
        enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
            System.out.println("调用方法:" + method);
            return methodProxy.invokeSuper(o, objects);
        });
        Service1 proxy = (Service1) enhancer.create();
        proxy.m1();
        proxy.m2();
    }
}
------
调用方法:public void com.example.lurenjia.spring.c15.cglib.Service1.m1()
我是m1方法
调用方法:public void com.example.lurenjia.spring.c15.cglib.Service1.m2()
我是m2方法

如果我们将 setCallback 这一行注释掉会发生什么呢?

在这里插入图片描述
可以看到 Callback 参数是必须要传的。

那么重点就是 setCallback 这个地方,我们点进去
在这里插入图片描述
参数传入了一个 Callback 类型接口,我们点进去
在这里插入图片描述
Callback 主要子接口如下
在这里插入图片描述

Dispatcher

在这里插入图片描述

在这里插入图片描述

public class CglibExtend08 {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Service1.class);
        enhancer.setCallback((Dispatcher) Service1::new);
        Service1 proxy = (Service1) enhancer.create();
        proxy.m1();
        proxy.m2();
    }
}

FixedValue

在这里插入图片描述

public class Cglib03 {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Service3.class);
        enhancer.setCallback((FixedValue) () -> "路人甲");
        Service3 proxy = (Service3) enhancer.create();
        System.out.println(proxy.m1());
        System.out.println(proxy.m2());
        System.out.println(proxy.toString());
    }
}

InvocationHandler

在这里插入图片描述

jdk
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
cglib
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

在这种类型下跟 jdk 动态代理一样

public class Cglib07 {
    public static void main(String[] args) {
        Service1 service1 = new Service1();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Service1.class);
        enhancer.setCallback((InvocationHandler) (o, method, objects) -> {
            System.out.println("调用方法:" + method);
            return method.invoke(service1, objects);
        });
        Service1 proxy = (Service1) enhancer.create();
        proxy.m1();
        proxy.m2();
    }
}

LazyLoader 延迟加载

在这里插入图片描述
案例

MethodInterceptor

在这里插入图片描述

	MethodInterceptor
    public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
                               MethodProxy proxy) throws Throwable;
	InvocationHandler
 	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

这里多了一个 MethodProxy proxy

NoOp

在这里插入图片描述

public class Cglib04 {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Service3.class);
        enhancer.setCallback(NoOp.INSTANCE);
        Service3 proxy = (Service3) enhancer.create();
        System.out.println(proxy.m1());
        System.out.println(proxy.m2());
    }
}

效果等同于没有做任何增强

ProxyRefDispatcher

在这里插入图片描述

案例1:使用MethodInterceptor增强方法处理

public class Service1 {

    public void m1() {
        System.out.println("我是m1方法");
    }

    public void m2() {
        System.out.println("我是m2方法");
    }

}
public class Cglib01 {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Service1.class);
        enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
            System.out.println("调用方法:" + method);
            return methodProxy.invokeSuper(o, objects);
        });
        Service1 proxy = (Service1) enhancer.create();
        proxy.m1();
        proxy.m2();
    }
}
----
调用方法:public void com.example.lurenjia.spring.c15.cglib.Service2.m1()
我是m1方法
调用方法:public void com.example.lurenjia.spring.c15.cglib.Service2.m2()
我是m2方法

案例2:在一个方法内调用另一个方法是否会被拦截?

    public void m1() {
        System.out.println("我是m1方法");
        this.m2();
    }

    public void m2() {
        System.out.println("我是m2方法");
    }
public class Cglib02 {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Service2.class);
        enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
            System.out.println("调用方法:" + method);
            return methodProxy.invokeSuper(o, objects);
        });
        Service2 proxy = (Service2) enhancer.create();
        proxy.m1();
    }
}
----
调用方法:public void com.example.lurenjia.spring.c15.cglib.Service2.m1()
我是m1方法
调用方法:public void com.example.lurenjia.spring.c15.cglib.Service2.m2()
我是m2方法

结论,代理类中一个方法调用另一个方法仍然会进入方法拦截器。

案例3:使用FixedValue拦截并返回固定值

public class Cglib03 {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Service3.class);
        enhancer.setCallback((FixedValue) () -> "abcdefg");
        Service3 proxy = (Service3) enhancer.create();
        System.out.println(proxy.m1());
        System.out.println(proxy.m2());
    }
}
---
abcdefg
abcdefg

案例4:使用NoOp.INSTANCE不做增强处理

public class Service3 {
    public String m1() {
        System.out.println("我是m1方法");
        return "hello:m1";
    }

    public String m2() {
        System.out.println("我是m2方法");
        return "hello:m2";
    }
}
public class Cglib04 {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Service3.class);
        enhancer.setCallback(NoOp.INSTANCE);
        Service3 proxy = (Service3) enhancer.create();
        System.out.println(proxy.m1());
        System.out.println(proxy.m2());
    }
}
----
我是m1方法
hello:m1
我是m2方法
hello:m2

案例5:不同的方法使用不同的拦截器(CallbackFilter)

enhancer.setCallbackFilter 通过 Callback[] 索引位置来决定走哪一个处理器。

public class Service4 {
    public void insert1() {
        System.out.println("我是insert1");
    }

    public void insert2() {
        System.out.println("我是insert2");
    }

    public String get1() {
        System.out.println("我是get1");
        return "get1";
    }

    public String get2() {
        System.out.println("我是get2");
        return "get2";
    }
}
public class Cglib05 {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Service4.class);
        Callback[] callbacks = {
                (MethodInterceptor) (o, method, objects, methodProxy) -> {
                    long starTime = System.nanoTime();
                    Object result = methodProxy.invokeSuper(o, objects);
                    long endTime = System.nanoTime();
                    System.out.println(method + ",耗时(纳秒):" + (endTime - starTime));
                    return result;
                },
                (FixedValue) () -> "fixedValueDefault"
        };
        enhancer.setCallbacks(callbacks);
        enhancer.setCallbackFilter(method -> {
            String methodName = method.getName();
            return methodName.startsWith("insert") ? 0 : 1;
        });
        Service4 proxy = (Service4) enhancer.create();
        System.out.println("---------------");
        proxy.insert1();
        System.out.println("---------------");
        proxy.insert2();
        System.out.println("---------------");
        System.out.println(proxy.get1());
        System.out.println("---------------");
        System.out.println(proxy.get2());
    }
}
----
---------------
我是insert1
public void com.example.lurenjia.spring.c15.cglib.Service4.insert1(),耗时(纳秒):7290600
---------------
我是insert2
public void com.example.lurenjia.spring.c15.cglib.Service4.insert2(),耗时(纳秒):35500
---------------
fixedValueDefault
---------------
fixedValueDefault

案例6:使用CallbackHelper

public class Cglib06 {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        //第一个
        Callback costTimeCallback = (MethodInterceptor) (Object o, Method method, Object[] objects, MethodProxy methodProxy) -> {
            long starTime = System.nanoTime();
            Object result = methodProxy.invokeSuper(o, objects);
            long endTime = System.nanoTime();
            System.out.println(method + ",耗时(纳秒):" + (endTime - starTime));
            return result;
        };
        //第二个
        Callback fixdValueCallback = (FixedValue) () -> "路人甲Java";
        CallbackHelper callbackHelper = new CallbackHelper(Service4.class, null) {
            @Override
            protected Object getCallback(Method method) {
                return method.getName().startsWith("insert") ? costTimeCallback : fixdValueCallback;
            }
        };
        enhancer.setSuperclass(Service4.class);
        enhancer.setCallbacks(callbackHelper.getCallbacks());
        enhancer.setCallbackFilter(callbackHelper);
        Service4 proxy = (Service4) enhancer.create();
        System.out.println("---------------");
        proxy.insert1();
        System.out.println("---------------");
        proxy.insert2();
        System.out.println("---------------");
        System.out.println(proxy.get1());
        System.out.println("---------------");
        System.out.println(proxy.get2());
    }
}

案例7:实现通用的统计任意类方法耗时代理类

public class CostTimeProxy implements MethodInterceptor {
    //目标对象
    private Object target;

    public CostTimeProxy(Object target) {
        this.target = target;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        long starTime = System.nanoTime();
        //调用被代理对象(即target)的方法,获取结果
        Object result;
        result = method.invoke(target, objects);
        // result = methodProxy.invokeSuper(o, objects);
        long endTime = System.nanoTime();
        System.out.println(method + ",耗时(纳秒):" + (endTime - starTime));
        return result;
    }

    /**
     * 创建任意类的代理对象
     *
     * @param target
     * @param <T>
     * @return
     */
    public static <T> T createProxy(T target) {
        CostTimeProxy costTimeProxy = new CostTimeProxy(target);
        Enhancer enhancer = new Enhancer();
        enhancer.setCallback(costTimeProxy);
        enhancer.setSuperclass(target.getClass());
        return (T) enhancer.create();
    }
}

常见问题

method.invoke 原理

jdk动态代理死循环

常见在将 proxy 代理对象传入到 method.invoke中
在这里插入图片描述
method.invoke 一路追踪到一个native方法
在这里插入图片描述

$Proxy0 中的方法

		try {
            super.h.invoke(this, m3, new Object[]{var1});
        } 

由于动态代理生成的对象每一个方法都会进入到 处理器的 invoke 方法,而 method.invoke 每一次执行过一遍这个 invoke 因此形成了死循环。

为什么 jdk 动态代理只能代理接口

public class JdkProxyClass {
    public static void main(String[] args) {
        InvocationHandler h = (proxy1, method, args1) -> {
            return "success";
        };
        //方式一
        A jdkProxy = (A) Proxy.newProxyInstance(A.class.getClassLoader(), new Class[]{A.class}, h);
    }
}

class A {
}
--------
Exception in thread "main" java.lang.IllegalArgumentException: com.example.lurenjia.spring.c15.A is not an interface
	at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:590)
	at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:557)
	at java.lang.reflect.WeakCache$Factory.get(WeakCache.java:230)
	at java.lang.reflect.WeakCache.get(WeakCache.java:127)
	at java.lang.reflect.Proxy.getProxyClass0(Proxy.java:419)
	at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:719)
	at com.example.lurenjia.spring.c15.JdkProxyClass.main(JdkProxyClass.java:18)

在这里插入图片描述
还记得我们获取过的 Proxy0.class 这个文件么,注意这个类继承了 Proxy类。

public final class $Proxy0 extends Proxy implements Msg 

众所周知,java是不支持多重继承的,所以 jdk 动态代理在实现上确定了只能处理接口。

那么在源码中是哪一步继承的 Proxy 呢 ?

为什么 jdk 动态代理生成的代理类要继承 Proxy

试想一下,如果不继承 Proxy 这个类会发生什么呢?

区别

jdk动态代理只能为接口创建代理。
Enhancer既能够代理普通的class,也能够代理接口。
都是在运行期生成字节码。
jdk通过反射机制,cglib通过fastclass机制。

在这里插入图片描述

桥接模式(bridge)

意图、思想

官方:将抽象与抽象方法的实现相互分离来实现解耦,以便二者可以独立的变化。

行为型

模板方法: (Template Method)

意图、思想

原文:在一个方法里实现一个算法,并推迟定义算法中的某些步骤,从而让其他类重新定义它们。
大白话:由抽象父类定义一个方法,子类实现其中的部分或者全部细节。

案例1 喝茶

public abstract class Drink {
    /**
     * 封装四个步骤
     */
    public final void drink() {
        first();
        second();
        third();
        fourth();
    }

    /**
     *
     */
    abstract void second();

    /**
     *
     */
    abstract void third();

    void first() {
        System.out.println("第一步:添水");
    }

    void fourth() {
        System.out.println("第四步:喝掉");
    }
}
public class Coffee extends Drink {

    @Override
    void second() {
        System.out.println("第二步:倒入咖啡、白糖");
    }

    @Override
    void third() {
        System.out.println("第三步:搅拌均匀");
    }

}
public class Tea extends Drink {

    @Override
    void second() {
        System.out.println("第二步:倒入茶叶");
    }

    @Override
    void third() {
        System.out.println("第三步:过滤茶杯");
    }
}
public class Client {
    public static void main(String[] args) {
        Drink coffee = new Coffee();
        coffee.drink();
        System.out.println("-----------");
        Drink tea = new Tea();
        tea.drink();
    }
}
第一步:添水
第二步:倒入咖啡、白糖
第三步:搅拌均匀
第四步:喝掉
-----------
第一步:添水
第二步:倒入茶叶
第三步:过滤茶杯
第四步:喝掉

案例2 购物

案例来自雷丰阳老师Gitee,https://gitee.com/leifengyang/java-design-pattern

public abstract class OrderProcessTemplate {

    private static final Logger logger = LoggerFactory.getLogger(OrderProcessTemplate.class);

    /**
     * 处理订单: 定义好算法骨架
     */
    public final void processOrder(){
        logger.info("开始处理订单");
        //1、选择商品
        doSelect();
        //2、进行支付
        doPayment();
        //3、开具发票
        doReceipt();
        //4、派送商品
        doDelivery();
        logger.info("处理订单结束");
    }


    public abstract void doSelect();
    public abstract void doPayment();
    public abstract void doReceipt();
    public abstract void doDelivery();

}
public class NetOrder extends OrderProcessTemplate {
    @Override
    public void doSelect() {
        System.out.println("把 xiaomi11 加入购物车");
    }

    @Override
    public void doPayment() {
        System.out.println("在线微信支付 1999");
    }

    @Override
    public void doReceipt() {
        System.out.println("发票已经发送给用户邮箱: aaaa@qq.com");
    }

    @Override
    public void doDelivery() {
        System.out.println("顺丰次日达:投送商品");
    }
}
public class StoreOrder extends OrderProcessTemplate {
    @Override
    public void doSelect() {
        System.out.println("用户选择了:3号货架-xiaomi11 商品");
    }

    @Override
    public void doPayment() {
        System.out.println("刷卡机:刷卡支付 1999");
    }

    @Override
    public void doReceipt() {
        System.out.println("打印发票,和物品一起包装");
    }

    @Override
    public void doDelivery() {
        System.out.println("把商品交给用户,用漂亮的袋子");
    }
}
public class Client {
    public static void main(String[] args) {
        //行为型模式玩的就是一个多态
        //1、外界调用模板类【遵循依赖反转原则】【依赖抽象而不是细节】
        OrderProcessTemplate processTemplate = new NetOrder();
        System.out.println("网络订单:");
        //处理订单
        processTemplate.processOrder(); //定义了算法的模板


        processTemplate = new StoreOrder();
        System.out.println("门店订单:");
        processTemplate.processOrder();
    }
}

输出

网络订单:
11:09:42.116 [main] INFO com.example.common.design_mode.template.OrderProcessTemplate - 开始处理订单
把 xiaomi11 加入购物车
在线微信支付 1999
发票已经发送给用户邮箱: aaaa@qq.com
顺丰次日达:投送商品
11:09:42.121 [main] INFO com.example.common.design_mode.template.OrderProcessTemplate - 处理订单结束
门店订单:
11:09:42.121 [main] INFO com.example.common.design_mode.template.OrderProcessTemplate - 开始处理订单
用户选择了:3号货架-xiaomi11 商品
刷卡机:刷卡支付 1999
打印发票,和物品一起包装
把商品交给用户,用漂亮的袋子
11:09:42.121 [main] INFO com.example.common.design_mode.template.OrderProcessTemplate - 处理订单结束

总结

父类是普通类行不行? 不行,因为普通class中不能存在抽象方法(abstract)。
父类是接口行不行? 不行,final和抽象不能共存,子类可以任意实现父接口中的实现,从而改变算法框架。
模板方法的唯一特征是:父类是抽象类。

策略模式

意图、思想

原文:将可互换的方法封装在各自独立的类中,并让每个方法都实现一个公共的操作。
大白话:

为什么要使用策略模式?

答:消除业务逻辑中非常多的if else。具体业务看下面这个例子。

public String getCheckResult(String type) { 
  if ("校验1".equals(type)) { 
    return "执行业务逻辑1"; 
  } else if ("校验2".equals(type)) { 
    return "执行业务逻辑2"; 
  } else if ("校验3".equals(type)) { 
    return "执行业务逻辑3"; 
  } else if ("校验4".equals(type)) { 
    return "执行业务逻辑4"; 
  } else if ("校验5".equals(type)) { 
    return "执行业务逻辑5"; 
  } else if ("校验6".equals(type)) { 
    return "执行业务逻辑6"; 
  } else if ("校验7".equals(type)) { 
    return "执行业务逻辑7"; 
  } else if ("校验8".equals(type)) { 
    return "执行业务逻辑8"; 
  } else if ("校验9".equals(type)) { 
    return "执行业务逻辑9"; 
  } 
  return "不在处理的逻辑中返回业务错误"; 
} 

最简单的实现?

阿里规范对于多if-else的说明?

在这里插入图片描述

卫语句

Replace Nested Conditional with Guard Clauses

示例:来自规范嵩山版

    public void findBoyfriend(Man man) {
        if (man.isUgly()) {
            System.out.println("本姑娘是外貌协会的资深会员");
            return;
        }
        if (man.isPoor()) {
            System.out.println("贫贱夫妻百事哀");
            return;
        }
        if (man.isBadTemper()) {
            System.out.println("银河有多远,你就给我滚多远");
            return;
        }
        System.out.println("可以先交往一段时间看看");
    }

案例1 排序策略

案例来自雷丰阳老师Gitee
https://gitee.com/leifengyang/java-design-pattern/tree/master/strategy-pattern

public interface SortStrategy {

    /**
     * 排序
     */
    void sort(Integer[] arr);
}
public class QuickSortStrategy implements SortStrategy {
    @Override
    public void sort(Integer[] arr) {
        System.out.println("快速排序开始");
        Arrays.sort(arr);
        System.out.println("排序结果:"+Arrays.asList(arr));

    }
}
public class BubbleSortStrategy implements SortStrategy {
    @Override
    public void sort(Integer[] arr) {
        System.out.println("开始冒泡排序....");
        for (int i=0;i< arr.length-1;i++){
            for (int j = 0; j < arr.length- 1 - i  ; j++) {
                if(arr[j] > arr[j+1]){
                    Integer temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
        System.out.println("排序结果:"+ Arrays.asList(arr));

    }
}
public class SortService {
    /**
     * 拿到一个排序算法
     */
    private SortStrategy strategy;

    /**
     * 为了强制要求用户必须传入一个排序算法
     * @param strategy
     */
    public SortService(SortStrategy strategy){
        this.strategy  =strategy;
    }

    /**
     * 随时动态更新排序算法
     * @param strategy
     */
    public void setStrategy(SortStrategy strategy) {
        this.strategy = strategy;
    }

    /**
     * 才是别人调用的排序方法
     * @param arr
     */
    public void sort(Integer[] arr){
        strategy.sort(arr);
    }
}
public class Client {
    public static void main(String[] args) {
        Integer[] arr = new Integer[]{2, 4, 6, 3, 1, 7, 9, 8};

        SortService sortService = new SortService(new BubbleSortStrategy());
        sortService.sort(arr);

        System.out.println("===============");

        //更新策略
        sortService.setStrategy(new QuickSortStrategy());
        sortService.sort(arr);
    }
}

策略模式在 JDK 中的应用?

compare()、ThreadPoolExecutor 拒绝策略RejectedExecutionHandler

观察者(Observer)

意图、思想

官方:在多个对象之间定义一对多的依赖关系,当一个对象的状态发生改变时,会通知依赖它的对象,并根据新状态做出相应的反应。

设计模式的区别

策略模式和状态模式的区别?

状态和策略这两种模式在多态下结构基本类似。

策略和模板方法区别?

实现区别:模板方法只能使用抽象类,策略模式使用接口。
模板定义大的框架,策略用来定义定义小细节。

经典组合

模板+策略

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
工厂模式是一种常用的面向对象设计模式,其主要特点是将对象的创建过程集中到一个工厂类中,由工厂类来负责实例化对象并返回给调用方。在Java中,工厂模式通常分为三种:简单工厂模式、工厂方法模式、抽象工厂模式。 一个经典的工厂模式案例是创建汽车,我们可以定义一个Car接口,定义汽车的基本方法,然后创建各种具体的汽车类实现这个接口。为了将汽车的创建与使用分离开来,我们可以创建一个CarFactory类,负责根据不同的参数创建不同的汽车对象。使用工厂模式,客户可以直接通过CarFactory来获取所需汽车的实例,而不必关心汽车的具体实现细节。 如下是一个简单的工厂模式实现汽车创建的例子: ```JAVA interface Car { void drive(); } class EconomyCar implements Car { public void drive() { System.out.println("drive economy car"); } } class LuxuryCar implements Car { public void drive() { System.out.println("drive luxury car"); } } class CarFactory { public static Car createCar(String carType) { if (carType == null) { return null; } if (carType.equalsIgnoreCase("Economy")) { return new EconomyCar(); } else if (carType.equalsIgnoreCase("Luxury")) { return new LuxuryCar(); } return null; } } public class FactoryPatternDemo { public static void main(String[] args) { Car economyCar = CarFactory.createCar("Economy"); economyCar.drive(); Car luxuryCar = CarFactory.createCar("Luxury"); luxuryCar.drive(); } } ``` 在上述代码中,我们定义了一个Car接口,并分别实现了EconomyCar和LuxuryCar两个具体的汽车类,最后创建了一个CarFactory类,负责根据参数创建不同的Car对象。最后在main函数中,我们使用CarFactory来获取两辆不同的汽车,并调用各自的drive方法。 此外,还有工厂方法模式和抽象工厂模式, 在工厂方法模式中,工厂类将具体类的实例化推迟到子类中实现,而在抽象工厂模式中,工厂类用于创建一系列相关或依赖对象的接口。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值