Java面试题(4) – 设计模式

Java面试题(4) – 设计模式

1 说说你知道的设计模式,分别怎么实现?

  • 责任链模式

责任链设计模式将一个请求进行递进处理,从而形成一条链,在链条的节点中处理请求,一个节点处理完自己的逻辑再传递到下一个节点中。在许多开源库中,可以看到责任链模式的使用,例如OkHttp、Spring中的Filter等。在日常开发中,例如登录校验、返回值处理、多个弹框都可以使用责任链模式。

举例说明:员工请假,请假一天需要组长审批,请假三天需要经理审批,请假七天需要老板审批,每个角色可以审批的天数不同,当无权限审批时则转给上级领导。

抽象处理者:领导类

public abstract class Leader {
    private Leader nextLeader;

    public Leader getNextLeader() {
        return nextLeader;
    }

    public void setNextLeader(Leader nextLeader) {
        this.nextLeader = nextLeader;
    }

    public abstract void handlerRequest();
}

具体处理者:组长类

public class GroupLeader extends Leader{
    @Override
    public void handlerRequest(int days) {
        if (days==1){
            System.out.println("组长批准了请假:"+days+"天");
        }else {
            if (getNextLeader()!=null){
                getNextLeader().handlerRequest(days);
            }
        }
    }
}

具体处理者:经理类

public class Manager extends Leader{
    @Override
    public void handlerRequest(int days) {
        if (days<=3){
            System.out.println("经理批准了请假:"+days+"天");
        }else {
            if (getNextLeader()!=null){
                getNextLeader().handlerRequest(days);
            }
        }
    }
}

具体处理者:老板

public class Boss extends Leader{
    @Override
    public void handlerRequest(int days) {
        if (days<=7){
            System.out.println("老板批准了请假:"+days+"天");
        }else {
            if (getNextLeader()!=null){
                getNextLeader().handlerRequest(days);
            }else {
                System.out.println("请假天数太多 不予审批");
            }
        }
    }
}
    public static void main(String[] args) {
        //组装责任链
        GroupLeader groupLeader = new GroupLeader();
        Manager manager = new Manager();
        Boss boss = new Boss();

        groupLeader.setNextLeader(manager);
        manager.setNextLeader(boss);

        //发送请求
        groupLeader.handlerRequest(1);
        groupLeader.handlerRequest(2);
        groupLeader.handlerRequest(5);
    }

输出:

组长批准了请假:1天
经理批准了请假:2天
老板批准了请假:5天

这里分别请假1,2,5天,可以看到请假不同的天数,是由不同审批权限的处理者来处理的。

优点:
降低了对象之间的耦合度。
增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。

缺点:
不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。

  • 适配器模式

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。

我们通过实例来演示适配器模式的使用。其中,音频播放器设备只能播放MP3文件,通过使用一个更高级的音频播放器来播放VLC和MP4文件。我们有一个MediaPlaver接口和一个实现了MediaPlaver接口的实体类AudioPlaver,默认情况下,AudioPlayer可以播放MP3格式的音频文件。我们还有另一个接口AdvancedMediaPlayer和实现了AdvancedMediaPlayer接口的实体类。该类可以播放VLC和MP4格式的文件。我们想要让AudioPlayer播放其他格式的音频文件。为了实现这个功能,我们需要创建一个实现了MediaPlayer接口的适配器类MediaAdapter,并使用AdvancedMediaPlayer对象来播放所需的格式。AudioPlayer使用适配器类MediaAdapter传递所需的音频类型,不需要知道能播放所需格式音频的实际类。AdapterPatternDemo类使用AudioPlayer类来播放各种格式。

为媒体播放器创建MediaPlayer接口:

/**
 * 媒体播放器的接口
 */
public interface MediaPlayer {
    //播放音频文件的方法
    void play(String audioType,String fileName);

}

为高级媒体播放器创建AdvanceMediaPlayer接口:

/**
 * 高级媒体播放器的接口
 */
public interface AdvanceMediaPlayer {
    void playVlc(String fileName);
    void playMp4(String fileName);
}

实现MediaPlayer类:

/**
 * Vlc文件的播放器
 */
