红橙Darren视频笔记 网络请求优化方案 OOP面向对象 6大基本原则

参考链接

https://www.jianshu.com/p/e1d9ddc86c7d

先看几个demo demo的目的很简单 就是发送一个Http请求

先给出几个工具类的代码 各个demo都依赖工具类或者公共类的代码

public class BaseApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        // 测试不同的demo时记得切换导包
        PreferencesUtil.getInstance().init(this);
        // 初始化xUtils
        x.Ext.init(this);
        x.Ext.setDebug(BuildConfig.DEBUG); // 是否输出debug日志, 开启debug会影响性能.
        HttpUtils.initHttpRequest(new OKHttpRequest());
    }
}

public class ConstantValue {

    public static final class UrlConstant{
        public static final String BASE_URL = "http://is.snssdk.com/2/essay/";
        public static final String HOME_DISCOVERY_URL = BASE_URL+"discovery/v3/?";
    }
}

public class Utils {
    private Utils() {
    }

    // 字符串拼接
    public static String jointParams(String url, Map<String, Object> params) {
        if (params == null || params.size() <= 0) {
            return url;
        }

        StringBuffer stringBuffer = new StringBuffer(url);
        if (!url.contains("?")) {
            stringBuffer.append("?");
        } else {
            if (!url.endsWith("?")) {
                stringBuffer.append("&");
            }
        }

        for (Map.Entry<String, Object> entry : params.entrySet()) {
            stringBuffer.append(entry.getKey() + "=" + entry.getValue() + "&");
        }

        stringBuffer.deleteCharAt(stringBuffer.length() - 1);

        return stringBuffer.toString();
    }

    /**
     * 解析一个类上面的class信息
     *
     */
    public static Class<?> analysisClazzInfo(Object object) {
        Type genType = object.getClass().getGenericSuperclass();
        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
        return (Class<?>) params[0];
    }
}

demo1

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "Sample1.MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /********************访问网络开始*******************/
        OkHttpClient mOkHttpClient = new OkHttpClient();
        Map<String, Object> params = new HashMap<>();
        // 特定参数
        params.put("iid", 6152551759L);
        params.put("aid", 7);
        // 公共参数
        params.put("app_name", "joke_essay");
        params.put("version_name", "5.7.0");
        params.put("ac", "wifi");
        params.put("device_id", "30036118478");
        params.put("device_brand", "Xiaomi");
        params.put("update_version_code", "5701");
        params.put("manifest_version_code", "570");
        params.put("longitude", "113.000366");
        params.put("latitude", "28.171377");
        params.put("device_platform", "android");

        final String jointUrl = Utils.jointParams(ConstantValue.UrlConstant.HOME_DISCOVERY_URL, params);  //打印
        Log.e(TAG, "url:" + jointUrl);

        Request.Builder requestBuilder = new Request.Builder().url(jointUrl).tag(this);
        //可以省略,默认是GET请求
        Request request = requestBuilder.build();

        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, final IOException e) {
                // 失败
                Log.e(TAG, "fail " + e.getMessage());
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                // 内涵段子上面的url请求的网络服务其实已经关闭
                final String resultJson = response.body().string();
                Log.e(TAG, "success " + resultJson);
            }
        });
        /********************访问网络结束*******************/
    }
}

demo看起来很简洁,所有请求参数 发起请求 以及请求 但是每一次我们调用http请求的时候都要添加这些冗杂的参数么 于是有了demo2

demo2

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Sample2.MainActivity";

    /**
     * 相比于sample1 sample2将冗余繁杂的重复参数抽取出来放在公共的get方法中
     * 并且外部只需要传递一个CallBack即可 请求和CallBack的处理都集中放在工具类中
     */

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /********************访问网络开始*******************/
        Map<String, Object> params = new HashMap<>();
        // 特定参数
        params.put("iid", 6152551759L);
        params.put("aid", 7);

        HttpUtils.get(this, ConstantValue.UrlConstant.HOME_DISCOVERY_URL, params, new Callback() {
            @Override
            public void onFailure(Call call, final IOException e) {
                // 失败
                Log.e(TAG, "fail " + e.getMessage());
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                final String resultJson = response.body().string();
                Log.e(TAG, "success " + resultJson);
            }
        });
        /********************访问网络结束*******************/
    }
}
public class HttpUtils {
    private static final String TAG = "Sample2";

    private HttpUtils() {
    }

