AndroidAndroid知识互联网科技

安卓设计模式(一)面向对象六大设计原则

2016-09-03  本文已影响773人  uncochen

最近在工作之余探究设计模式的运用,看设计模式方面的书籍,在这里做一下学习的记录,一个是防止忘记过快,另一个也是给大家分享下自己学习设计模式的一些收获.

该系列其他文章:

一 单一职责原则 SRP

就一个类而言,应该仅有一个引起它变化的原因.

ok,简单点就是说一个类的功能和职责应该是单一的,是一组相关性很高的函数和数据的封装.

比如我们自己封装一个log工具类,包括可以控制全局是否打印log,自动获取类名作为TAG,在log信息后附加方法名丶线程丶机型丶网络环境信息,这样方便调试.

这里的附加信息的获取就应该单独提出来,因为这些信息的获取不属于打印log的职责范围内,并且其他地方也可能需要获取这些信息.

二 开闭原则 OCP

软件中的对象(类,模块,函数等)应该对于扩展是开放的,但是,对于修改是封闭的.

简单说就是,你现在写的代码在面对需求变更时能够这样轻松应对:通过继承来实现新需求,不修改内部代码

例如我们自己实现ImageLoader,其中肯定包括缓存的实现,根据上一节的单一职责原则,我肯定知道将缓存提出来为ImageCache类,然后在ImageLoder中

ImageCache mImageCache=new ImageCache();//实例化一个缓存,内部通过内存缓存LruCache实现

下个星期产品说每次app重新打开都需要重新下载图片,这样太费流量了,于是我到ImageCache类中将缓存改为LruCache+DiskCache双缓存.

很明显这是违反开闭原则的,优雅的代码应该是这样的

缓存接口:

public interface ImageCache {
    public Bitmap get(String url);

    public void put(String url, Bitmap bitmap);
}

实现:

public class DoubleCache implements ImageCache {
    @Override
    public Bitmap get(String url) {
        return null;
    }

    @Override
    public void put(String url, Bitmap bitmap) {

    }
}

产品说要双缓存:

setImageCache(new DoubleCaChe)

产品说还是换回内存缓存吧(可恶):

setImageCache(new memoryCache)

三 里氏替换原则 LSP

所有引用基类的地方必须能够透明的使用其子类对象.

ok,简单点说,父类能出现的地方,子类就能出现,并且替换为子类也不会产生任何异常

例如上面的ImageLoder,产品说在设置里要能够清除缓存,根据开闭原则我肯定不会去修改我的DoubleCaChe,我可以重新写一个带清除的双缓存DoubleCacheWithClear(实现ImageCache);同样我也可以这样做:

public class DoubleCacheWithClear extends DoubleCache {
    public void clear() {
        //清除
    }
}

然后

setImageCache(new DoubleCaChe)

替换为

setImageCache(new DoubleCacheWithClear)

四 依赖倒置原则 DIP

依赖倒置原则指代了一种特定的解耦形式,使得高层次的模块不依赖于低层次模块的实现细节,依赖模块被颠倒了.

简单说就是样的:

对于"倒置"这个关键字我的理解是这样的,老板只跟老板谈项目,至于怎么去实现,那是你员工的事情,但是员工必须按照老板的安排做.

在java语言中的具体表述就是:模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过抽象产生的.

举个栗子,在MVP模式下,这三个层次之间往往是通过接口交流的,Presenter持有View和Model的接口(IView,IModel),Persenter和实现类Viewiml丶Modeliml不会有任何关联(抽象不依赖细节),但是实现类Viewiml丶Modeliml必须实现IView,IModel中的方法(细节依赖抽象)

五 接口隔离原则 ISP##

类间的依赖关系应该建立在最小的接口上.

简单说就是,让客户端依赖的接口尽可能的小

这个原则是比较好理解的,抽象应该尽可能的小,没有必然联系的方法应该分别在不同的抽象中

举个栗子:在RecycleView时,需要我们自己实现onItemClick()的回调,并且有时候我们还需要获取item的长按事件,ok,接口回调即可搞定,于是我很快写了这样的代码:

interface itemCallBack {//回调
    void onItemClick(int position);

    void onItemLongClick(int position);
}

在View层中:

mMyAdapter = new MyAdapter();
    mMyAdapter.setCallBack(new MyAdapter.ItemCallBack() {
        @Override
        public void onItemClick(int position) {
            //do
        }

        @Override
        public void onItemLongClick(int position) {
            //do
        }
    });