public class VlcPlayer implements AdvanceMediaPlayer{

    @Override
    public void playVlc(String fileName) {
        System.out.println("当前正在播放Vlc文件,文件名是"+fileName);
    }

    @Override
    public void playMp4(String fileName) {
        //不用关注
    }
}
/**
 * Mp4文件的播放器
 */
public class Mp4Player implements AdvanceMediaPlayer{

    @Override
    public void playVlc(String fileName) {
        //不用关注此方法
    }

    @Override
    public void playMp4(String fileName) {
        System.out.println("当前正在播放Mp4文件,文件名是"+fileName);
    }
}

编写适配器:

/**
 * 媒体适配器类
 */
public class MediaAdapter implements MediaPlayer{
    //适配器依赖于高级媒体播放器的接口
    private AdvanceMediaPlayer advanceMediaPlayer;

    //对当前的高级播放器做初始化操作
    public MediaAdapter(String audioType) {
        if (audioType.equalsIgnoreCase("vlc")){//播放vlc类型的文件
            advanceMediaPlayer = new VlcPlayer();//多态:向上转型
        }else if (audioType.equalsIgnoreCase("mp4")){
            advanceMediaPlayer = new Mp4Player();
        }
    }

    @Override
    public void play(String audioType, String fileName) {
        //根据当前的文件类型来选择调用哪一个播放器
        if (audioType.equalsIgnoreCase("vlc")){//播放vlc类型的文件
            advanceMediaPlayer.playVlc(fileName);
        }else if (audioType.equalsIgnoreCase("mp4")){
            advanceMediaPlayer.playMp4(fileName);
        }
    }

创建音频文件播放器类:

/**
 * 音频文件播放器类:可以播放mp3文件,如果传递的是mp4和vlc需要调用适配器来进行播放
 */
public class AudioPlayer implements MediaPlayer{
    //这个对象需要进行初始化操作
    private MediaAdapter mediaAdapter;//高级媒体播放的适配器
    @Override
    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("mp3")){
            System.out.println("正在播放mp3文件,文件名称是"+fileName);
        }else if (audioType.equalsIgnoreCase("vlc")||
                audioType.equalsIgnoreCase("mp4")){//调用高级适配器来完成播放
            //创建当前的适配器对应的对象
            mediaAdapter=new MediaAdapter(audioType);
            mediaAdapter.play(audioType,fileName);//桥梁搭建成功
        }else {
            System.out.println("您要播放的文件格式不支持");
        }
    }
}

测试:

public class AdapterPattern {
    public static void main(String[] args) {
        //只能播放mp3类型的对象
        AudioPlayer audioPlayer=new AudioPlayer();
        audioPlayer.play("mp3","后来");
        audioPlayer.play("vlc","故乡");
        audioPlayer.play("mp4","蝙蝠侠");
        audioPlayer.play("avi","遇见");

    }
}
正在播放mp3文件,文件名称是后来
当前正在播放Vlc文件,文件名是故乡
当前正在播放Mp4文件,文件名是蝙蝠侠
您要播放的文件格式不支持

2 单例模式有哪几种,分别怎么实现?

单例模式是一种非常简单的设计模式之一,当我们使用的对象要在全局唯一时就需要用到该模式,以保证对象的唯一性。除此之外,还能避免反复的实例化对象,减少内存开销。

