欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

面向对象的六大原则

程序员文章站 2024-03-16 22:00:04
...

单一职责原则:Single Responsibility Principle

顾名思义:就一个类而言,应该仅有一个引起它变化的原因。简单来说,一个类中应该是一组相关性很高的函数,数据的封装。

举个很简单的例子:我们在使用Retrofit+okhttp请求的时候,一般都会这么写

//创建okHttpClient对象
OkHttpClient mOkHttpClient = new OkHttpClient();

//创建retrofit对象
Retrofit retrofit = new Retrofit.Builder()
        .addConverterFactory(GsonConverterFactory.create())
        .baseUrl("http://apis.baidu.com/txapi/")
        .setOkHttpClient(mOkHttpClient)
       .build();
// 实例化我们的mApi对象
mApi = retrofit.create(APi.class); 

// 调用我们的响应的方法
Call<News> news = mApi.getNews(number, page);
news.enqueue(new Callback<News>() {
    @Override
    public void onResponse(Call<News> call, Response<News> response) {
        News body = response.body();
        Logger.i("onResponse:   ="+body.toString());
    }

    @Override
    public void onFailure(Call<News> call, Throwable t) {
        Logger.i("onResponse:   ="+t.getMessage());

    }
});
这么写可以吗?当然可以,没有任何的问题,同样可以正常的请求到数据。但是,对于程序而言,其扩展性几乎没有,举一个很简单的例子,如果我需要使用缓存,那怎么办呢?这个时候有的同学就说了,这还不简单吗?直接设置Cache呀,OkHttp本身就提供了缓存机制。没错,但是如果我说对于有的接口我想使用缓存,有的接口我不想使用缓存的话那怎么办呢?哈哈,是不是感觉懵逼了。其实很简单,使用单一职责对上面的代码进行简单的修改就好了。

首先创建一个OkHttpUtil类

public class OkHttpUtil {

    private boolean  isCache = false;
    private boolean isIntercept = true;
    private long readTimeOut = 10000;
    private long connectTimeOut = 10000;
    private long writeTimeOut = 10000;
    private static Cache cache;

    static {
        File cacheDir = new File(IApplication.mContext.getCacheDir(), "cache");
        cache = new Cache(cacheDir, 1024 * 1024 * 10);  //缓存的最大尺寸10m
    }

    public OkHttpUtil() {
    }

    // 设置缓存
    public OkHttpUtil setCache(boolean cacheValue) {
        this.isCache = cacheValue;
        return this;
    }

    // 设置数据读取超时时间
    public OkHttpUtil setTimeOutTime(long timeout) {
        readTimeOut = timeout;
        return this;
    }

    // 设置网络连接超时时间
    public OkHttpUtil setConnectTime(long timeout) {
        connectTimeOut = timeout;
        return this;
    }

    // 设置写入服务器的超时时间
    public OkHttpUtil setWriteTime(long timeout) {
        writeTimeOut = timeout;
        return this;
    }

    // 设置拦截器
    public OkHttpUtil setIntercept(boolean isIntercept) {
        this.isIntercept = isIntercept;
        return this;
    }

    // 设置Build方法
    public OkHttpClient build() {
        OkHttpClient.Builder okHttpClient = new OkHttpClient.Builder();
        okHttpClient.readTimeout(readTimeOut, TimeUnit.MILLISECONDS);
        okHttpClient.connectTimeout(connectTimeOut, TimeUnit.MILLISECONDS);
        okHttpClient.writeTimeout(writeTimeOut, TimeUnit.MILLISECONDS);
        if (isCache) {
            okHttpClient.cache(cache);
        }
        if (isIntercept) {
            okHttpClient.addInterceptor(new HttpLoggingInterceptor());
            okHttpClient.addNetworkInterceptor(new HttpNetInterceptor());
            okHttpClient.addInterceptor(new HttpNetInterceptor());
        }
        return okHttpClient.build();
    }
}
有没有发现一个问题,这个类其实只处理一个问题:那就是设置OkHttp的一些东西,比如缓存呀,超时时间呀等等。这样的话就保证了类的纯粹性,也遵循了单一职责原则。下面的东西我就直接贴出来了哈。

  

public class RetrofitUtil {