    public static void get(Context context, String url, Map<String, Object> params, final Callback callback) {
        OkHttpClient mOkHttpClient = new OkHttpClient();
        // 公共参数
        params.put("app_name", "joke_essay");
        params.put("version_name", "5.7.0");
        params.put("ac", "wifi");
        params.put("device_id", "30036118478");
        params.put("device_brand", "Xiaomi");
        params.put("update_version_code", "5701");
        params.put("manifest_version_code", "570");
        params.put("longitude", "113.000366");
        params.put("latitude", "28.171377");
        params.put("device_platform", "android");

        final String jointUrl = Utils.jointParams(url, params);  //打印
        Log.e(TAG, "url:" + jointUrl);

        Request.Builder requestBuilder = new Request.Builder().url(jointUrl).tag(context);
        //可以省略,默认是GET请求
        Request request = requestBuilder.build();

        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, final IOException e) {
                // 失败
                callback.onFailure(call, e);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                // 1.JSON解析转换
                // 2.显示列表数据
                // 3.缓存数据
                callback.onResponse(call, response);
            }
        });
    }
}

demo2在HttpUtils封装了get方法 

1.这样外部只需要添加几个特殊的参数 而不需要列出冗杂的参数,可以一定程度做到代码的复用

2.OkHttpClient的使用也被封装到方法内部 外部调用者(MainActivity)不需要了解OkHttpClient的存在 

3.外部传递的callback在get方法中可以进行统一的处理之后在返回给调用者

但是新的需求来了 我们希望增加一个缓存的功能 如何做呢

demo3

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Sample3.MainActivity";

    /**
     * Sample3 比起Sample2
     * 1.多考虑了缓存问题 对比缓存数据是否和cloud最新数据一致
     * 先显示缓存数据
     * 如果一致 直接返回
     * 如果不一致 更新界面 将新数据缓存
     * 2.使用了泛型类型的HttpCallBack
     */

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /********************访问网络开始*******************/
        Map<String, Object> params = new HashMap<>();
        // 特定参数
        params.put("iid", 6152551759L);
        params.put("aid", 7);

        HttpUtils.get(this, ConstantValue.UrlConstant.HOME_DISCOVERY_URL,
                params,new HttpCallBack<DiscoverListResult>() {
                    @Override
                    public void onSuccess(DiscoverListResult result) {
                        if (result.isOK()) {
                            // 没有列表数据的情况, 打印 Toast 或者做一些其他处理
                            Log.e(TAG, "success is OK "+result);
                        } else {
                            // 有数据列表的情况,显示列表
                            // showListData(result);
                            Log.e(TAG, "success is NG "+result);
                        }
                    }
                    @Override
                    public void onFailure(Exception e) {
                        Log.e(TAG, "fail " + e.getMessage());
                    }
                },true);
        /********************访问网络结束*******************/
    }
}

http请求的调用者没发生多大的变化 倒是get方法的最后一个参数由OKHttp的CallBack变成了我们自己定义的带有泛型类型的回调 其定义如下

public abstract class HttpCallBack<T>{

    // 返回可以直接操作的对象
    public abstract void onSuccess(T result);

    public abstract void onFailure(Exception e);
}

这样可以规范回调的返回类型 方便外部的调用 至于DiscoverListResult的具体实现不需要了解 想象它是一个Student差不多的类型即可 不过也需要考虑当返回的DiscoverListResult如果数据不完整如何处理,比如返回的数据缺少一些必要的数据 或者返回就不是一个有效的DiscoverListResult对象 这时应该考虑增加一个isOK类似的接口判断数据是否可用

public class HttpUtils {
    private static String TAG = "Sample3";

    private HttpUtils() {
    }