对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;售票时,一共有100张票,可有有多个窗口同时售票,但需要保证不要超售(这里的票数余量就是单例,售票涉及到多线程)。如果不是用机制对窗口对象进行唯一化将弹出多个窗口,如果这些窗口显示的都是相同的内容,重复创建就会浪费资源。

  • 懒汉模式(线程不安全)

    public class SingletonDemo1 {
    private static SingletonDemo1 instance;
    private SingletonDemo1(){}
    public static SingletonDemo1 getInstance(){
        if (instance == null) {
            instance = new SingletonDemo1();
        }
        return instance;
    }
    }
  • 懒汉模式(线程安全)

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

    这种写法在getInstance()方法中加入了synchronized锁。能够在多线程中很好的工作,而且看起来它也具备很好的lazy loading,但是效率很低(因为锁),并且大多数情况下不需要同步。

  • 饿汉模式

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

    这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,这时候初始化instance显然没有达到lazy loading的效果。

  • 饿汉模式(变种)

    public class SingletonDemo4 {
    private static SingletonDemo4 instance = null;
    static {
        instance = new SingletonDemo4();
    }
    private SingletonDemo4(){}
    public static SingletonDemo4 getInstance(){
        return instance;
    }
    }
  • 静态内部类

    public class SingletonDemo5 {
    private static class SingletonHolder{
        private static final SingletonDemo5 instance = new SingletonDemo5();
    }
    private SingletonDemo5(){}
    public static final SingletonDemo5 getInsatance(){
        return SingletonHolder.instance;
    }
    }

    这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,它跟第三种和第四种方式不同的是(很细微的差别):第三种和第四种方式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比第三和第四种方法就显得更合理。

  • 枚举

    public enum SingletonDemo6 {
    instance;
    public void whateverMethod(){
    }
    }

    这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。不过用这种方式写不免让人感觉生疏,在实际工作中,我也很少看见有人这么写过。

  • 双重校验锁

    public class SingletonDemo7 {
    private volatile static SingletonDemo7 singletonDemo7;
    private SingletonDemo7(){}
    public static SingletonDemo7 getSingletonDemo7(){
        if (singletonDemo7 == null) {
            synchronized (SingletonDemo7.class) {
                if (singletonDemo7 == null) {
                    singletonDemo7 = new SingletonDemo7();
                }
            }
        }
        return singletonDemo7;
    }
    }

    这个是第二种方式的升级版,俗称双重检查锁定。

3 你在编码时最常用的设计模式有哪些,在什么场景下用?

单例模式:保证一个类仅有一个实例,并提供一个全局访问点,比如一些配置文件或者管理类可以设计为单例,我们常用的线程池也是单例的。
模板方法:在定义好的算法骨架下,允许子类为一个或多个步骤提供实现,一次性实现算法的不变部分,将可变部分留给子类实现,当子类实现代码逻辑雷同时,可以使用此设计模式。
工厂模式:创建对象需要大量的重复代码时,通过子类实现方法来创建对象。如Spring中通过工厂模式将创建对象的任务交给容器管理。
建造者模式:将复杂对象的构建和表示分离,适用于流程固定,但是顺序不一定固定的场景。如需要给一个对象多次给不同的属性赋值,可以使用链式调用传参,最后生成对象。

4 你能列举一个使用了访问者或者装饰者模式的开源项目吗?

访问者模式将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。Elasticsearch、ShardingJdbc、MybatisPlus等项目使用了这种模式。访问者模式的主要角色:

抽象访问者角色:定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素。
具体访问者角色:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。
抽象元素角色:声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数。
具体元素角色:实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作。
对象结构角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。

装饰者模式在不改变原有对象的基础上,动态的将职责添加到对象上,若要拓展功能,装饰者提供了比继承更有弹性的替代方案。装饰者模式可以在运行时动态的为对象添加职责和功能,更加灵活,也易于拓展。在java.io包里可以发现大量的装饰者模式的运用,以InputStream举例,作为一个抽象组件,有大量的类继承了InputStream来拓展各种功能,选取其中的部分类来看下UML图:

其中ByteArrayInputStream和FileInputStream是可以被装饰者包装起来的具体装饰组件,当然还有部分没有列出来。FilterInputStream虽然是一个普通类,但是它在这里却是一个抽象装饰者,继承这个FilterInputStream的类就是一个个具体的装饰者,可以看到每一个装饰者都有一个参数是顶层抽象组件InputStream的构造方法,也就是说都可以对基础组件进行加工,每一个具体装饰者都具有自己独特的能力去处理这个输入流。看下对于这些装饰者的简单使用,首先是BufferedInputStream

try {
    // FileInputStream 就是一个装饰组件,通过它获取输入流
    InputStream in = new FileInputStream("external-file/test.txt");
    BufferedInputStream bufferedInputStream = new BufferedInputStream(in);
    byte[] bytes = new byte[1024];
    int byteRead;
    while ((byteRead = bufferedInputStream.read(bytes)) != -1) {
        System.out.println(new String(bytes, 0, byteRead));
    }
} catch (Exception e) {
    e.printStackTrace();
}

