Java 常用设计模式

单例模式

单例模式用于确保一个类只有一个实例。有两种常见的实现方式:懒汉模式(Lazy Initialization)和饿汉模式(Eager Initialization),主要区别在实例化的时机和线程安全性方面。

懒汉

在第一次获取单例实例时才进行实例化,如果没有使用到单例,那么单例实例就不会被创建,实现了延迟加载。

懒汉模式在多线程环境下需要考虑线程安全性,需要使用 synchronized 关键字或者双重检查锁定(Double-Checked Locking)来保证线程安全。

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

饿汉

在类加载的时候就进行实例化,一直存在于内存中,不管是否被调用。饿汉模式天生线程安全,因为在类加载的过程中就已经创建了单例实例,不存在竞态条件。

public class Singleton {
    private static Singleton instance = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() {
        return instance;
    }
}

选择那种方式?

如果对象实例比较简单,不需要耗费多少资源和时间,直接使用恶汉模式即可;

如果对象实例较复杂,并且可能阻塞主线程,那么应该使用懒汉模式。例如创建一个Http客户端时,对象复杂,耗时长,可能因为连接超时问题阻塞主线程。

代理模式

代理模式用于控制对对象的访问,为一个对象提供代理以控制对这个对象的访问。代理对象持有对真实对象的引用,可以在访问真实对象时进行控制和扩展。

适用于需要在访问真实对象时添加额外的逻辑处理,比如日志记录、权限校验等。Spring AOP就是基于动态代理实现的,默认使用JDK动态代理方案。

代理分为静态代理和动态代理

静态代理就是手动创建一个代理对象,然后内部包含一个真实对象。

更常用的是动态代理,即运行时动态的进行代理,有两种实现方案

JDK动态代理

Java自带的代理机制,基于接口实现,只能代理实现了接口的类。原理是利用反射机制,动态生成匿名类实现要代理的接口,并且继承Proxy类,由于java不支持多继承,所以JDK动态代理不能代理类。

在匿名类内部,使用反射机制调用真实对象目标方法,在JDK7、8之前的版本存在性能问题。

/**代理接口*/
public interface IHello {
    String hi(String key);
}

/**代理接口实现类*/
public class HelloImpl implements IHello {
    @Override
    public String hi(String key) {
        String str = "hello:" + key;
        System.out.println("HelloImpl! " + str);
        return str;
    }
}


/**jdk动态代理类*/
public class JdkProxy implements InvocationHandler {

    private Object target;

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

    /**
     * 获取被代理接口实例对象
     *
     * @param <T>
     * @return
     */
    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(">>> JdkProxy start");
        Object result = method.invoke(target, args);
        System.out.println(">>> JdkProxy end");
        return result;
    }
}

/**测试*/
public class Demo {

    public static void main(String[] args) {
        JdkProxy proxy = new JdkProxy(new HelloImpl());
        IHello helloProxy = proxy.getProxy();
        helloProxy.hi(" jdk proxy !");
    }
}

Cglib动态代理

基于字节码技术实现的代理机制,可以代理没有实现接口的类,可以对类的任意方法进行代理,需要导入cglib库。

原理是通过继承目标类或者通过目标类的接口生成子类,没有使用反射机制,性能相对较高。

final类无法被继承,所以无法被代理

引入cglib依赖

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib-nodep</artifactId>
    <version>3.1.0</version>
</dependency>

代理示例

/**目标类*/
public class ServiceImpl {
    public void doSomething() {
        System.out.println("do something");
    }
}

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
/**cglib代理类*/
public class CglibProxy {
    public static  <T> T  getProxy(Class<?> clazz) {
        // 创建一个 Enhancer 对象。
        Enhancer enhancer = new Enhancer();

        // 设置要代理的类。
        enhancer.setSuperclass(clazz);

        // 设置代理逻辑。
        enhancer.setCallback((MethodInterceptor) (o, method, args, methodProxy) -> {
            System.out.println("before do something");

            // 调用原始对象的方法。
            Object result = methodProxy.invokeSuper(o, args);

            System.out.println("after do something");

            return result;
        });

        // 创建代理对象。
        return (T) enhancer.create();
    }
}