    private String baseUrl;
    private Converter.Factory factory;
    private CallAdapter.Factory callFactory;
    private OkHttpClient okHttpClient;

    public RetrofitUtil() {
        baseUrl = "https://api2.wifiyaoshi.com/";
        factory = GsonConverterFactory.create();
        callFactory = RxJavaCallAdapterFactory.create();
    }

    // 设置BaseUrl
    public RetrofitUtil setBaseUrl(String baseUrl) {
        this.baseUrl = baseUrl;
        return this;
    }

    // 设置数据读取超时时间
    public RetrofitUtil addConverterFactory(Converter.Factory factory) {
        this.factory = factory;
        return this;
    }

    // 设置网络连接超时时间
    public RetrofitUtil addCallAdapterFactory(CallAdapter.Factory factory) {
        this.callFactory = factory;
        return this;
    }

    // 设置写入服务器的超时时间
    public RetrofitUtil setOkHttpClient(OkHttpClient okHttpClient) {
        this.okHttpClient = okHttpClient;
        return this;
    }

    // 设置Build方法
    public Retrofit build() {
        Retrofit.Builder builder = new Retrofit.Builder();
        builder.baseUrl(baseUrl);
        builder.addConverterFactory(factory);
        builder.addCallAdapterFactory(callFactory);
        builder.client(okHttpClient);
        return builder.build();
    }
}
    
public class BaseHttpRetrofit {

    private OkHttpUtil okHttpUtil;
    private RetrofitUtil retrofitUtil;

    public synchronized OkHttpUtil getOkHttpUtil() {
        if (okHttpUtil == null) {
            synchronized (OkHttpUtil.class) {
                if (okHttpUtil == null) {
                    okHttpUtil = new OkHttpUtil();
                }
            }
        }
        return okHttpUtil;
    }

    public synchronized RetrofitUtil getRetrofitUtil() {
        if (retrofitUtil == null) {
            synchronized (RetrofitUtil.class) {
                if (retrofitUtil == null) {
                    retrofitUtil = new RetrofitUtil();
                }
            }
        }
        return retrofitUtil;
    }
}
上面的这些我就不做过多解释了,很容易看懂,这三个类遵循的就是单一职责原则。

public class HttpRetrofit extends BaseHttpRetrofit {


    // 使用单例模式生成唯一的HttpRetrofit类
    public static ApiManager apiManager;

    // 设置缓存
    public HttpRetrofit setCache(boolean cacheValue) {
        getOkHttpUtil().setCache(cacheValue);
        return this;
    }

    // 设置数据读取超时时间
    public HttpRetrofit setTimeOutTime(long timeout) {
        getOkHttpUtil().setTimeOutTime(timeout);
        return this;
    }

    // 设置网络连接超时时间
    public HttpRetrofit setConnectTime(long timeout) {
        getOkHttpUtil().setConnectTime(timeout);
        return this;
    }

    // 设置写入服务器的超时时间
    public HttpRetrofit setWriteTime(long timeout) {
        getOkHttpUtil().setWriteTime(timeout);
        return this;
    }

    // 设置拦截器
    public HttpRetrofit setIntercept(boolean isIntercept) {
        getOkHttpUtil().setIntercept(isIntercept);
        return this;
    }

    // 设置ApiManager
    public HttpRequest build() {
        apiManager = getRetrofitUtil().setOkHttpClient(getOkHttpUtil().build()).build().create(ApiManager.class);
        return HttpRequest.getInstances();
    }
}
这个类其实就是对外暴露的一些方法供外部调用,包括设置拦截器,设置缓存,设置超时时间等等,最后build()方法生成apiManager。有木有觉得逻辑很清楚,也很容易理解呀。好了,最后附上一张UML图,帮助大家去更好的理解

面向对象的六大原则

开闭原则:Open Close Principle

开闭原则的定义是:软件中的对象(类,函数,模块)应该对于扩展是开放的,对于修改是关闭的。也就是说对于已经写好的功能模块的代码最好不要去修改,而是采用扩展的方式去适应需求。还是以上面的例子来进行讲解说明下。一般情况下,我们在APP里面一般只有一套网络请求的框架,而且框架都是封装的很好的一套,但是当突然有一天我们要使用其他框架里面一个特别好的点的时候(例如断点下载),我们需要对我们原来封装的代码进行大范围的修改,这其实很不好,对于程序的扩展性而言并不好,那么这个时候我们的开闭原则就要开始闪亮登场了。