    public static <T> void get(Context context, String url, Map<String, Object> params, final HttpCallBack<T> callback, final boolean cache) {
        OkHttpClient mOkHttpClient = new OkHttpClient();
        // 公共参数
        params.put("app_name", "joke_essay");
        params.put("version_name", "5.7.0");
        params.put("ac", "wifi");
        params.put("device_id", "30036118478");
        params.put("device_brand", "Xiaomi");
        params.put("update_version_code", "5701");
        params.put("manifest_version_code", "570");
        params.put("longitude", "113.000366");
        params.put("latitude", "28.171377");
        params.put("device_platform", "android");

        final String jointUrl = Utils.jointParams(url, params);  //打印

        Log.e(TAG, " url: " + jointUrl);
        // 缓存问题
        // 缓存写到  SP 里面, 考虑多级缓存(比如说 最经常使用的放在内存中,2天内使用的数据放在数据库,其余放在文件中 )
        final String cacheJson = (String) PreferencesUtil.getInstance().getParam(jointUrl, "");
        // 写一大堆多级缓存处理逻辑,内存怎么扩展等等
        // 这里只考虑了一级缓存

        if (cache && !TextUtils.isEmpty(cacheJson)) {
            // 从缓存中取得数据
            Gson gson = new Gson();
            // data:{"name","darren"}   data:"请求失败"
            T objResult = (T) gson.fromJson(cacheJson,
                    Utils.analysisClazzInfo(callback));
            callback.onSuccess(objResult);
        }

        // 同时并行得实时查询cloud数据
        Request.Builder requestBuilder = new Request.Builder().url(jointUrl).tag(context);
        //可以省略,默认是GET请求
        Request request = requestBuilder.build();

        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, final IOException e) {
                // 失败
                callback.onFailure(e);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                final String resultJson = response.body().string();

                Log.e(TAG, resultJson.equals(cacheJson) + "");
                if (cache && resultJson.equals(cacheJson)) {
                    // 缓存数据与cloud数据相同 直接返回
                    return;
                }
                // 1.JSON解析转换
                // 2.显示列表数据
                // 3.缓存数据
                Gson gson = new Gson();
                // data:{"name","darren"}   data:"请求失败"
                T objResult;
                try {
                    objResult = (T) gson.fromJson(resultJson,
                            Utils.analysisClazzInfo(callback));
                } catch (Exception e) {
                    Log.e(TAG, "onResponse: " + e.getMessage());
                    e.printStackTrace();
                    return;
                }

                if (objResult != null) {
                    callback.onSuccess(objResult);
                }
                if (cache) {// 缓存数据
                    PreferencesUtil.getInstance().saveParam(jointUrl, resultJson);
                }
            }
        });
    }
}

HttpUtils方面主要是增加了缓存的判断逻辑 并且在处理回调时增加了对对象类型的转换逻辑 将对象从Json对象转换为某个类型的对象 注意这里的onSuccess可能发生两次 一次是读取到缓存数据发生的 一次是真的访问网络获取数据 其实HttpCallBack的onSuccess可以增加一个参数 说明是从缓存获取的数据还是从cloud获取

最后缓存是存放在sharedPreference的 部分逻辑如下

public class PreferencesUtil {
    private SharedPreferences preferences = null;
    private SharedPreferences.Editor editor = null;
    private Object object;
    public static PreferencesUtil preferencesUtil;

    public static PreferencesUtil getInstance() {
        if (preferencesUtil == null) {
            synchronized (PreferencesUtil.class) {
                if (preferencesUtil == null) {
                    // 使用双重校验锁
                    preferencesUtil = new PreferencesUtil();
                }
            }
        }
        return preferencesUtil;
    }

    public void init(Context context){
        /**
         * 问题在于,这个Context哪来的我们不能确定,很大的可能性,你在某个Activity里面为了方便,直接传了个this;
         * 这样问题就来了,我们的这个类中的sInstance是一个static且强引用的,在其内部引用了一个Activity作为Context,也就是说,
         * 我们的这个Activity只要我们的项目活着,就没有办法进行内存回收。而我们的Activity的生命周期肯定没这么长,造成了内存泄漏。
         * 所以这里使用context.getApplicationContext()
         */
        preferences = PreferenceManager.getDefaultSharedPreferences(context
                .getApplicationContext());
    }

    private PreferencesUtil() {

    }

    /**
     * 保存数据 所有的类型都适用
     *
     * 以key为key object为value 保存信息到shared preference
     */
    public synchronized void saveParam(String key, Object object) {
        if (editor == null)
            editor = preferences.edit();
        // 得到object的类型
        String type = object.getClass().getSimpleName();
        if ("String".equals(type)) {
            // 保存String 类型
            editor.putString(key, (String) object);
        } else if ("Integer".equals(type)) {
            // 保存integer 类型
            editor.putInt(key, (Integer) object);
        } else if ("Boolean".equals(type)) {
            // 保存 boolean 类型
            editor.putBoolean(key, (Boolean) object);
        } else if ("Float".equals(type)) {
            // 保存float类型
            editor.putFloat(key, (Float) object);
        } else if ("Long".equals(type)) {
            // 保存long类型
            editor.putLong(key, (Long) object);
        } else {
            if (!(object instanceof Serializable)) {
                throw new IllegalArgumentException(object.getClass().getName() + " 必须实现Serializable接口!");
            }

            // 不是基本类型则是保存对象
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            try {
                ObjectOutputStream oos = new ObjectOutputStream(baos);
                oos.writeObject(object);
                String productBase64 = Base64.encodeToString(
                        baos.toByteArray(), Base64.DEFAULT);
                editor.putString(key, productBase64);
                Log.d(this.getClass().getSimpleName(), "save object success");
            } catch (IOException e) {
                e.printStackTrace();
                Log.e(this.getClass().getSimpleName(), "save object error");
            }
        }
        editor.commit();
    }

