面向对象六大原则

在此以前,有一点须要你们知道,熟悉这些原则并非说你写出的程序就必定灵活、清晰,只是为你的优秀代码之路铺上了一层栅栏,在这些原则的指导下你才能避免陷入一些常见的代码泥沼,从而让你专心写出优秀的东西。
下面咱们就以Android网络框架SimpleNet为例来学习这六大面向对象的基本原则,体会这些原则在开发过程当中带来的强大能量。php

1 单一职责原则

单一职责原则的英文名称是Single Responsibility Principle,简称是SRP,简单地说就是一个类只作一件事。这个设计原则备受争议却又极其重要。只要你想和别人争执、怄气或者是吵架,这个原则是屡试不爽的。由于单一职责的划分界限并非如马路上的行车道那么清晰,不少时候都是须要靠我的经验来界定。固然,最大的问题就是对职责的定义,什么是类的职责,以及怎么划分类的职责。
试想一下,若是你遵照了这个原则,那么你的类就会划分得很细,每一个类都有比较单一的职责,这不就是高内聚、低耦合么!固然,如何界定类的职责这须要你的我的经验了。
在SimpleNet中,我以为很可以体现SRP原则的就是HttpStack这个类族了。HttpStack定义了一个执行网络请求的接口,代码以下:java

public interface HttpStack {
  /**    * 执行Http请求,而且返回一个Response    */ 
  public Response performRequest(Request<?> request);
}


从上述程序中能够看到,HttpStack只有一个performRequest函数,它的职责就是执行网络请求而且返回一个Response。它的职责很单一,这样在须要修改执行网络请求的相关代码时,只须要修改实现HttpStack接口的类,而不会影响其余的类的代码。若是某个类的职责包含有执行网络请求、解析网络请求、进行gzip压缩、封装请求参数等,那么在你修改某处代码时就必须谨慎,以避免修改的代码影响了其余的功能。当你修改的代码可以基本上不影响其余的功能。这就在必定程度上保证了代码的可维护性。注意,单一职责原则并非说一个类只有一个函数,而是说这个类中的函数所作的工做是高度相关的,也就是高内聚。HttpStack抽象了执行网络请求的具体过程,接口简单清晰,也便于扩展。web

优势编程


(1)类的复杂性下降,实现什么职责都有清晰明确的定义。
(2)可读性提升,复杂性下降,那固然可读性提升了。
(3)可维护性提升,可读性提升,那固然更容易维护了。
(4)变动引发的风险下降,变动是必不可少的,若是接口的单一职责作得好,一个接口修改只对相应的实现类有影响,对其余的接口无影响,这对系统的扩展性、维护性都有很是大的帮助。json

 

2 里氏替换原则

面向对象的语言的三大特色是继承、封装、多态,里氏替换原则就是依赖于继承、多态这两大特性。里氏替换原则简单来讲就是全部引用基类、接口的地方必须能透明地使用其子类的对象。通俗点讲,只要父类能出现的地方子类就能够出现,并且替换为子类也不会产生任何错误或异常,使用者可能根本就不须要知道是父类仍是子类。可是,反过来就不行了,有子类出现的地方,父类未必就能适应。
仍是以HttpStack为例,SimpleNet定义了HttpStack来表示执行网络请求这个抽象概念。在执行网络请求时,只须要定义一个HttpStack对象,而后调用performRequest便可,至于HttpStack的具体实现由更高层的调用者指定。这部分代码在RequestQueue类中,示例以下:设计模式

 /**    * @paramcoreNums线程核心数    * @paramhttpStack http执行器    */
  protected RequestQueue(intcoreNums, HttpStackhttpStack) {
    mDispatcherNums = coreNums;
    mHttpStack = httpStack != null ? httpStack : HttpStackFactory.createHttpStack();
  }

 

HttpStackFactory类的createHttpStack函数负责根据API版本建立不一样的HttpStack,实现代码以下:缓存

// 根据API版本选择HttpClient或者HttpURLConnection
public final class HttpStackFactory {
  // API 9
  private static final int GINGERBREAD_SDK_NUM = 9;