还有LineInputStream

try {
    InputStream in = new FileInputStream("external-file/test.txt");
    LineInputStream lis = new LineInputStream(in);
    String readLine;
    while ((readLine = lis.readLine()) != null) {
        System.out.println(readLine);
    }
} catch (Exception e) {
    e.printStackTrace();
}

对于一个相同的输入流,经过不同的装饰器加工,处理输入流的方式就根本不一样了,根据实际情况灵活选择需要的装饰器,当然,我们也可以自己去实现一个装饰器。假设我需要一个装饰者,把输入的所有大写字母转换成小写字母:

public class LowerCaseInputStream extends FilterInputStream {

    protected LowerCaseInputStream(InputStream in) {
        super(in);
    }

    @Override
    public int read() throws IOException {
        int c = super.read();
        return (c == -1 ? c : Character.toLowerCase((char) c));
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        int result = super.read(b, off, len);
        for (int i = off; i < off+ result; i++) {
            b[i] = (byte) Character.toLowerCase((char) b[i]);
        }
        return result;
    }
}

创建一个test.txt的文件,里面写上大写字母字符串THIS_IS_A_TEST_LINE,然后来测试下

try {
    InputStream in = new FileInputStream("external-file/test.txt");
    LowerCaseInputStream lowerCaseInputStream = new LowerCaseInputStream(in);
    byte[] bytes = new byte[1024];
    int byteRead;
    while ((byteRead = lowerCaseInputStream.read(bytes)) != -1) {
        System.out.println(new String(bytes, 0, byteRead));
    }
} catch (Exception e) {
    e.printStackTrace();
}
// 输出
this_is_a_test_line

一个自定义的装饰器完成了,没有修改任务源码,只是进行了拓展,并且可以在运行时根据需要灵活选择装饰器。当然,也有不好的地方,装饰者会导致设计中出现许多小对象,如果过度使用,会让程序变得很复杂,不利于维护。

5 如何实现动态和静态代理模式?

代理模式就是为对象提供一个替身,以控制对这个对象的访问,即通过代理对象访问目标对象,这样做的好处是可以在目标对象实现的基础上,增强额外的功能操作( 在调用这个方法前做前置处理,调用这个方法后 做后置处理。),即扩展对象的功能。代理模式可以分为动态和静态代理两种。

静态代理代码示例:

//定义用户操作接口
public interface UserManager {
    void addUser(String userId, String userName);
}
//实现用户操作接口
public class UserManagerImpl implements UserManager {
    @Override
    public void addUser(String userId, String userName) {
        System.out.println("添加用户!!!!!!!");
    }

}
//实现代理对象
public class ProxyUserManager implements UserManager {
    private UserManager userManager;  //目标对象
    //构造器传递目标对象
    public ProxyUserManager(UserManager userManager) {
        this.userManager = userManager;
    }

    @Override
    public void addUser(String userId, String userName) {
        System.out.println("添加用户开始!!!!!!!!!!!!!!!");
        userManager.addUser(userId, userName);
        System.out.println("添加用户结束!!!!!!!!!!!!!!!!!");
    }

}
//测试代理类
public class TestProxyUserManager {

    public static void main(String[] args) {
        UserManager userManager = new ProxyUserManager(new UserManagerImpl());
        userManager.addUser("1","张三");
    }
}

Java实现动态代理实现方式有两种,一种利用JDK反射机制生成代理,另外一种是使用CGLIB代理。JDK代理必须要提供接口,而CGLIB则不需要,可以直接代理类。

JDK动态代理:

public interface People {
    public void sayHello();
}
public class Chinese implements People {