    /**
     * 移除信息
     */
    public synchronized void remove(String key) {
        if (editor == null)
            editor = preferences.edit();
        editor.remove(key);
        editor.commit();
    }


    /**
     * 从shared preference 获取缓存数据的方法,所有类型都适用
     *
     * @param key
     * @param defaultObject
     * @return
     */
    public Object getParam(String key, Object defaultObject) {
        if (defaultObject == null) {
            return getObject(key);
        }

        String type = defaultObject.getClass().getSimpleName();

        if ("String".equals(type)) {
            return preferences.getString(key, (String) defaultObject);
        } else if ("Integer".equals(type)) {
            return preferences.getInt(key, (Integer) defaultObject);
        } else if ("Boolean".equals(type)) {
            return preferences.getBoolean(key, (Boolean) defaultObject);
        } else if ("Float".equals(type)) {
            return preferences.getFloat(key, (Float) defaultObject);
        } else if ("Long".equals(type)) {
            return preferences.getLong(key, (Long) defaultObject);
        }
        // 以上所有类型都不是 就是序列化对象了
        return getObject(key);
    }

    public Object getObject(String key) {
        String wordBase64 = preferences.getString(key, "");
        byte[] base64 = Base64.decode(wordBase64.getBytes(), Base64.DEFAULT);
        ByteArrayInputStream bais = new ByteArrayInputStream(base64);
        try {
            ObjectInputStream bis = new ObjectInputStream(bais);
            object =  bis.readObject();
            Log.d(this.getClass().getSimpleName(), "Get object success");
            return object;
        } catch (Exception e) {

        }
        Log.e(this.getClass().getSimpleName(), "Get object is error");
        return null;
    }
}

demo4

我们将HttpUtils的get方法改造 运用上builder设计模式

改造完如下

public class HttpUtils {
    private OKHttpRequest mHttpRequest;
    private final int TYPE_POST = 0x0011, TYPE_GET = 0x0022;
    private int mType = TYPE_GET;
    // 用于临时存储参数
    private Map<String, Object> mParams;
    private String mUrl;
    private boolean mCache = false;
    private Context mContext;

    private static String TAG = "Sample4";

    // 使用了builder设计模式
    public static HttpUtils with(Context context) {
        return new HttpUtils(context);
    }

    private HttpUtils(Context context) {
        mHttpRequest = new OKHttpRequest();
        mParams = new HashMap<>();
        this.mContext = context;
    }

    public HttpUtils param(String key, Object value) {
        mParams.put(key, value);
        return this;
    }

    public HttpUtils url(String url) {
        mUrl = url;
        return this;
    }

    public HttpUtils cache(boolean cache) {
        mCache = cache;
        return this;
    }

    public <T> void request() {
        request(null);
    }

    public <T> void request(final HttpCallBack<T> callback) {
        // 异常判断
        if (mContext == null) {
            Log.e(TAG, "request: mContext is null,请调用with方法!");
        }
        if (mType == TYPE_GET) {
            mHttpRequest.get(mContext, mUrl, mParams, callback, mCache);
        } else {
            mHttpRequest.post(mContext, mUrl, mParams, callback, mCache);
        }
    }

    public HttpUtils get() {
        mType = TYPE_GET;
        return this;
    }

    // 外部直接调用该方法 传递参数太多会觉得很难用 应该提供较少的参数的方法 外部不需要知道所有参数的意义
    // 真正想用的参数自己调用方法设定即可 没有传递的参数可以使用默认值
    /*public static <T> void get(Context context, String url, Map<String, Object> params, final HttpCallBack<T> callback, final boolean cache) {
    }*/
}