  /**    * 根据SDK版本号来建立不一样的Http执行器,即SDK 9以前使用HttpClient,以后则使用HttlUrlConnection    * @return    */
  public static HttpStackcreateHttpStack() {
     intruntimeSDKApi = Build.VERSION.SDK_INT;
     if (runtimeSDKApi>= GINGERBREAD_SDK_NUM) {
      return new HttpUrlConnStack();
    }
    return new HttpClientStack();
  }
}


上述代码中,RequestQueue类中依赖的是HttpStack接口,而经过HttpStackFactory的createHttpStack函数返回的是HttpStack的实现类HttpClientStack或HttlUrlConnStack。这就是所谓的里氏替换原则,任何父类、父接口出现的地方子类均可以出现,这不就保证了可扩展性吗!
任何实现HttpStack接口的类的对象均可以传递给RequestQueue实现网络请求的功能,这样SimpleNet执行网络请求的方法就有不少种可能性,而不是只有HttpClient和HttpURLConnection。例如,用户想使用OkHttp做为SimpleNet的执行引擎,那么建立一个实现了HttpStack接口的OkHttpStack类,而后在该类的performRequest函数中执行网络请求,最终将OkHttpStack对象注入RequestQueue便可。
细想一下,不少应用框架不就是这样实现吗?框架定义一系列相关的逻辑骨架与抽象,使得用户能够将本身的实现注入到框架中,从而实现变化万千的功能。服务器

优势网络


(1)代码共享,减小建立类的工做量,每一个子类都拥有父类的方法和属性。
(2)提升代码的重用性。
(3)提升代码的可扩展性,实现父类的方法就能够“随心所欲”了,不少开源框架的扩展接口都是经过继承父类来完成的。
(4)提升产品或项目的开放性。框架

 

 

缺点


(1)继承是侵入性的。只要继承,就必须拥有父类的全部属性和方法。
(2)下降代码的灵活性。子类必须拥有父类的属性和方法,让子类自由的世界中多了些约束。
(3)加强了耦合性。当父类的常量、变量和方法被修改时,必须要考虑子类的修改,并且在缺少规范的环境下,这种修改可能带来很是糟糕的结果——大片的代码须要重构。

 

 

3 依赖倒置原则

依赖倒置原则这个名字看着有点很差理解,“依赖”还要“倒置”,这究竟是什么意思?依赖倒置原则的几个关键点以下:
(1)高层模块不该该依赖低层模块,二者都应该依赖其抽象。
(2)抽象不该该依赖细节。
(3)细节应该依赖抽象。
在Java语言中,抽象就是指接口或抽象类,二者都是不能直接被实例化的。细节就是实现类、实现接口或继承抽象类而产生的类就是细节,其特色就是能够直接被实例化,也就是能够加上一个关键字 new 产生一个对象。依赖倒置原则在 Java 语言中的表现就是:模块间的依赖经过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是经过接口或抽象类产生的。软件先驱们老是喜欢将一些理论定义得很抽象,弄得不是那么容易理解,其实就是一句话:面向接口编程,或者说是面向抽象编程,这里的抽象指的是接口或者抽象类。面向接口编程是面向对象精髓之一。
采用依赖倒置原则能够减小类间的耦合性,提升系统的稳定性,下降并行开发引发的风险,提升代码的可读性和可维护性。

优势


(1)可扩展性好。
(2)耦合度低。

 

 

4 开闭原则

开闭原则是Java世界里最基础的设计原则,它指导咱们如何创建一个稳定的、灵活的系统。开闭原则的定义是:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。在软件的生命周期内,由于变化、升级和维护等缘由须要对软件原有代码进行修改时,
可能会给旧代码引入错误。所以,当软件须要变化时,咱们应该尽可能经过扩展的方式来实现变化,而不是经过修改已有的代码来实现。
在软件开发过程当中,永远不变的就是变化。开闭原则是使咱们的软件系统拥抱变化的核心原则之一。对扩展开放,对修改关闭这样的高层次的归纳,即在须要对软件进行升级、变化时应该经过扩展的形式来实现,而非修改原有代码。固然这只是一种比较理想的状态,是经过扩展仍是经过修改旧代码须要根据代码自身来定。
在SimpleNet中,开闭原则体现得比较好的是Request类族的设计。咱们知道,在开发C/S应用时,服务器返回的数据格式多种多样,有字符串类型、xml、Json等。而解析服务器返回的Response的原始数据类型则是经过Request类来实现的,这样就使得Request类对于服务器返回的数据格式有良好的扩展性,即Request的可变性太大。
例如,返回的数据格式是Json,那么,使用JsonRequest请求来获取数据,它会将结果转成JsonObject对象,咱们看看JsonRequest的核心实现:

// 返回的数据类型为Json的请求, Json对应的对象类型为JSONObject
public class JsonRequest extends Request<JSONObject> {

  public JsonRequest(HttpMethod method, String url,
     RequestListener<JSONObject> listener) {
     super(method, url, listener);
}

// 将Response的结果转换为JSONObject
@Override
public JSONObjectparseResponse(Response response) {
    String jsonString = new String(response.getRawData());
    try {
      return new JSONObject(jsonString);
    } catch (JSONException e) {
    e.printStackTrace();
    }
    return null;
  }
}


JsonRequest经过实现Request抽象类的parseResponse解析服务器返回的结果,这里将结果转换为JSONObject,而且封装到Response类中。
例如,SimpleNet添加对图片请求的支持,即要实现相似ImageLoader的功能。这个时候个人请求返回的数据是Bitmap图片。所以,我须要在该类型的Request中获得的结果是Request,但支持一种新的数据格式不能经过修改源码的形式,这样可能会为旧代码引入错误,可是,你又必须实现功能扩展。这就是开闭原则的定义:对扩展开放,对修改关闭。咱们看看SimpleNet是如何作的:

public class ImageRequest extends Request<Bitmap> {

  public ImageRequest(HttpMethod method, String url,
    RequestListener<Bitmap> listener) {
    super(method, url, listener);
  }

  // 将数据解析为Bitmap
  @Override
  public Bitmap parseResponse(Response response) {
      return BitmapFactory.decodeByteArray(response.rawData,
       0, response.rawData.length);
  }

}


ImageRequest类的parseResponse函数中将Response中的原始数据转换为Bitmap便可。当咱们须要添加其余数据格式时,只须要继承自Request类,而且在parseResponse方法中将数据转换为具体的形式便可。这样经过扩展的形式来应对软件的变化或者说用户需求的多样性,既避免了破坏原有系统,又保证了软件系统的可扩展性。依赖于抽象,而不依赖于具体,使得对扩展开放,对修改关闭。开闭原则与依赖倒置原则、里氏替换原则同样,实际上最终都遵循一句话:面向接口编程。

优势


(1)增长稳定性。
(2)可扩展性高。

 

 

5 接口隔离原则

客户端不该该依赖它不须要的接口;一个类对另外一个类的依赖应该创建在最小的接口上。根据接口隔离原则,当一个接口太大时,咱们须要将它分割成一些更细小的接口,使用该接口的客户端仅需知道与之相关的方法便可。
可能描述起来不是很好理解,咱们仍是以示例来增强理解吧。
咱们知道,在SimpleNet的网络队列中是会对请求进行排序的。SimpleNet内部使用PriorityBlockingQueue来维护网络请求队列,PriorityBlockingQueue须要调用Request类的compareTo函数来进行排序。试想一下,PriorityBlockingQueue其实只须要调用Request类的排序方法就能够了,其余的接口它根本不须要,即PriorityBlockingQueue只须要compareTo这个接口,而这个compareTo方法就是咱们上述所说的最小接口。固然,compareTo这个方法并非SimpleNet自己定义的接口方法,而是Java中的Comparable接口,但咱们这里只是为了学习,至于哪里定义的可有可无:

public abstract class Request<T> implements Comparable<Request<T>> {

  /**    * 排序方法,PriorityBlockingQueue只须要调用元素的compareTo便可进行排序    */
  @Override
  public intcompareTo(Request<T> another) {
    Priority myPriority = this.getPriority();
    Priority anotherPriority = another.getPriority();
    // 若是优先级相等,那么按照添加到队列的序列号顺序来执行
    return myPriority.equals(anotherPriority) ?this.getSerialNumber()
        - another.getSerialNumber()
        : myPriority.ordinal() - anotherPriority.ordinal();
} 
// 代码省略
}

 