    @Override
    public void sayHello() {
        System.out.println("Chinese say hello.");
    }
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class PeopleInvocationHandler implements InvocationHandler{

    private Object peolple;

    Intermediary(Object people){
        this.people = people;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {        
            Object invoke = method.invoke(people, args);        
            System.out.println("-------- end ---------");
        return invoke;
    }
}
import java.lang.reflect.Proxy;

public class TestPeopleInvocationHandler {
    public static void main(String[] args) {
        People chinese = new People();
        PeopleInvocationHandler invocationHandler = new PeopleInvocationHandler(chinese);
        People proxy = (People) Proxy.newProxyInstance(chinese.getClass().getClassLoader(), chinese.getClass().getInterfaces(), invocationHandler);
        proxy.sayHello();
    }
}

CGLIB动态代理:

public class Chinese {
    public void sayHello(){
        System.out.println("Chinese say hello");
    }
}
import java.lang.reflect.Method;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class ChinesePoxy implements MethodInterceptor {

    @Override
    public Object intercept(Object object, Method method, Object[] args,MethodProxy methodProxy) throws Throwable {
        Object intercept = methodProxy.invokeSuper(object, args);
     System.out.println("-------- end ---------");
    return intercept;   } }
import net.sf.cglib.proxy.Enhancer;

public class TestChineseProxy {
    public static void main(String[] args) {
        ChineseProxy chineseProxy = new ChineseProxy();

        Enhancer enhancer = new Enhancer();  
        enhancer.setSuperclass(Chinese.class);
        enhancer.setCallback(chineseProxy);

        Chinese proxy = (Chinese) enhancer.create();
        proxy.sayHello();
    }
}

6 JDK源码有哪些让你印象深刻的设计模式?

(1)结构性模式
1.适配器模式
java.util.Arrays#asList()
java.io.InputStreamReader(InputStream)
java.io.OutputStreamWriter(OutputStream)
java.xml.bind.annotation.adapters.xmlAdapter#mashal()
java.xml.bind.annotation.adapters.xmlAdapter#mashal()
2.桥接模式(将抽象和抽象的具体实现进行解耦,这样可以使得抽象和抽象的具体实现可以独立进行变化。)
JDBC
3.组合模式(让客户端看起来在处理单个对象和对象的组合是平等的,换句话说,某个类型的方法同时也接受自身类型作为参数。)
Java.util.List#addAll(Collection)
Java.util.Set#addAll(Collection)
Java.util.Map#addAll(Map)
4装饰者模式
Java.io.BufferedInputStream(InputStream)
5.门面模式
java.lang .Class
6.享元模式(使用缓存来减少对小对象的访问时间)
Java.lang.Integer#valueOf(int)
Java.lang.Bollean#valueOf(boolean)
Java.lang.Byte#valueOf(byte)
Java.lang.Character#valueOf(char)
7.代理模式
java.lang.reflect.Proxy
(2)创建模式
8.抽象工厂模式
java.sql.DriverManger#getConnection()
java.sql.connection#createStatement()
java.text.NumberFormat#getInstance()
java.util.Calender#getInstance()
9.建造者模式
Java.lang.StringBuilder#apend()
java.lang.StringBuffer#append()
java.sql.PreparedStatement
10.工厂方法
Java.lang.Proxy#newProxyInstance()
java.lang.Object#toString()
java.lang.Class#newInstance()
java.lang.Class#forName()
Java.lang.Bollean#valueOf(String)
11.原型模式(使得类的实例能够生成自身的拷贝。)
java.lang.Object#clone()
java.lang.Cloneable
12.单例模式
java.langRuntime#getRuntime()
spring中的bean
13.行为模式(通过把请求从一个对象传递到链条中下一个对象的方式来解除对象之间的耦合,直到请求被处理完毕。)
java.util.logging.Logger#log()
javax.servlet.Filter#doFilter()
14.命令模式(将命令包装在对象中,以便可以将其存储,传递到方法中,并像任何其他对象一样返回。)
java.lang.Runnable()
15.解释器模式
Java.text.Format
16.迭代器模式
Java.uil.Iterator
17.中介者模式(通过使用一个中间对象来进行消息分发以及减少类之间的直接依赖。)
Java.util.Timer
java.util.concurrent.Executor#execute()
java.util.concurrent.ExecutorService#submit()
java.lang.reflect.Method#invoke()
18.空对象模式(它允许您抽象空对象的处理。)
java.util.Collections#emptyList()
java.util.Collections#emptyMap()
java.util.Collections#emptySet()
19.观察者模式
java.util.EventListener
java.servlet.http.HttpSessionBindingListener
java.servlet.http.HttpSessionAttributeListener
20.状态模式(允许您在运行时根据内部状态轻松更改对象的行为。)
Java.uil.Iterator
Java.faces.lifecycle.LifeCyele#execute()
21.策略模式(使用这个模式来将一组算法封装成一系列对象)
java.util.Comparator#comoare()
java.servlet.http.HttpServlet
java.servlet.Filter#doFilter()
22.模板方法模式(让子类可以重写方法的一部分,而不是整个重写,你可以控制子类需要重写那些操作。)
Java.util.Collections#sort()
java.io.InoutStream#read()
java.util.AbstractList#indexOf()

7 Spring、Mybatis使用到了那些设计模式?

  • Mybatis

1.Builder模式,例如SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder、CacheBuilder;
2.工厂模式,例如SqlSessionFactory、ObjectFactory、MapperProxyFactory;
3.单例模式,例如ErrorContext和LogFactory;
4.代理模式,Mybatis实现的核心,比如MapperProxy、ConnectionLogger,用的jdk的动态代理;还有executor.loader包使用了cglib或者javassist达到延迟加载的效果;
5.组合模式,例如SqlNode和各个子类ChooseSqlNode等;
6.模板方法模式,例如BaseExecutor和SimpleExecutor,还有BaseTypeHandler和所有的子类例如IntegerTypeHandler;
7.适配器模式,例如Log的Mybatis接口和它对jdbc、log4j等各种日志框架的适配实现;
8.装饰者模式,例如Cache包中的cache.decorators子包中等各个装饰者的实现;
9.迭代器模式,例如迭代器模式PropertyTokenizer;

  • Spring

1.简单工厂模式 ,如BeanFactory
2.工厂方法模式 ,通常由应用程序直接使用new创建新的对象,为了将对象的创建和使用相分离,采用工厂模式,即应用程序将对象的创建及初始化职责交给工厂对象。
应用程序有自己的工厂对象来创建bean.如果将应用程序自己的工厂对象交给Spring管理,那么Spring管理的就不是普通的bean,而是工厂Bean。 Spring中的FactoryBean就是典型的工厂方法模式。
3.单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
Spring下默认的bean均为singleton,可以通过singleton=“true|false” 或者 scope="?"来指定。
4.代理模式
在Spring的Aop中,使用的Advice(通知)来增强被代理类的功能。Spring实现这一AOP功能的原理就使用代理模式(1、JDK动态代理。 2、CGLib字节码生成技术代理。)对类进行方法级别的切面增强,即,生成被代理类的代理类, 并在代理类的方法前,设置拦截器,
通过执行拦截器重的内容增强了代理方法的功能,实现的面向切面编程。
5.观察者模式
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
spring中Observer模式常用的地方是listener的实现。如ApplicationListener。
6.策略模式
定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
spring中在实例化对象的时候用到Strategy模式
7.模板方法模式
spring中的JdbcTemplate。
8.包装器模式
Spring中用到的包装器模式在类名上有两种表现:一种是类名中含有Wrapper,另一种是类名中含有Decorator。基本上都是动态地给一个对象添加一些额外的职责。
9.适配器模式
实现方式:SpringMVC中的适配器HandlerAdatper。
实现原理:HandlerAdatper根据Handler规则执行不同的Handler。
实现过程:
DispatcherServlet根据HandlerMapping返回的handler,向HandlerAdatper发起请求,处理Handler。HandlerAdapter根据规则找到对应的Handler并让其执行,
执行完毕后Handler会向HandlerAdapter返回一个ModelAndView,最后由HandlerAdapter向DispatchServelet返回一个ModelAndView。
实现意义:
HandlerAdatper使得Handler的扩展变得容易,只需要增加一个新的Handler和一个对应的HandlerAdapter即可。因此Spring定义了一个适配接口,使得每一种Controller
有一种对应的适配器实现类,让适配器代替controller执行相应的方法。这样在扩展Controller时,只需要增加一个适配器类就完成了SpringMVC的扩展了。

参考
https://blog.csdn.net/weixin_45864705/article/details/127279142
http://www.360doc.com/content/22/0729/10/10087950_1041821804.shtml

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注