以上其实是不符合接口隔离原则的,试想一下,ItimClcik和ItemLongClick是没有必然联系的,我只想监听单击事件,却也必须实现长按事件,这很不科学.

根据接口隔离原则,抽象或者接口应当尽量小,所以好的做法是这样的:

public class MyAdapter {//...继承
    private ItemClcikCallBack mClcikCallBack;
    private ItemLongClickCallBack mLongClickCallBack;

    public void setClcikCallBack(ItemClcikCallBack clcikCallBack) {
        mClcikCallBack = clcikCallBack;
    }

    public void setLongClickCallBack(ItemLongClickCallBack longClickCallBack) {
        mLongClickCallBack = longClickCallBack;
    }

    //...调用mClcikCallBack和mLongClickCallBack

    interface ItemClcikCallBack {//单击回调
        void onItemClick(int position);

    }

    interface ItemLongClickCallBack {//长按回调

        void onItemLongClick(int position);
    }
}` 

在View层中:

mMyAdapter = new MyAdapter();
    mMyAdapter.setClcikCallBack(new MyAdapter.ItemClcikCallBack() {
        @Override
        public void onItemClick(int position) {

        }
    });
    mMyAdapter.setLongClickCallBack(new MyAdapter.ItemLongClickCallBack() {
        @Override
        public void onItemLongClick(int position) {
            
        }
    });

六 迪米特原则(最少知识原则) LOD

迪米特原则又称为最少知识原则

一个对象应该对其他对象有最少的了解

在java中应该是这样体现的:一个类应该对自己需要耦合或调用的类知道的最少,类的内部如何实现与调用者或者依赖者没有关系.

个人理解包含两个方面:

  1. 需要调用的类应该最少
  2. 对于调用的某个类,这个类内部,调用者应该知道的最少

举栗子:还是前面的ImageLoder,缓存这块是已经搞定了.假如在某次加载图片中,缓存没找到就需要联网去服务器拿图片,并且需要存到缓存中以备下次直接从缓存加载,ok,很快可以写出这样的代码:

public class ImageLoder {
    private ImageCache mImageCache = new DoubleCache();
    //...
    public void dispalyImage(String url, ImageView imageView) {
        Bitmap bitmap = mImageCache.get(url);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            return;
        }
        HttpImage4Service.down4Service(url, imageView, mImageCache);
    }
}

HttpImage4Service下载类中从网络中加载图片方法:

    public static void down4Service(String url, ImageView imageView, ImageCache     imageCache) {
        //...从网络拉取图片
        //回调↓
        imageView.setImageBitmap(bitmap4Service);//显示图片
        imageCache.put(url, bitmap4Service);//存到缓存中
    }

分析下这样设计的耦合情况,

三个类之间是否知道的最少?试想一下,从网络拉取图片跟缓存这样两个类应该有关联吗?实际上是没必要的,根据最少知识原则,改进之后应该是下面这样的:

public class ImageLoder {
    private ImageCache mImageCache = new DoubleCache();
    //...
    public void dispalyImage(String url, ImageView imageView) {
        Bitmap bitmap = mImageCache.get(url);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            return;
        }
        bitmap=HttpImage4Service.down4Service(url);//只负责下载图片
        imageView.setImageBitmap(bitmap);
        imageCache.put(url, bitmap);//存到缓存中
    }
}

改进后三个类中只有ImageLoder调用HttpImage4Service和ImageCache中的方法,其余没有任何调用关系,耦合度降低.

在MVP中,View层和Model层拒绝通信,也是符合最少知识原则的,达到降低耦合效果,同时可扩展性会大大增加.

总结

面向对象的六大设计原则,最终可以化为这几个关键字:抽象,单一职责,最小化

这也是大家经常提到的面向接口编程的重点

应用开发,最难的不是完成开发工作,而是维护和升级.为了后续能够很好的维护和升级,我们的系统需要在满足稳定性的前提下保持以下三个特性:


以上是面向对象的六大设计原则.第一次写这么长的博客,想想还有些激动呢!

有些模式可能自己理解的不足,还请大家及时指出,大家一起讨论.

不说了,老司机周末要去开车了...

关于作者

上一篇 下一篇

猜你喜欢

热点阅读