PriorityBlockingQueue类相关代码 :
 

public class PriorityBlockingQueue<E> extends AbstractQueue<E>   implements BlockingQueue<E>, java.io.Serializable {

  // 代码省略

  // 添加元素时进行排序
  public boolean offer(E e) {
    if (e == null)
      throw new NullPointerException();
    final ReentrantLock lock = this.lock;
    lock.lock();
    int n, cap;
    Object[] array;
    while ((n = size) >= (cap = (array = queue).length))
        tryGrow(array, cap);
        try {
           Comparator<? super E>cmp = comparator;
          // 没有设置Comparator,则使用元素自己的compareTo方法进行排序
          if (cmp == null)
             siftUpComparable(n, e, array);
          else
             siftUpUsingComparator(n, e, array, cmp);
          size = n + 1;
          notEmpty.signal();
    } finally {
      lock.unlock();
    }
    return true;
  }

  private static <T> void siftUpComparable(int k, T x, Object[] array) {
    Comparable<? super T> key = (Comparable<? super T>) x;
    while (k > 0) {
    int parent = (k - 1) >>> 1;
      Object e = array[parent];
      // 调用元素的compareTo方法进行排序
      if (key.compareTo((T) e) >= 0)
        break;
      array[k] = e;
      k = parent;
    }
    array[k] = key;
  }
 }


从PriorityBlockingQueue的代码可知,在元素排序时,PriorityBlockingQueue只须要知道元素是个Comparable对象便可,不须要知道这个对象是否是Request类以及这个类的其余接口。它只须要排序,所以,只要知道它是实现了Comparable接口的对象便可,Comparable就是它的最小接口,也是经过Comparable隔离了PriorityBlockingQueue类对Request类的其余方法的可见性。

优势


(1)下降耦合性。
(2)提高代码的可读性。
(3)隐藏实现细节。

 

 

6 迪米特原则

迪米特法则也称为最少知识原则(Least Knowledge Principle),虽然名字不一样,但描述的是同一个原则:一个对象应该对其余对象有最少的了解。通俗地讲,一个类应该对本身须要耦合或调用的类知道得最少,这有点相似接口隔离原则中的最小接口的概念。类的内部如何实现、如何复杂都与调用者或者依赖者没有关系,调用者或者依赖者只须要知道它须要的方法便可,其余的一律不关心。类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另外一个类的影响也越大。
迪米特法则还有一个英文解释是:Only talk to your immedate friends(只与直接的朋友通讯)。什么叫作直接的朋友呢?每一个对象都必然会与其余对象有耦合关系,两个对象之间的耦合就成为朋友关系,这种关系的类型有不少,例如组合、聚合、依赖等。
例如,SimpleNet中的Response缓存接口的设计。

/** * 请求缓存接口 * @param<K> key的类型 * @param<V> value类型 */
public interface Cache<K, V> {
  public V get(K key);
  public void put(K key, V value);
  public void remove(K key);
}


Cache接口定义了缓存类须要实现的最小接口,依赖缓存类的对象只须要知道这些接口便可。例如,须要将Http Response缓存到内存中,而且按照LRU的规则进行存储。咱们须要LruCache类实现这个功能,代码以下: 

// 将请求结果缓存到内存中
public class LruMemCache implements Cache<String, Response> {

  /**    * Reponse LRU缓存    */
  private LruCache<String, Response>mResponseCache;

  public LruMemCache() {
    // 计算可以使用的最大内存
    final intmaxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

    // 取八分之一的可用内存做为缓存
    final intcacheSize = maxMemory / 8;
    mResponseCache = new LruCache<String, Response>(cacheSize) {
      @Override
      protected intsizeOf(String key, Response response) {
        return response.rawData.length / 1024;
      }
    };

  }

  @Override
  public Response get(String key) {
    return mResponseCache.get(key);
  }

  @Override
  public void put(String key, Response response) {
    mResponseCache.put(key, response);
  }

  @Override
  public void remove(String key) {
    mResponseCache.remove(key);
  }
}


在这里,SimpleNet的直接朋友就是Cache或者LruMemCahce,间接朋友就是LruCache类。SimpleNet只须要直接和Cache类交互便可,并不须要知道LruCache的对象的存在,即真正实现缓存功能的对象是LruCache。这就是迪米特原则,尽可能少地知道对象的信息,只与直接的朋友交互。

