Android中的设计模式及见解

文章目录

  • 单例模式
    • 饿汉式
    • 懒汉模式
      • 普通懒汉
      • DCL
      • 静态内部类
      • 容器单例模式
    • 构造者模式
    • 工厂模式
    • 责任链模式
    • 外观模式

单例模式

确保某个类有且只有一个对象的场景,避免对象多次创建而浪费资源,其UML图如下。
Android中的设计模式及见解_第1张图片

饿汉式

会在类加载初始化的时候创建一个对象实例,然后在get方法中直接返回该对象。

public class SingleTon1 {

    private  static SingleTon1 mySingleTon = new SingleTon1();
    private SingleTon1(){
        
    }
    public static SingleTon1 getInstance()
    {
        return mySingleTon;
    }
}

懒汉模式

普通懒汉

懒汉模式下会在getInstance()中创建对象实例,保证只有在使用才会创建对象,可以节省资源

为了保证多线程情况下,也要只有一个对象实例,因此对方法上锁;(代表该方法加锁,不管哪一个线程运行到该方法时,都要检查有没有其他线程正在使用,如果有的话就需要等待另一线程运行结束后,再运行;没有的话,锁定调用者,然后直接运行。)但是每调用一次就要进行同步,会造成不必要的同步开销。

public class SingleTon1 {
    private  static SingleTon1 mySingleTon;
    private SingleTon1(){

    }
    public static synchronized  SingleTon1 getInstance()
    {
        if(mySingleTon==null)
            mySingleTon = new SingleTon1();
        return mySingleTon;
    }
}

DCL

DoubleCheckLock方式,两次判空的目的在于第一次为了避免不必要的同步不是null的话就可以直接返回),第二次判空是为了在null情况下载创建对象(加锁是为了避免多线程同时执行getInstance()产生多个single对象)

这里使用volatile的原因在于 new Singleton()其实是三步:(1)为对象分配内存;(2)执行构造函数,初始化成员变量(3)将对象指向分配的内存(此时instance就不是null了),但jvm中允许乱序执行,有可能出现(1)(3)(2)的顺序,那么假如A线程执行了(1)(3),而此时B线程调用getInstance就会返回一个instance对象,但调用上就会出错。而使用volatile禁止进行指令重排序

volatile的作用:

  • 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的
  • 使用volatile禁止进行指令重排序
public class Singleton {

    private Singleton() {}
    private volatile static Singleton instance;//第一层锁:保证变量可见性

    public static Singleton getInstance() {
        if (instance== null) {//第一次判空:无需每次都加锁,提高性能
            synchronized (Singleton.class) {//第二层锁:保证线程同步
                if (instance == null) {//第二次判空:避免多线程同时执行getInstance()产生多个single对象
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

静态内部类

在创建Singleton对象实例时,不会创建sInstance。**在第一次调用getInstance方法时,会导致SingletonHolder被加载,从而sInstance被初始化。**这种方式不但线程安全,同时保证单例对象的唯一性。

public class Singleton { 
    private Singleton(){
    }
      public static Singleton getInstance(){  
        return SingletonHolder.sInstance;  
    }  
    private static class SingletonHolder {  
        private static final Singleton sInstance = new Singleton();  
    }  
} 

容器单例模式

使用SingletonManager将多种的单例类统一管理,用HashMap存储单例的名字与实例的键值对,通过registerService注册实例,之后在使用时根据key获取对象对应类型的对象。这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。

public class SingletonManager { 
  private static Map<String, Object> objMap = new HashMap<String,Object>();
  private Singleton() { 
  }
  public static void registerService(String key, Objectinstance) {
    if (!objMap.containsKey(key) ) {
      objMap.put(key, instance) ;
    }
  }
  public static ObjectgetService(String key) {
    return objMap.get(key) ;
  }
}

构造者模式

定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
Android中的设计模式及见解_第2张图片
例子:OkHttp中的Client和Request的构建都借助了构造者模式。

public final class Request {

  private Request(Builder builder) {
    ......
  }

  public Builder newBuilder() {
    return new Builder(this);
  }

  public static class Builder {   
    private HttpUrl url;
    public Builder() {
      ......
    }
    private Builder(Request request) {
      this.url = request.url;
      ......
    }
    public Builder url(HttpUrl url) {}
    public Builder header(String name, String value) {}
    ......
    public Request build() {
      if (url == null) throw new IllegalStateException("url == null");
      return new Request(this);             
    }
  }
  
}

工厂模式

定义一个用于创建对象的接口,由子类决定实例化哪个类。

图中的工厂模式是比较复杂的,建立在场景中有多个Factory和多个Product的前提下,那么我们要创建一个抽象工厂Factory和一个抽象产品Product。具体可以有多个子工厂Factory,都要重写createProduct方法,生产他们对应的产品Product,也可以有多个子产品Product,都要重写method方法。
Android中的设计模式及见解_第3张图片
实例:在OkHttp中,不论是execute还是enqueue方法都会调用OkHttpClient对象的newCall方法,该方法其实是工厂接口中提供的方法。

  @Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }

一言以蔽之,OkhttpClient实现了Call.Factory接口。虽然这个工厂只有一个产品(RealCall),但是这里用到了工厂模式的中心思想,将搭建复杂对象将的细节交待给RealCall实现,我们只管得到Call即可

public interface Call extends Cloneable {

  Request request();

  Response execute() throws IOException;

  void enqueue(Callback responseCallback);

  void cancel();

  boolean isExecuted();

  boolean isCanceled();

  Call clone();

  interface Factory { // 此处就是上述源码实现Factory接口
    Call newCall(Request request);
  }
}

责任链模式

定义:使多个对象都有机会处理请求,从而降低收发之间的耦合。将这些对象连成一条责任链,沿着链传递该请求,直到被处理。

优点:耦合度低,请求与处理是分开的
缺点:如果链过长会影响性能

实例:(1)View的事件分发。View的点击事件总是从根ViewGroup逐渐向下传递的,直到有View消耗了事件。
Android中的设计模式及见解_第4张图片
(2)OkHttp拦截器
当调用了getResponseWithInterceptorChain之后,会创建一个RealInterceptorChain,调用它的proceed方法,在该方法中会首先调用链中的第一个拦截器,执行其intercept方法(传入新创建的RealInterceptorChain,同时index+1),执行完自身逻辑后,又会调用新的RealInterceptorChain的procedd方法,继续去调用下一个拦截器。这样就将拦截器作为一条责任链调用了,直到调用完最后一个拦截器的方法才会返回response。

public final class RealInterceptorChain implements Interceptor.Chain {
  private final List<Interceptor> interceptors;

  public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
      HttpCodec httpCodec, Connection connection, int index, Request request) {
    ......// 构造函数
  }

  public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      Connection connection) throws IOException {

    //调用责任链中下一个拦截器
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpCodec, connection, index + 1, request);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

    return response;
  }

}

外观模式

外观模式要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。现阶段SDK大概率都会使用外观模式,通过一个外观类使得整个系统的接口只有一个统一的高层接口,降低用户的使用成本外,也对用户隐藏了很多实现细节。例子包括:OkHttp的OkHttpClient,我们只需要使用OkHttpClient就可以实现对网络数据的get与post操作,而不需要知道内部的实现。

使用场景:(1)为一个复杂的子系统提供一个简单接口
(2)如果需要构建一个层次结构的子系统,使用外观模式来定义子系统中每层的入口点。如果子系统之间互相依赖,可以让它们仅通过外观接口进行通信,从而简化了它们之间的依赖关系。

你可能感兴趣的:(Android中的设计模式及见解)