首先新建一个接口

public interface IHttpRequest {

    // Get请求
    void mHttpGet(TreeMap map, HttpRequestCallback mCallBack);

    // Post请求
    void mHttpPost(TreeMap map, HttpRequestCallback mCallBack);

    // 单文件上传
    void mHttpFile(File file, TreeMap map, HttpRequestCallback mCallBack);

    // 多文件上传
    void mHttpMultiFile(List<File> list, TreeMap map, HttpRequestCallback mCallBack);
}
对于Retrofit来说,实现的代码是:

public class HttpRetrofitRequest extends HttpRetrofit implements IHttpRequest {

    private volatile static HttpRetrofitRequest INSTANCES;
    private static boolean flag = true;

    public synchronized static HttpRetrofitRequest getInstances() {
        if (INSTANCES == null) {
            synchronized (HttpRetrofitRequest.class) {
                if (INSTANCES == null) {
                    INSTANCES = new HttpRetrofitRequest();
                }
            }
        }
        return INSTANCES;
    }

    private HttpRetrofitRequest() {
        if (flag) {
            flag = false;
            build();
        }
    }

    // 执行Get请求
    @Override
    public void mHttpGet(TreeMap map, HttpRequestCallback mCallBack) {
        map = HttpTool.getTreeCrc(map);
        Observable<Response<Object>> observable = apiManager.getData(map);
        observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new BaseObserver(mCallBack));
    }

    // 执行Post请求
    @Override
    public void mHttpPost(TreeMap map, HttpRequestCallback mCallBack) {
        map = HttpTool.getTreeCrc(map);
        Observable<Response<Object>> observable = apiManager.postData(map);
        observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new BaseObserver(mCallBack));
    }

    // 执行文件上传的请求(单个文件上传)
    @Override
    public void mHttpFile(File file, TreeMap map, HttpRequestCallback mCallBack) {
        // 生成单个文件
        RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
        MultipartBody.Part body = MultipartBody.Part.createFormData("pic1", file.getName(), requestFile);

        // 将所有的字段进行转换
        map = HttpTool.getTreeCrc(map);
        Map<String, RequestBody> mapValue = new HashMap<>();
        for (Object key : map.keySet()) {
            mapValue.put(key.toString(), HttpTool.convertToRequestBody(map.get(key).toString()));
        }
        Observable<Response<Object>> observable = apiManager.upload(mapValue, body);
        observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new BaseObserver(mCallBack));
    }

    // 执行文件上传的请求(多个文件上传)
    @Override
    public void mHttpMultiFile(List<File> list, TreeMap map, HttpRequestCallback mCallBack) {
        //生成多个文件并添加到集合中
        List<MultipartBody.Part> params = new ArrayList<>();
        RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), list.get(0));
        MultipartBody.Part body = MultipartBody.Part.createFormData("pic1", list.get(0).getName(), requestFile);
        RequestBody requestFile1 = RequestBody.create(MediaType.parse("multipart/form-data"), list.get(1));
        MultipartBody.Part body1 = MultipartBody.Part.createFormData("pic2", list.get(1).getName(), requestFile1);
        RequestBody requestFile2 = RequestBody.create(MediaType.parse("multipart/form-data"), list.get(1));
        MultipartBody.Part body2 = MultipartBody.Part.createFormData("pic3", list.get(1).getName(), requestFile2);
        params.add(body);
        params.add(body1);
        params.add(body2);
        // 将所有的字段进行转换
        map = HttpTool.getTreeCrc(map);
        Map<String, RequestBody> mapValue = new HashMap<>();
        for (Object key : map.keySet()) {
            mapValue.put(key.toString(), HttpTool.convertToRequestBody(map.get(key).toString()));
        }
        // 发送异步请求
        Observable<Response<Object>> observable = apiManager.uploadMultipart(mapValue, params);
        observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new BaseObserver(mCallBack));
    }
}
同样的,如果你要实现xutils,volley等等,操作按照上面的代码写就可以了,接下来就是重点了,那么我们去实例化我们具体的使用的框架呢?首先新建一个HttpUtils类