优势


(1)下降复杂度。
(2)下降耦合度。
(3)增长稳定性。
面向对象六大原则在开发过程当中极为重要,它们给灵活、可扩展的软件系统提供了更细粒度的指导原则。若是可以很好地将这些原则运用到项目中,再在一些合适的场景运用一些通过验证过的设计模式,那么开发出来的软件在必定程度上可以获得质量保证。其实这六大原则最终能够简化为几个关键词:抽象、单一职责、最小化。那么在实际开发过程当中如何权衡、实践这些原则,也是须要你们在工做中不断地思考、摸索。

3 依赖倒置原则

依赖倒置原则这个名字看着有点很差理解,“依赖”还要“倒置”,这究竟是什么意思?依赖倒置原则的几个关键点以下:
(1)高层模块不该该依赖低层模块,二者都应该依赖其抽象。
(2)抽象不该该依赖细节。
(3)细节应该依赖抽象。
在Java语言中,抽象就是指接口或抽象类,二者都是不能直接被实例化的。细节就是实现类、实现接口或继承抽象类而产生的类就是细节,其特色就是能够直接被实例化,也就是能够加上一个关键字 new 产生一个对象。依赖倒置原则在 Java 语言中的表现就是:模块间的依赖经过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是经过接口或抽象类产生的。软件先驱们老是喜欢将一些理论定义得很抽象,弄得不是那么容易理解,其实就是一句话:面向接口编程,或者说是面向抽象编程,这里的抽象指的是接口或者抽象类。面向接口编程是面向对象精髓之一。
采用依赖倒置原则能够减小类间的耦合性,提升系统的稳定性,下降并行开发引发的风险,提升代码的可读性和可维护性。

优势


(1)可扩展性好。
(2)耦合度低。

 

 

4 开闭原则

开闭原则是Java世界里最基础的设计原则,它指导咱们如何创建一个稳定的、灵活的系统。开闭原则的定义是:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。在软件的生命周期内,由于变化、升级和维护等缘由须要对软件原有代码进行修改时,
可能会给旧代码引入错误。所以,当软件须要变化时,咱们应该尽可能经过扩展的方式来实现变化,而不是经过修改已有的代码来实现。
在软件开发过程当中,永远不变的就是变化。开闭原则是使咱们的软件系统拥抱变化的核心原则之一。对扩展开放,对修改关闭这样的高层次的归纳,即在须要对软件进行升级、变化时应该经过扩展的形式来实现,而非修改原有代码。固然这只是一种比较理想的状态,是经过扩展仍是经过修改旧代码须要根据代码自身来定。
在SimpleNet中,开闭原则体现得比较好的是Request类族的设计。咱们知道,在开发C/S应用时,服务器返回的数据格式多种多样,有字符串类型、xml、Json等。而解析服务器返回的Response的原始数据类型则是经过Request类来实现的,这样就使得Request类对于服务器返回的数据格式有良好的扩展性,即Request的可变性太大。
例如,返回的数据格式是Json,那么,使用JsonRequest请求来获取数据,它会将结果转成JsonObject对象,咱们看看JsonRequest的核心实现:

// 返回的数据类型为Json的请求, Json对应的对象类型为JSONObject
public class JsonRequest extends Request<JSONObject> {

  public JsonRequest(HttpMethod method, String url,
     RequestListener<JSONObject> listener) {
     super(method, url, listener);
}

// 将Response的结果转换为JSONObject
@Override
public JSONObjectparseResponse(Response response) {
    String jsonString = new String(response.getRawData());
    try {
      return new JSONObject(jsonString);
    } catch (JSONException e) {
    e.printStackTrace();
    }
    return null;
  }
}


JsonRequest经过实现Request抽象类的parseResponse解析服务器返回的结果,这里将结果转换为JSONObject,而且封装到Response类中。
例如,SimpleNet添加对图片请求的支持,即要实现相似ImageLoader的功能。这个时候个人请求返回的数据是Bitmap图片。所以,我须要在该类型的Request中获得的结果是Request,但支持一种新的数据格式不能经过修改源码的形式,这样可能会为旧代码引入错误,可是,你又必须实现功能扩展。这就是开闭原则的定义:对扩展开放,对修改关闭。咱们看看SimpleNet是如何作的:

public class ImageRequest extends Request<Bitmap> {

  public ImageRequest(HttpMethod method, String url,
    RequestListener<Bitmap> listener) {
    super(method, url, listener);
  }

  // 将数据解析为Bitmap
  @Override
  public Bitmap parseResponse(Response response) {
      return BitmapFactory.decodeByteArray(response.rawData,
       0, response.rawData.length);
  }

}


ImageRequest类的parseResponse函数中将Response中的原始数据转换为Bitmap便可。当咱们须要添加其余数据格式时,只须要继承自Request类,而且在parseResponse方法中将数据转换为具体的形式便可。这样经过扩展的形式来应对软件的变化或者说用户需求的多样性,既避免了破坏原有系统,又保证了软件系统的可扩展性。依赖于抽象,而不依赖于具体,使得对扩展开放,对修改关闭。开闭原则与依赖倒置原则、里氏替换原则同样,实际上最终都遵循一句话:面向接口编程。

优势


(1)增长稳定性。
(2)可扩展性高。

 

 

5 接口隔离原则

客户端不该该依赖它不须要的接口;一个类对另外一个类的依赖应该创建在最小的接口上。根据接口隔离原则,当一个接口太大时,咱们须要将它分割成一些更细小的接口,使用该接口的客户端仅需知道与之相关的方法便可。
可能描述起来不是很好理解,咱们仍是以示例来增强理解吧。
咱们知道,在SimpleNet的网络队列中是会对请求进行排序的。SimpleNet内部使用PriorityBlockingQueue来维护网络请求队列,PriorityBlockingQueue须要调用Request类的compareTo函数来进行排序。试想一下,PriorityBlockingQueue其实只须要调用Request类的排序方法就能够了,其余的接口它根本不须要,即PriorityBlockingQueue只须要compareTo这个接口,而这个compareTo方法就是咱们上述所说的最小接口。固然,compareTo这个方法并非SimpleNet自己定义的接口方法,而是Java中的Comparable接口,但咱们这里只是为了学习,至于哪里定义的可有可无:

public abstract class Request<T> implements Comparable<Request<T>> {

  /**    * 排序方法,PriorityBlockingQueue只须要调用元素的compareTo便可进行排序    */
  @Override
  public intcompareTo(Request<T> another) {
    Priority myPriority = this.getPriority();
    Priority anotherPriority = another.getPriority();
    // 若是优先级相等,那么按照添加到队列的序列号顺序来执行
    return myPriority.equals(anotherPriority) ?this.getSerialNumber()
        - another.getSerialNumber()
        : myPriority.ordinal() - anotherPriority.ordinal();
} 
// 代码省略
}

 

PriorityBlockingQueue类相关代码 :
 

public class PriorityBlockingQueue<E> extends AbstractQueue<E>   implements BlockingQueue<E>, java.io.Serializable {

  // 代码省略

  // 添加元素时进行排序
  public boolean offer(E e) {
    if (e == null)
      throw new NullPointerException();
    final ReentrantLock lock = this.lock;
    lock.lock();
    int n, cap;
    Object[] array;
    while ((n = size) >= (cap = (array = queue).length))
        tryGrow(array, cap);
        try {
           Comparator<? super E>cmp = comparator;
          // 没有设置Comparator,则使用元素自己的compareTo方法进行排序
          if (cmp == null)
             siftUpComparable(n, e, array);
          else
             siftUpUsingComparator(n, e, array, cmp);
          size = n + 1;
          notEmpty.signal();
    } finally {
      lock.unlock();
    }
    return true;
  }

  private static <T> void siftUpComparable(int k, T x, Object[] array) {
    Comparable<? super T> key = (Comparable<? super T>) x;
    while (k > 0) {
    int parent = (k - 1) >>> 1;
      Object e = array[parent];
      // 调用元素的compareTo方法进行排序
      if (key.compareTo((T) e) >= 0)
        break;
      array[k] = e;
      k = parent;
    }
    array[k] = key;
  }
 }