然后调用端修改如下

    private static final String TAG = "Sample4.MainActivity";

    /**
     * Sample4 比起Sample3
     * 1.HttpUtils使用了builder设计模式
     * 2.将请求单独新建为OKHttpRequest 与 HttpUtils分离 结构更清晰
     * 同时 缓存部分也新建为一个类 与 HttpUtils分离
     */

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /********************访问网络开始*******************/
        HttpUtils.with(this).cache(true).get().param("iid", 6152551759L).param("aid", 7).url(ConstantValue.UrlConstant.HOME_DISCOVERY_URL).request(
                new HttpCallBack<DiscoverListResult>() {
                    @Override
                    public void onSuccess(DiscoverListResult result) {
                        if (result.isOK()) {
                            // 没有列表数据的情况, 打印 Toast 或者做一些其他处理
                        } else {
                            // 有数据列表的情况,显示列表
                        }
                    }

                    @Override
                    public void onFailure(Exception e) {

                    }
                });
        /********************访问网络结束*******************/
    }
}

这样的有时是可以省略一些不必要的参数 比如默认的请求方式为get 是否缓存的默认值为false

那么调用者可以不添加这两个参数。事实上 传递的参数越少 外部的调用者会越觉得该方法调用起来简单

此外还有重要的一点是从HttpUtils中抽取出http相关的东西单独构成类OKHttpRequest

这样HttpUtils只负责存储各种参数 OKHttpRequest则专门处理Http相关的逻辑 做到分工明确 职责单一

最后 我们封装出一个SPHttpCache 代码如下

public class SPHttpCache {
    public void saveCache(String finalUrl,String resultJson){
        PreferencesUtil.getInstance().saveParam(finalUrl,resultJson);
    }

    public String getCache(String finalUrl){
        return (String) PreferencesUtil.getInstance().getObject(finalUrl);
    }
}

 PreferencesUtil则没有变化 这样让OKHttpRequest不是直接依赖PreferencesUtil 

SPHttpCache起到纽带的作用,OKHttpRequest依赖SPHttpCach,SPHttpCache调用PreferencesUtil 这样做的目的也是解耦(OKHttpRequest与PreferencesUtil 不直接关联),让我们可以轻易替换SPHttpCache为其他存储方式

demo5

最后 我们需要能灵活切换http的引擎 比如切换OKHttp为XUtils 如何做呢? 这就是面向接口编程了

首先提供一个接口

public interface IHttpRequest {
    <T> void get(Context context, String url, Map<String, Object> params,
        final HttpCallBack<T> callback, final boolean cache);

    <T> void post(Context context, String url, Map<String, Object> params,
             final HttpCallBack<T> callback, final boolean cache);

    <T> void download(Context context, String url, Map<String, Object> params,
                  final HttpCallBack<T> callback);

    <T> void upload(Context context, String url, Map<String, Object> params,
                      final HttpCallBack<T> callback);
}

OKHttpRequest和XUtilsRequest都实现了IHttpRequest接口

public class OKHttpRequest implements IHttpRequest{
    private static final String TAG = "Sample4.OKHttpRequest";
    private SPHttpCache mHttpCache;

    public OKHttpRequest() {
        mHttpCache = new SPHttpCache();
    }

    // 参数还是很多
    @Override
    public <T> void get(Context context, String url, Map<String, Object> params,
                        final HttpCallBack<T> callback, final boolean cache) {
        OkHttpClient mOkHttpClient = new OkHttpClient();
        // 公共参数
        params.put("app_name", "joke_essay");
        params.put("version_name", "5.7.0");
        params.put("ac", "wifi");
        params.put("device_id", "30036118478");
        params.put("device_brand", "Xiaomi");
        params.put("update_version_code", "5701");
        params.put("manifest_version_code", "570");
        params.put("longitude", "113.000366");
        params.put("latitude", "28.171377");
        params.put("device_platform", "android");

        final String jointUrl = Utils.jointParams(url, params);  //打印
        // 缓存问题
        Log.e(TAG, "request: mUrl "+jointUrl); // 缓存写到  SP 里面,多级缓存(内存中 30条,数据库 ,文件中 )
        final String cacheJson = mHttpCache.getCache(jointUrl);
        // 写一大堆处理逻辑 ,内存怎么扩展等等
        if (cache && !TextUtils.isEmpty(cacheJson)) {
            Gson gson = new Gson();
            // data:{"name","darren"}   data:"请求失败"
            T objResult = (T) gson.fromJson(cacheJson,
                    Utils.analysisClazzInfo(callback));
            callback.onSuccess(objResult);
        }

        Request.Builder requestBuilder = new Request.Builder().url(jointUrl).tag(context);
        //可以省略,默认是GET请求
        Request request = requestBuilder.build();

        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, final IOException e) {
                // 失败
                callback.onFailure(e);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                final String resultJson = response.body().string();

                Log.e(TAG, resultJson.equals(cacheJson) + "");
                if (cache && resultJson.equals(cacheJson)) {
                    return;
                }
                // 1.JSON解析转换
                // 2.显示列表数据
                // 3.缓存数据
                Gson gson = new Gson();
                // data:{"name","darren"}   data:"请求失败"
                T objResult;
                try {
                    objResult = (T) gson.fromJson(resultJson,
                            Utils.analysisClazzInfo(callback));
                } catch (Exception e) {
                    Log.e(TAG, "onResponse: " + e.getMessage());
                    e.printStackTrace();
                    return;
                }

                if (objResult != null) {
                    callback.onSuccess(objResult);
                }

                if (cache) {
                    mHttpCache.saveCache(jointUrl, resultJson);
                }
            }
        });
    }

    @Override
    public <T> void post(Context context, String url, Map<String, Object> params, HttpCallBack<T> callback, boolean cache) {

    }

    @Override
    public <T> void download(Context context, String url, Map<String, Object> params, HttpCallBack<T> callback) {

    }

    @Override
    public <T> void upload(Context context, String url, Map<String, Object> params, HttpCallBack<T> callback) {

    }
}