/**测试*/
public class Main {
    public static void main(String[] args) throws Exception {
        // 创建一个 Service 对象。
        ServiceImpl service = new ServiceImpl();
        // 创建一个代理对象。
        ServiceImpl proxy = CglibProxy.getProxy(service.getClass());
        // 通过代理对象调用方法。
        // proxy.getClass().getMethod("doSomething").invoke(proxy);
        proxy.doSomething();
    }
}

装饰器模式

装饰器模式用于增强对象的功能,可以向一个现有对象添加新功能,同时又不改变其结构。

具体应用

一个比较具体的应用实例是在使用Spring中的ThreadPoolTaskExecutor中,通过实现一个上下文装饰器,可以将主线程中的信息复制到线程池里面的线程。

ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setTaskDecorator(new ContextDecorator());

// ContextDecorator
public class ContextDecorator implements TaskDecorator {

    @Override
    public Runnable decorate(Runnable runnable) {
        RequestAttributes context;
        try{
            context = RequestContextHolder.currentRequestAttributes();
        } catch (IllegalStateException e) {
            context = new NonWebRequestAttributes();
        }

        RequestAttributes finalContext = context;
        return () -> {
            try {
                RequestContextHolder.setRequestAttributes(finalContext);
                runnable.run();
            } finally {
                RequestContextHolder.resetRequestAttributes();
            }
        };
    }
}

装饰器示例

public interface Pizza {
    public String getDescription();
    public double getCost();
}
 // 具体组件
public class PlainPizza implements Pizza {
    public String getDescription() {
        return "薄饼";
    }
    public double getCost() {
        return 4.00;
    }
}
 // 装饰器
public abstract class ToppingDecorator implements Pizza {
    protected Pizza pizza;
    public ToppingDecorator(Pizza pizza) {
        this.pizza = pizza;
    }
    public String getDescription() {
        return pizza.getDescription();
    }
    public double getCost() {
        return pizza.getCost();
    }
}
 // 具体装饰器
public class Cheese extends ToppingDecorator {
    public Cheese(Pizza pizza) {
        super(pizza);
    }
    public String getDescription() {
        return pizza.getDescription() + ",马苏里拉奶酪";
    }
    public double getCost() {
        return pizza.getCost() + 0.50;
    }
}

策略模式

策略模式是一种行为设计模式,它可以定义一系列算法,然后再将每个算法封装起来。在运行时,根据需要选择相应的算法来完成任务。

结构

  • Context(环境类):维护一个对策略对象的引用,在必要时会调用具体的策略对象来完成任务。
  • Strategy(策略接口):定义了算法的统一接口,具体的算法实现类都实现了该接口。
  • ConcreteStrategy(具体策略类):具体的算法实现类,实现了策略接口中定义的算法。

工作原理

  • 环境类通过持有策略接口的引用,可以动态切换不同的具体策略类。
  • 客户端在创建环境类时可以选择不同的具体策略类来传入,从而实现不同的算法行为

示例

// 定义一个排序的策略接口
interface SortStrategy {
    void sort(int[] arr);
}

// 具体策略类 - 快速排序
class QuickSortStrategy implements SortStrategy {
    @Override
    public void sort(int[] arr) {
        // 使用快速排序算法进行排序
    }
}

// 具体策略类 - 冒泡排序
class BubbleSortStrategy implements SortStrategy {
    @Override
    public void sort(int[] arr) {
        // 使用冒泡排序算法进行排序
    }
}

// 包装类
class SortContext {
    private SortStrategy strategy;

    public SortContext(SortStrategy strategy) {
        this.strategy = strategy;
    }

    public void sortArray(int[] arr) {
        strategy.sort(arr);
    }
}

// 客户端代码
class Demo {
    public static void main(String[] args) {
        int[] array = {5, 2, 8, 4, 1};
        
        SortContext context = new SortContext(new QuickSortStrategy());
        context.sortArray(array); // 使用快速排序算法排序
        
        context = new SortContext(new BubbleSortStrategy());
        context.sortArray(array); // 使用冒泡排序算法排序
    }
}
posted @   cd_along  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示