Android面试整理-设计模式

单例模式

答案参考自:

意图: 保证一个类仅有一个实例,并提供一个访问它的全局访问点。

关键代码: 构造函数是私有的。

优点:

  1. 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。

  2. 避免对资源的多重占用(比如写文件操作)。

实现代码

  1. 饿汉式

    优点:没有加锁,执行效率会提高。
    缺点:类加载时就初始化,浪费内存。

    1
    2
    3
    4
    5
    6
    7
    public class Singleton {  
    private static Singleton instance = new Singleton();
    private Singleton (){}
    public static Singleton getInstance() {
    return instance;
    }
    }
  2. DCL

    优点:资源利用率高。

    缺点:第一次加载的时候反应稍慢。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class Singleton {  
    private volatile static Singleton singleton;
    private Singleton () {}
    public static Singleton getSingleton() {
    if (singleton == null) {
    synchronized (Singleton.class) {
    if (singleton == null) {
    singleton = new Singleton();
    }
    }
    }
    return singleton;
    }
    }

    使用volatile是为了防止指令重排序问题,避免第8行代码被分配了控件但是值为null的情况。

    两次 if 语句的作用:

    • 第一个 if 语句

      防止在变量实例化后仍有线程获取锁,造成性能的开销。

    • 第二个 if 语句

      防止变量的重复实例化。

  3. 静态内部类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class Singleton {
    private Singleton() {}
    public static Singleton getInstance() {
    return SingletonHolder.instance;
    }
    private static class SingletonHolder {
    private static final Singleton instance = new Singleton();
    }
    }

    第一次加载Singleton类的时候并不会初始化instance,只有在第一次调用Singleton.getInstance方法时才会初始化instance。因此第一次调用getInstance方法时也会导致虚拟机加载SingletonHolder类,这种方式不仅确保了线程安全,也能够保证单例模式对象的唯一性,同时也延迟了单例的实例化。

Android源码中的单例模式

LayoutInflater

ContextImpl类中,LayoutInflater的获取被写进了该类的static{},即静态语句块中。表示了只有在该类被加载的时候才会被执行一次且只会执行一次,保证了实例的唯一性。

建造者模式

答案参考自:

意图: 将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。

何时使用: 一些基本部件不会变,而其组合经常变化的时候。

优点:

  1. 建造者独立,易扩展。

  2. 便于控制细节风险。

缺点: 产品必须有共同点,范围有限制。

注意事项: 与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。

用例:

调用者不需要知道Director类中方法的具体实现,而Director类同样也不需要知道Builder类中的具体实现。

当我们需求发生变动时,只需要单独修改Director或者Builder类的内部细节即可,不会影响到其他类的实现。

Android中的建造者模式

  1. JavaStringBuilder
  2. OkHttp中的OkHttpClient
  3. Android中的AlterDialog

责任链模式

答案参考自:

在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

意图: 避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

主要解决: 职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。

何时使用: 在处理消息的时候需要过滤很多道。

优点:

  1. 降低耦合度。它将请求的发送者和接收者解耦。

  2. 简化了对象。使得对象不需要知道链的结构。

  3. 增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。

  4. 增加新的请求处理类很方便。

缺点:

  1. 不能保证请求一定被接收。

  2. 系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。

  3. 可能不容易观察运行时的特征,有碍于除错。

Android中类似责任链模式的使用

  1. OkHttp的拦截器。
  2. View事件分发的dispatchTouchEvent以及dispatchTransformedTouchEvent方法。

观察者模式

答案参考自:

定义一个被观察者和多个观察者,每当被观察者变化,所有观察者都会得到通知。

要点: P用一个list把观察者保存起来,并提供add和remove观察者,在被观察者变化的时候就遍历并调用list里观察者的方法。核心就是一个list遍历

优点:

  1. 观察者和被观察者解耦

  2. 增强灵活性,解耦

缺点: 由于java代码的顺序执行,要考虑被察者的执行效率,多个观察者需要及时响应就得考虑异步的问题了。

Android源码中的观察者模式

  1. RecyclerViewnotifyDataChanged方法。

    当数据集发生变化,调用RecyclerView.Adapter的notifyDataSetChanged()时,发生变化的地方就会刷新,这个过程就涉及到了Java中的观察者模式。

    在RecyclerView的使用中,最重要的两个类应该就是RecyclerView和RecyclerView.Adapter了。

    RecyclerView内部维持了一个Observer(RecyclerViewDataObserver)。

    Adapter内部维持了一个Observable(AdapterDataObservable)。

    我们调用setAdapter(Adapter adapter)时,Adapter内的Observable会持有RecyclerView内部的Observer;当调用notifyDataSetChanged()时,Observable持有的Observer会依次调用onChanged()方法,而RecyclerView持有的Observer的onChanged()方法中实现了UI刷新。


Android面试整理-设计模式
https://luoyuy.top/posts/ad1cccdb201a/
作者
LuoYu-Ying
发布于
2022年5月26日
许可协议