public class XUtilsRequest implements IHttpRequest {
    private static final String TAG = "Sample5.XUtilsRequest";
    private SPHttpCache mHttpCache;

    public XUtilsRequest() {
        mHttpCache = new SPHttpCache();
    }

    // 参数还是很多
    public <T> void get(Context context, String url, Map<String, Object> params,
                        final HttpCallBack<T> callback, final boolean cache) {
        // 公共参数
        params.put("app_name", "joke_essay");
        params.put("version_name", "5.7.0");
        params.put("ac", "wifi");
        params.put("device_id", "30036118478");
        params.put("device_brand", "Xiaomi");
        params.put("update_version_code", "5701");
        params.put("manifest_version_code", "570");
        params.put("longitude", "113.000366");
        params.put("latitude", "28.171377");
        params.put("device_platform", "android");

        final String jointUrl = Utils.jointParams(url, params);
        // 缓存问题
        final String cacheJson = mHttpCache.getCache(jointUrl);
        // 写一大堆处理逻辑 ,内存怎么扩展等等
        if (cache && !TextUtils.isEmpty(cacheJson)) {
            Gson gson = new Gson();
            // data:{"name","darren"}   data:"请求失败"
            T objResult = (T) gson.fromJson(cacheJson,
                    Utils.analysisClazzInfo(callback));
            callback.onSuccess(objResult);
        }

        Log.e(TAG, "request: mUrl " + jointUrl);
        RequestParams requestParams = new RequestParams();
        requestParams.setUri(jointUrl);
        x.http().get(requestParams, new org.xutils.common.Callback.CommonCallback<String>() {
            @Override
            public void onSuccess(String result) {
                Log.e(TAG, "onSuccess: " + result);
            }

            @Override
            public void onError(Throwable ex, boolean isOnCallback) {
                Log.e(TAG, "onSuccess: " + ex.getMessage());
                ex.printStackTrace();
            }

            @Override
            public void onCancelled(CancelledException cex) {
                Log.e(TAG, "onCancelled: " + cex.getMessage());
                cex.printStackTrace();
            }

            @Override
            public void onFinished() {
                Log.e(TAG, "onFinished: ");
            }
        });
    }

    @Override
    public <T> void post(Context context, String url, Map<String, Object> params, HttpCallBack<T> callback, boolean cache) {

    }

    @Override
    public <T> void download(Context context, String url, Map<String, Object> params, HttpCallBack<T> callback) {

    }

    @Override
    public <T> void upload(Context context, String url, Map<String, Object> params, HttpCallBack<T> callback) {

    }
}

然后使用使用到HttpRequest的地方都使用IHttpRequest 这样

其中提供了

    // 重点:提供给外部用于切换引擎的接口
    public HttpUtils httpRequest(IHttpRequest httpRequest){
        mHttpRequest = httpRequest;
        return this;
    }

用于中途无缝切换引擎

public class HttpUtils {
    // 重点 面向接口编程 使用IHttpRequest接口对象 而不是实现类
    // 可以中途切换Http引擎 比如换成Xutils
    private IHttpRequest mHttpRequest;
    //初始化Http引擎
    private static IHttpRequest mInitHttpRequest;
    private final int TYPE_POST = 0x0011, TYPE_GET = 0x0022;
    private int mType = TYPE_GET;
    // 用于临时存储参数
    private Map<String, Object> mParams;
    private String mUrl;
    private boolean mCache = false;
    private Context mContext;