从PriorityBlockingQueue的代码可知,在元素排序时,PriorityBlockingQueue只须要知道元素是个Comparable对象便可,不须要知道这个对象是否是Request类以及这个类的其余接口。它只须要排序,所以,只要知道它是实现了Comparable接口的对象便可,Comparable就是它的最小接口,也是经过Comparable隔离了PriorityBlockingQueue类对Request类的其余方法的可见性。

优势


(1)下降耦合性。
(2)提高代码的可读性。
(3)隐藏实现细节。

 

 

6 迪米特原则

迪米特法则也称为最少知识原则(Least Knowledge Principle),虽然名字不一样,但描述的是同一个原则:一个对象应该对其余对象有最少的了解。通俗地讲,一个类应该对本身须要耦合或调用的类知道得最少,这有点相似接口隔离原则中的最小接口的概念。类的内部如何实现、如何复杂都与调用者或者依赖者没有关系,调用者或者依赖者只须要知道它须要的方法便可,其余的一律不关心。类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另外一个类的影响也越大。
迪米特法则还有一个英文解释是:Only talk to your immedate friends(只与直接的朋友通讯)。什么叫作直接的朋友呢?每一个对象都必然会与其余对象有耦合关系,两个对象之间的耦合就成为朋友关系,这种关系的类型有不少,例如组合、聚合、依赖等。
例如,SimpleNet中的Response缓存接口的设计。

/** * 请求缓存接口 * @param<K> key的类型 * @param<V> value类型 */
public interface Cache<K, V> {
  public V get(K key);
  public void put(K key, V value);
  public void remove(K key);
}


Cache接口定义了缓存类须要实现的最小接口,依赖缓存类的对象只须要知道这些接口便可。例如,须要将Http Response缓存到内存中,而且按照LRU的规则进行存储。咱们须要LruCache类实现这个功能,代码以下: 

// 将请求结果缓存到内存中
public class LruMemCache implements Cache<String, Response> {

  /**    * Reponse LRU缓存    */
  private LruCache<String, Response>mResponseCache;

  public LruMemCache() {
    // 计算可以使用的最大内存
    final intmaxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

    // 取八分之一的可用内存做为缓存
    final intcacheSize = maxMemory / 8;
    mResponseCache = new LruCache<String, Response>(cacheSize) {
      @Override
      protected intsizeOf(String key, Response response) {
        return response.rawData.length / 1024;
      }
    };

  }

  @Override
  public Response get(String key) {
    return mResponseCache.get(key);
  }

  @Override
  public void put(String key, Response response) {
    mResponseCache.put(key, response);
  }

  @Override
  public void remove(String key) {
    mResponseCache.remove(key);
  }
}


在这里,SimpleNet的直接朋友就是Cache或者LruMemCahce,间接朋友就是LruCache类。SimpleNet只须要直接和Cache类交互便可,并不须要知道LruCache的对象的存在,即真正实现缓存功能的对象是LruCache。这就是迪米特原则,尽可能少地知道对象的信息,只与直接的朋友交互。

优势


(1)下降复杂度。
(2)下降耦合度。
(3)增长稳定性。
面向对象六大原则在开发过程当中极为重要,它们给灵活、可扩展的软件系统提供了更细粒度的指导原则。若是可以很好地将这些原则运用到项目中,再在一些合适的场景运用一些通过验证过的设计模式,那么开发出来的软件在必定程度上可以获得质量保证。其实这六大原则最终能够简化为几个关键词:抽象、单一职责、最小化。那么在实际开发过程当中如何权衡、实践这些原则,也是须要你们在工做中不断地思考、摸索。

设计模式

设计模式(Design pattern)是一套被反复使用、多数人知晓的、通过分类编目的、代码设计经验的总结。这个术语是由Erich Gamma等人在1990年从建筑设计领域引入到软件工程领域,今后设计模式在面向对象设计领域逐渐被重视起来。

设计模式并不直接用来完成代码的编写,而是描述在各类状况下要如何解决软件设计问题。面向对象设计模式一般以类或对象来描述其中的关系和相互做用,它们的相互做用可以使软件系统具备高内聚、低耦合的特性,而且使软件可以应对变化。

相关文章
相关标签/搜索