public class HttpUtils {

    private static IHttpRequest mInitHttpRequest;
    private static IHttpRequest mHttpRequest;
    public static void initHttpRequest(IHttpRequest httpRequest){
        mInitHttpRequest = httpRequest;
    }

    // 如果有两种的情况下 比如 volley 下载文件并不是很屌 ,那么可以换成 OKHttp
    public HttpUtils httpRequest(IHttpRequest httpRequest) {
        this.mHttpRequest = httpRequest;
        return this;
    }

    // Get请求
    public <T> void executeGet(TreeMap<String, Object> maps,HttpRequestCallback<T> callback) {
        // 如果没有指定,那么就用 application 中初始化的
        if(mHttpRequest == null){
            mHttpRequest = mInitHttpRequest;
        }
        mHttpRequest.mHttpGet(maps,callback);
    }

    // Post请求
    public <T> void executePost(TreeMap<String, Object> maps,HttpRequestCallback<T> callback) {
        // 如果没有指定,那么就用 application 中初始化的
        if(mHttpRequest == null){
            mHttpRequest = mInitHttpRequest;
        }
        mHttpRequest.mHttpPost(maps,callback);
    }

    // 单文件请求
    public <T> void executeFile(File file,TreeMap<String, Object> maps, HttpRequestCallback<T> callback) {
        // 如果没有指定,那么就用 application 中初始化的
        if(mHttpRequest == null){
            mHttpRequest = mInitHttpRequest;
        }
        mHttpRequest.mHttpFile(file,maps,callback);
    }

    // 多文件请求
    public <T> void executeMultiFile(List<File> files,TreeMap<String, Object> maps, HttpRequestCallback<T> callback) {
        // 如果没有指定,那么就用 application 中初始化的
        if(mHttpRequest == null){
            mHttpRequest = mInitHttpRequest;
        }
        mHttpRequest.mHttpMultiFile(files,maps,callback);
    }
}
发现没有,这个类没有实现任何的功能,而是把请求下发到下一级去实现,同时在这里也没有实例化任何的对象,而是通过接口去动态实例化,这样就可以保证适应于你要添加的任何框架了。接下来使用起来非常简单
public class MApplication extends IApplication {

    public static HttpUtils mHttpUtils;

    @Override
    public void onCreate() {
        super.onCreate();
        mHttpUtils = new HttpUtils();
        mHttpUtils.httpRequest(HttpRetrofitRequest.getInstances());
 }

    public static HttpUtils getHttpUtils() {
        return mHttpUtils;
    }
}
 // 执行Get请求
    public void requestGet(View view) {
        TreeMap<String, Object> maps = new TreeMap<>();
        maps.put("api", "rest.session.start");
        // 使用默认的框架
        MApplication.getHttpUtils().executeGet(maps,this);
    }

    // 执行GET请求
    public void requestGetTwo(View view) {
        TreeMap<String, Object> maps = new TreeMap<>();
        maps.put("api", "home.list");
        maps.put("area_id", "1");
        // 使用xutils框架
        MApplication.getHttpUtils().httpRequest(new xUtils()).executeGet(maps,this);
    }

里氏替换原则:Liskov Substitution Principle

里氏替换原则简单来说就是所有引用父类的地方必须能够使用子类对象,比如我们在上面写的

 mHttpUtils.httpRequest(HttpRetrofitRequest.getInstances());
这其实就是一个很典型的里氏替换原则,包括我们平时写的

mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setLayoutManager(new GridLayoutManager(this,3));
都是属于里氏替换原则,很简单吧

依赖倒置原则:Dependence Inversion Principle

依赖倒置原则指定了一种特定的解耦方式,即高层不依赖于低层的具体实现而依赖于抽象,还是拿上面的例子来说明,在HttpUtils类里面,我们在设置初始化的时候并没有去具体的指向某一个实例,而是通过接口的方式去实现,这样的话就有利于我们在实际的业务层中去随时更改我们选用的框架而不用再次跑到HttpUtils里面来修改代码。

接口隔离原则:InterfaceSegregation Principles

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

迪米特原则

迪米特原则说简单点就是最少知识原则,即只关心自己所持有的对象和要调用的方法,至于其他的一律不在自己的关心范围之内