    private static String TAG = "Sample5";

    // 使用了builder设计模式
    public static HttpUtils with(Context context) {
        return new HttpUtils(context);
    }

    private HttpUtils(Context context) {
        mHttpRequest = new OKHttpRequest();
        mParams = new HashMap<>();
        this.mContext = context;
    }

    // 提供给外部初始化Http引擎的接口
    public static void initHttpRequest(IHttpRequest httpRequest) {
        mInitHttpRequest = httpRequest;
    }

    // 重点:提供给外部用于切换引擎的接口
    public HttpUtils httpRequest(IHttpRequest httpRequest){
        mHttpRequest = httpRequest;
        return this;
    }

    public HttpUtils param(String key, Object value) {
        mParams.put(key, value);
        return this;
    }

    public HttpUtils url(String url) {
        mUrl = url;
        return this;
    }

    public HttpUtils cache(boolean cache) {
        mCache = cache;
        return this;
    }

    public <T> void request() {
        request(null);
    }

    public <T> void request(final HttpCallBack<T> callback) {
        if(mHttpRequest == null){
            mHttpRequest = mInitHttpRequest;
        }
        // 异常判断
        if (mContext == null) {
            Log.e(TAG, "request: mContext is null,请调用with方法!");
        }
        mHttpRequest.get(mContext, mUrl, mParams, callback, mCache);
    }

    public HttpUtils get() {
        mType = TYPE_GET;
        return this;
    }

    // 外部直接调用该方法 传递参数太多会觉得很难用 应该提供较少的参数的方法 外部不需要知道所有参数的意义
    // 真正想用的参数自己调用方法设定即可 没有传递的参数可以使用默认值
    /*public static <T> void get(Context context, String url, Map<String, Object> params, final HttpCallBack<T> callback, final boolean cache) {
    }*/
}

调用者变化不大

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Sample5.MainActivity";

    /**
     * Sample5 比起Sample4
     * 1.面向接口编程 HttpUtils中的IHttpRequest是一个接口 可以被赋值为任意接口实现者 如果需要切换引擎 十分方便
     * 同时提供了XUtils的请求 使用XUtils访问网络 可以灵活切换使用的网络引擎
     */

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /********************访问网络开始*******************/
        HttpUtils.with(this).cache(true).get()
                //.httpRequest(new XUtilsRequest())//中途切换引擎
                .param("iid", 6152551759L).param("aid", 7).url(ConstantValue.UrlConstant.HOME_DISCOVERY_URL).request(
                new HttpCallBack<DiscoverListResult>() {
                    @Override
                    public void onSuccess(DiscoverListResult result) {
                        if (result.isOK()) {
                            // 没有列表数据的情况, 打印 Toast 或者做一些其他处理
                        } else {
                            // 有数据列表的情况,显示列表
                        }
                    }

                    @Override
                    public void onFailure(Exception e) {

                    }
                });
        /********************访问网络结束*******************/
    }
}

我们可以关注17行 这里可以灵活的切换其他http引擎

事实上SPHttpCache也可抽取出一个接口 比如

public interface ICache {
    void saveCache(String finalUrl,String resultJson);

    String getCache(String finalUrl);
}

然后让FileCache和SPHttpCache分别实现该接口

这样我们可以像切换Http引擎一样灵活的切换缓存类型

完整代码

https://github.com/caihuijian/LearnCodeArchitecture2/tree/master/sixprinciple

下面我们结合上面的代码一点点理解面向对象的6个基本原则

1.单一职责原则

单一职责原则的英文名称是Single Responsibility Principle,简称SRP。它的定义是:就一个类而言,应该仅有一个引起它变化的原因。简单来说,一个类中应该是一组相关性很高的函数、数据的封装。

比如我们改造demo3为4的时候 从HttpUtils中抽取出http相关的东西单独构成类OKHttpRequest

这样HttpUtils只负责存储各种参数 OKHttpRequest则专门处理Http相关的逻辑 这里就是遵守了单一职责的原则

2.开闭原则

开闭原则的英文全称是Open Close Principle,简称OCP。开闭原则的定义是:软件中的对象(类、模块、函数等)应该对于扩展是开放的,但是,对于修改是封闭的。简而言之,对修改关闭 对扩展开发---->便于扩展 这里要求我们定义对象的时候使用接口或者父类,创建具体对象的时候才使用子类 这样我们后期可以轻易修改子类(实现对象)

