设计摸式-面向对象的六大原则
- 单一职责原则
- 开闭原则
- 里氏替换原则
- 依赖倒置原则
- 接口隔离原则
- 迪米特原则
本文对上面的面试对象的六大原则,做逐步了解记录,并结合真实项目与Android源码进行分析,那么开工了,兄弟们靠勤劳双手致富吧!
一 单一职责原则
单一职责原则(Single Responsibility Principle),缩写SRP
概述:就一个类而言,应该仅有一个引起它变化的原因.简单理解,一个类中应该是一组相关性很高的函数,数据的封装
以RxVolley为例,打开源码看client文件下的FileRequest,FormRequest,JSonRequest3个请求,从下面3个类我们能够体会,单一职责的实现,两个完全不一样的功能分开抽取到不同的类中,一个类中应该是一组相关性很高的方法,数据的封装.而我们开发则需要根据具体的业务,功能对类型进行相应的拆分,这也是程序猿优化代码迈出的第一步.
FileRequest源码
/*
* Copyright (c) 2014,
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kymjs.rxvolley.client;
import android.text.TextUtils;
import com.kymjs.rxvolley.http.HttpHeaderParser;
import com.kymjs.rxvolley.http.NetworkResponse;
import com.kymjs.rxvolley.http.Request;
import com.kymjs.rxvolley.http.Response;
import com.kymjs.rxvolley.http.URLHttpResponse;
import com.kymjs.rxvolley.http.VolleyError;
import com.kymjs.rxvolley.rx.Result;
import com.kymjs.rxvolley.toolbox.HttpParamsEntry;
import com.kymjs.rxvolley.toolbox.Loger;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.zip.GZIPInputStream;
/**
* 请求文件方法类
*
* @author kymjs (http://www.kymjs.com/) .
*/
public class FileRequest extends Request<byte[]> {
private final File mStoreFile;
private final File mTemporaryFile; // 临时文件
private ArrayList<HttpParamsEntry> mHeaders = new ArrayList<>();
public FileRequest(String storeFilePath, RequestConfig config, HttpCallback callback) {
super(config, callback);
mStoreFile = new File(storeFilePath);
File folder = mStoreFile.getParentFile();
if (folder != null && folder.mkdirs()) {
if (!mStoreFile.exists()) {
try {
mStoreFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
mTemporaryFile = new File(storeFilePath + ".tmp");
}
public File getStoreFile() {
return mStoreFile;
}
public File getTemporaryFile() {
return mTemporaryFile;
}
@Override
public String getCacheKey() {
return "";
}
@Override
public boolean shouldCache() {
return false;
}
@Override
public Response<byte[]> parseNetworkResponse(NetworkResponse response) {
String errorMessage = null;
if (!isCanceled()) {
if (mTemporaryFile.canRead() && mTemporaryFile.length() > 0) {
if (mTemporaryFile.renameTo(mStoreFile)) {
return Response.success(response.data, response.headers,
HttpHeaderParser.parseCacheHeaders(getConfig().mUseServerControl,
getConfig().mCacheTime, response));
} else {
errorMessage = "Can't rename the download temporary file!";
}
} else {
errorMessage = "Download temporary file was invalid!";
}
}
if (errorMessage == null) {
errorMessage = "Request was Canceled!";
}
return Response.error(new VolleyError(errorMessage));
}
@Override
public ArrayList<HttpParamsEntry> getHeaders() {
mHeaders.add(new HttpParamsEntry("Range", "bytes=" + mTemporaryFile.length() + "-"));
mHeaders.add(new HttpParamsEntry("Accept-Encoding", "identity"));
return mHeaders;
}
public ArrayList<HttpParamsEntry> putHeader(String k, String v) {
mHeaders.add(new HttpParamsEntry(k, v));
return mHeaders;
}
public static boolean isSupportRange(URLHttpResponse response) {
if (TextUtils.equals(getHeader(response, "Accept-Ranges"), "bytes")) {
return true;
}
String value = getHeader(response, "Content-Range");
return value != null && value.startsWith("bytes");
}
public static String getHeader(URLHttpResponse response, String key) {
return response.getHeaders().get(key);
}
public static boolean isGzipContent(URLHttpResponse response) {
return TextUtils.equals(getHeader(response, "Content-Encoding"), "gzip");
}
public byte[] handleResponse(URLHttpResponse response) throws IOException {
long fileSize = response.getContentLength();
if (fileSize <= 0) {
Loger.debug("Response doesn't present Content-Length!");
}
long downloadedSize = mTemporaryFile.length();
boolean isSupportRange = isSupportRange(response);
if (isSupportRange) {
fileSize += downloadedSize;
String realRangeValue = response.getHeaders().get("Content-Range");
if (!TextUtils.isEmpty(realRangeValue)) {
String assumeRangeValue = "bytes " + downloadedSize + "-" + (fileSize - 1);
if (TextUtils.indexOf(realRangeValue, assumeRangeValue) == -1) {
Loger.debug("The Content-Range Header is invalid Assume["
+ assumeRangeValue + "] vs Real["
+ realRangeValue + "], "
+ "please remove the temporary file ["
+ mTemporaryFile + "].");
}
}
}
if (fileSize > 0 && mStoreFile.length() == fileSize) {
mStoreFile.renameTo(mTemporaryFile);
if (mProgressListener != null)
mRequestQueue.getDelivery().postProgress(mProgressListener,
fileSize, fileSize);
return null;
}
RandomAccessFile tmpFileRaf = new RandomAccessFile(mTemporaryFile, "rw");
if (isSupportRange) {
tmpFileRaf.seek(downloadedSize);
} else {
tmpFileRaf.setLength(0);
downloadedSize = 0;
}
InputStream in = response.getContentStream();
try {
if (isGzipContent(response) && !(in instanceof GZIPInputStream)) {
in = new GZIPInputStream(in);
}
byte[] buffer = new byte[6 * 1024]; // 6K buffer
int offset;
while ((offset = in.read(buffer)) != -1) {
tmpFileRaf.write(buffer, 0, offset);
downloadedSize += offset;
//下载进度回调
if (mProgressListener != null)
mRequestQueue.getDelivery().postProgress(mProgressListener,
downloadedSize, fileSize);
if (isCanceled()) {
break;
}
}
} finally {
if (in != null) {
in.close();
}
try {
response.getContentStream().close();
} catch (Exception e) {
Loger.debug("Error occured when calling consumingContent");
}
tmpFileRaf.close();
}
return null;
}
@Override
public Priority getPriority() {
return Priority.LOW;
}
@Override
protected void deliverResponse(ArrayList<HttpParamsEntry> headers, byte[] response) {
HashMap<String, String> map = new HashMap<>(headers.size());
for (HttpParamsEntry entry : headers) {
map.put(entry.k, entry.v);
}
if (response == null) response = new byte[0];
if (mCallback != null) {
mCallback.onSuccess(map, response);
}
getConfig().mSubject.onNext(new Result(getUrl(), response, map));
}
}
FormRequest源码
/*
* Copyright (c) 2014, 张涛.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kymjs.rxvolley.client;
import com.kymjs.rxvolley.RxVolley;
import com.kymjs.rxvolley.http.HttpHeaderParser;
import com.kymjs.rxvolley.http.NetworkResponse;
import com.kymjs.rxvolley.http.Request;
import com.kymjs.rxvolley.http.Response;
import com.kymjs.rxvolley.rx.Result;
import com.kymjs.rxvolley.toolbox.HttpParamsEntry;
import com.kymjs.rxvolley.toolbox.Loger;
import java.io.ByteArrayOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
/**
* Form表单形式的Http请求
*
* @author kymjs(http://www.kymjs.com)
*/
public class FormRequest extends Request<byte[]> {
private final HttpParams mParams;
public FormRequest(RequestConfig config, HttpParams params, HttpCallback callback) {
super(config, callback);
if (params == null) {
params = new HttpParams();
}
this.mParams = params;
}
@Override
public String getCacheKey() {
if (getMethod() == RxVolley.Method.POST) {
return getUrl() + mParams.getUrlParams();
} else {
return getUrl();
}
}
@Override
public String getBodyContentType() {
if (mParams.getContentType() != null) {
return mParams.getContentType();
} else {
return super.getBodyContentType();
}
}
@Override
public ArrayList<HttpParamsEntry> getHeaders() {
return mParams.getHeaders();
}
@Override
public byte[] getBody() {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
if (mProgressListener != null) {
mParams.writeTo(new CountingOutputStream(bos, mParams.getContentLength(),
mProgressListener));
} else {
mParams.writeTo(bos);
}
} catch (IOException e) {
Loger.debug("FormRequest#getBody()--->IOException writing to ByteArrayOutputStream");
}
return bos.toByteArray();
}
@Override
public Response<byte[]> parseNetworkResponse(NetworkResponse response) {
return Response.success(response.data, response.headers,
HttpHeaderParser.parseCacheHeaders(getUseServerControl(), getCacheTime(),
response));
}
@Override
protected void deliverResponse(ArrayList<HttpParamsEntry> headers, final byte[] response) {
final HashMap<String, String> map = new HashMap<>(headers.size());
for (HttpParamsEntry entry : headers) {
map.put(entry.k, entry.v);
}
if (mCallback != null) {
mCallback.onSuccess(map, response);
}
getConfig().mSubject.onNext(new Result(getUrl(), response, map));
}
@Override
public Priority getPriority() {
return Priority.IMMEDIATE;
}
public static class CountingOutputStream extends FilterOutputStream {
private final ProgressListener progListener;
private long transferred;
private long fileLength;
public CountingOutputStream(final OutputStream out, long fileLength,
final ProgressListener listener) {
super(out);
this.fileLength = fileLength;
this.progListener = listener;
this.transferred = 0;
}
public void write(int b) throws IOException {
out.write(b);
if (progListener != null) {
this.transferred++;
if ((transferred % 20 == 0) && (transferred <= fileLength)) {
RxVolley.getRequestQueue().getDelivery().postProgress(this.progListener,
this.transferred, fileLength);
}
}
}
}
}
JsonRequest源码
/*
* Copyright (c) 2014,
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kymjs.rxvolley.client;
import com.kymjs.rxvolley.RxVolley;
import com.kymjs.rxvolley.http.HttpHeaderParser;
import com.kymjs.rxvolley.http.NetworkResponse;
import com.kymjs.rxvolley.http.Request;
import com.kymjs.rxvolley.http.Response;
import com.kymjs.rxvolley.rx.Result;
import com.kymjs.rxvolley.toolbox.HttpParamsEntry;
import com.kymjs.rxvolley.toolbox.Loger;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
/**
* 用来发起application/json格式的请求的,我们平时所使用的是form表单提交的参数,而使用JsonRequest提交的是json参数。
*/
public class JsonRequest extends Request<byte[]> {
private final String mRequestBody;
private final HttpParams mParams;
public JsonRequest(RequestConfig config, HttpParams params, HttpCallback callback) {
super(config, callback);
mRequestBody = params.getJsonParams();
mParams = params;
}
@Override
public ArrayList<HttpParamsEntry> getHeaders() {
return mParams.getHeaders();
}
@Override
protected void deliverResponse(ArrayList<HttpParamsEntry> headers, byte[] response) {
HashMap<String, String> map = new HashMap<>(headers.size());
for (HttpParamsEntry entry : headers) {
map.put(entry.k, entry.v);
}
if (mCallback != null) {
mCallback.onSuccess(map, response);
}
getConfig().mSubject.onNext(new Result(getUrl(), response, map));
}
@Override
public Response<byte[]> parseNetworkResponse(NetworkResponse response) {
return Response.success(response.data, response.headers,
HttpHeaderParser.parseCacheHeaders(getUseServerControl(), getCacheTime(),
response));
}
@Override
public String getBodyContentType() {
return String.format("application/json; charset=%s", getConfig().mEncoding);
}
@Override
public String getCacheKey() {
if (getMethod() == RxVolley.Method.POST) {
return getUrl() + mParams.getJsonParams();
} else {
return getUrl();
}
}
@Override
public byte[] getBody() {
try {
return mRequestBody == null ? null : mRequestBody.getBytes(getConfig().mEncoding);
} catch (UnsupportedEncodingException uee) {
Loger.debug(String.format("Unsupported Encoding while trying to get the bytes of %s" +
" using %s", mRequestBody, getConfig().mEncoding));
return null;
}
}
@Override
public Priority getPriority() {
return Priority.IMMEDIATE;
}
}
二 开闭原则
开闭原则(Open Close Principle),缩写OCP,他是Java世界里最基础的设计原则,它知道我们如何建立一个稳定的,灵活的系统.
概述:软件中的对象(类,模块,方法等)应该对于扩展是开放的,但是,对于修改是封闭的.在软件的生命周期内,因为变化,升级和维护等原因需要对软件原有代码进行修改时,可能会讲错误引入原本已经经过测试的老版本代码,破坏原有的系统.因此,当软件需要变化时,我们应该尽量通过扩展方式来实现变化,而不是修改已有的代码来实现.当然,在我们实际开发中,只通过继承的方式来升级,维护原有系统知识一个理想化的愿景,因此,在实际开发中,修改原有代码,拓展代码往往都是同时存在的
安卓源码分析截图:Android系统的上下文对象,即在Context中,为我们开发者封装了一个这个"语境".博文拿开发项目为例,点击一个基类,鼠标放在AppCompatActivity上,查看类继承关系(快捷键:Ctrl + H ),即可看到下图这个体系,请读者再结合上面概念细细回味
三 里氏替换原则
里氏替换原则(Liskov Substitution Principle),缩写LSP
概述:所有引用基类的地方必须能透明地使用个其子类的对象.(老爸能玩孩子的笔记本打魔兽).面向对象语言的三大特点是继承,封装,多态.里氏替换原则就是依赖继承.多态这两大特性.
历史替换元核心原理是抽象,抽象又依赖于继承这个特性,
继承优点:
1,代码重用,减少创建类的成本,每个子类都拥有父类的方法和属性
2,子类和父类基本相似,但又与父类有所区别
3,提高代码的可拓展性
继承缺点;
1,继承是侵入性,只要继承就必须拥有父类的所有方法和属性
2,可能造成子类代码冗余,灵活性降低,因为子类必须拥有父类的属性和方法
四 依赖倒置原则
依赖导致原则(Dependence Inversion Principle),缩写DIP
概述:依赖导致原则指代了一种特定的解耦形式,使得高层次的模块不依赖低层次的模块的实现细节的目的,依赖模块被颠倒了
1,高层模块不应该依赖低层模块,两者都应该依赖起抽象
2,抽象不应该依赖细节
3,细节应该依赖抽象