比如我们在Demo4升级到Demo5时 我们能够无缝切换引擎就是因为我们在HttpUtils不直接引用具体的实现类而是使用的接口IHttpRequest 这样后期我们可以灵活切换 后面如果我们想进一步扩展 比如用Volley Retrofit 增加IHttpRequest的实现类即可,而不是通过修改原有代码来实现新功能

3.里氏替换原则

里氏替换原则英文全称是Liskov Substitution Principle,简称LSP。我们知道,面向对象的语言的三大特点是继承、封装、多态,里氏替换原则就是依赖于继承、多态这两大特性。里氏替换原则简单来说就是,所有引用基类的地方必须能透明地使用其子类的对象。通俗点讲,只要父类能出现的地方子类就可以出现。但是,反过来就不行了,有子类出现的地方,父类未必就能适应。

该原则适用于接口的实现与基类的继承

    public static void initHttpRequest(IHttpRequest httpRequest) {
        //这里定义的传入的是接口
        mInitHttpRequest = httpRequest;
    }

	//实际使用 可以使用任意实现者替换
    HttpUtils.initHttpRequest(new OKHttpRequest());
	HttpUtils.initHttpRequest(new XUtilsRequest());

	// 又比如RecyclerView的方法 定义的时候参数为基类
    public void setLayoutManager(@Nullable LayoutManager layout) {
        ......
    }
    // 使用的时候 可以用任意子类替换
    mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
    mRecyclerView.setLayoutManager(new GridLayoutManager(this,3));        

4.依赖倒置原则

依赖倒置原则英文全称是Dependence Inversion Principle,简称DIP。依赖反转原则指代了一种特定的解耦形式,高层模块不依赖低层次模块的细节,说白了高层次就是不依赖细节而是依赖抽象。

比如demo5中

HttpUtils依赖接口就是这个原则的体现

5.接口隔离原则

接口隔离原则英文全称是InterfaceSegregation Principles,简称ISP。它的定义是:客户端不应该依赖它不需要的接口。另一种定义是:类间的依赖关系应该建立在最小的接口上。接口隔离原则将非常庞大、臃肿的接口拆分成为更小的和更具体的接口,这样客户将会只需要知道他们感兴趣的方法。接口隔离原则的目的是系统解开耦合,从而容易重构、更改和重新部署,让客户端依赖的接口尽可能地小。

即接口尽量小 当然极端情况是每个接口就一个方法 显然这是不合适的,那么我们可以结合职责单一原则 使接口尽量小

6.最少知识原则

参考链接

https://blog.csdn.net/qq_38844728/article/details/88739574

最少知识原则又称为迪米特原则英文全称为Law of Demeter,简称LOD,虽然名字不同,但描述的是同一个原则:一个对象应该对其他对象有最少的了解。通俗地讲,一个类应该对自己需要耦合或调用的类知道得最少,类的内部如何实现、如何复杂都与调用者或者依赖者没关系,调用者或者依赖者只需要知道他需要的方法即可,其他的我一概不关心。类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。

迪米特法则还有一个英文解释是:Only talk to your immedate friends,翻译过来就是:只与直接的朋友通信。个对象应当对其它对象有尽可能少的了解,不要和陌生人说话。

强调只和朋友说话,不和陌生人说话。这里的朋友指的是:出现在成员变量,方法输入,输出参数中的类称为成员朋友类,而出现在方法体内部的类不属于朋友类。

迪米特法则初衷在于降低类之间的耦合。由于每个类尽量减少对其它类的依赖,因此。很容易使得系统的功能模块独立,相互之间不存在(或很少有)依赖关系。

Boss想从TeamLeader那里知道现有课程的总数。它们之间的调用关系应该为Boss—>TeamLeader—>Course。Boss与Course并无直接联系,所以在Boss类的方法中不应该出现Course类。

public class Boss {
    public void commandCheckNumber(TeamLeader teamLeader){
        teamLeader.checkNumberOfCourses();
    }
}


public class TeamLeader {
    public void checkNumberOfCourses(){
        List<Course> courseList = new ArrayList<Course>();
        for(int i = 0 ;i < 20;i++){
            courseList.add(new Course());
        }
        System.out.println("在线课程的数量是:"+courseList.size());
    }
}

要遵守该法则 可以从以下方面入手

优先考虑将一个类设置成不变类

尽量降低一个类的访问权限

尽量降低成员的访问权限

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值