六大设计原则之单一职责

1.前言

最近重温了各种设计模式,顺便写了下笔记,分享出来给有缘的人,希望此系列文章能帮助到你们,那
将是我写此系列文章的最有用的价值之一了。

学中作,做中学,这是学技术最好的方法之一,所以我几乎都是通过例子来说明问题,这样文章就有点
长,可是如果肯研究下,定会有收获,如果只讲理论原理,那似乎懂了,可是做的时候还是不会用,所以
踏踏实实的去动手是你进步的源泉了。本系列文章用到的例子是从书籍《Android 源码设计模式解析与实战》中获取,在此感谢非常作者的汗水。若侵权则删。由于表达的需要对这些例子做不同程度的修改。《Android 源码设计模式解析与实战》这本书很适合学习,大家可以直接去买来研究。

那为啥要学设计模式呢?等你敲代码到一定程度了自然就明白了。否则你学着会觉得没有,这是我的真
实感受。在正式的学习经典的各种设计模式之前,我们先来看看设计模式的六大原则,这些原则都将会
体现在设计模式之中。

2.单一职责概念

单一职责,简单来说就是一个类中一组相关性很高的函数、数据的封装。用外行话来说,就是通常一个
类只负责一个功能。但是这个单一职责的界限并没有那么清晰的,需要自己的个人经验来确定,不要陷
入去区分什么才算是单一职责的漩涡里。等下我们会说明例子来说明这个问题。单一职责是优化代码的
第一步。

3.菜鸟的写法

  • 需求:图片加载并显示图片,并将图片缓存起来。
package com.study.demo.designdemo;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Looper;
import android.util.LruCache;
import android.widget.ImageView;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 图片加载类
 * Created by WKC on 2017/7/8.
 */

public class ImageLoader {
    //图片缓存
    LruCache<String, Bitmap> mImageCache;
    //线程池,线程数量为CUP的数量
    ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().
            availableProcessors());
    //UI Handler
    Handler mUiHandler = new Handler(Looper.getMainLooper());

    //构造函数
    public ImageLoader() {
        initImageCache();
    }

    //初始化缓存内存的参数
    private void initImageCache() {
        //计算可使用的最大内存
        final long maxMemory = Runtime.getRuntime().maxMemory() / 1024;
        //取四分之一的可用内存作为缓存
        final int cacheSize = (int) (maxMemory / 4);
        mImageCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
            }
        };
    }

    /**
     * 提供外部调用显示图片的函数
     *
     * @param url  图片的路径
     * @param imageview 显示图片的view
     */
    public void displayImage(final String url, final ImageView imageview) {
        Bitmap bitmap = mImageCache.get(url);
        if (bitmap != null){
            imageview.setImageBitmap(bitmap);
            return;
        }
        imageview.setTag(url);
        mExecutorService.submit(new Runnable() {
            @Override
            public void run() {

                Bitmap bitmap = downloadImage(url);
                if (bitmap == null) return;
                if (imageview.getTag().equals(url)) {
                    updateImageview(imageview, bitmap);
                }
                mImageCache.put(url, bitmap);
            }
        });
    }

    /**
     * 在UI线程显示图片
     *
     * @param imageview
     * @param bitmap
     */
    private void updateImageview(final ImageView imageview, final Bitmap bitmap) {
        mUiHandler.post(new Runnable() {
            @Override
            public void run() {
                imageview.setImageBitmap(bitmap);
            }
        });
    }

    /**
     * 下载图片
     *
     * @param imegeUrl
     * @return
     */
    private Bitmap downloadImage(String imegeUrl) {
        Bitmap bitmap = null;
        try {
            URL url = new URL(imegeUrl);
            final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            bitmap = BitmapFactory.decodeStream(conn.getInputStream());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bitmap;
    }
}

ok,写好了,能顺利的加载图片,能显示,也能缓存,似乎万事大吉了。可是如果功能不断的增多,这
个类就会变得越来越臃肿,代码也越来越复杂,图片加载系统就会越来越脆弱,那么我们该怎么做呢?
我们来分析下:这个类都做了哪些事?

  • 1.加载图片;
  • 2.缓存图片;
  • 3.显示图片。

主要做了这三件事。刚才我们说了单一职责,就是一个类中应该是一组相关性很高的函数、数据的封
装。而这个类显然不符合这个要求。我们姑且把缓存的职责提出来,生成一个鲜明的单一职责的类–
缓存,这样做的具体好处,会在下篇文章《六大设计原则之开闭原则》中有体现。现在我们把缓存提
取出来。当然我们也可以把下载的职责提出来,这个就留给你来练手了。
我们先看下UML图:
这里写图片描述

如图所示:如果对UML图还不了解,这篇文章可以帮助到你
http://www.uml.org.cn/oobject/201211231.asp

4.ImageLoader 代码修改

package com.study.demo.designdemo;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Looper;
import android.util.LruCache;
import android.widget.ImageView;

import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 图片加载类
 * Created by WKC on 2017/7/8.
 */

public class ImageLoader {
    //图片缓存
    private ImageCache mImageCache = new ImageCache();
    //线程池,线程数量为CUP的数量
    ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().
            availableProcessors());
    //UI Handler
    Handler mUiHandler = new Handler(Looper.getMainLooper());

    /**
     * 提供外部调用显示图片的函数
     *
     * @param url       图片的路径
     * @param imageview 显示图片的view
     */
    public void displayImage(final String url, final ImageView imageview) {
        Bitmap bitmap = mImageCache.get(url);
        if (bitmap != null) {
            imageview.setImageBitmap(bitmap);
            return;
        }
        imageview.setTag(url);
        mExecutorService.submit(new Runnable() {
            @Override
            public void run() {
                Bitmap bitmap = downloadImage(url);
                if (bitmap == null) return;
                if (imageview.getTag().equals(url)) {
                    updateImageview(imageview, bitmap);
                }
                mImageCache.put(url, bitmap);
            }
        });
    }

    /**
     * 在UI线程显示图片
     *
     * @param imageview
     * @param bitmap
     */
    private void updateImageview(final ImageView imageview, final Bitmap bitmap) {
        mUiHandler.post(new Runnable() {
            @Override
            public void run() {
                imageview.setImageBitmap(bitmap);
            }
        });
    }

    /**
     * 下载图片
     *
     * @param imegeUrl
     * @return
     */
    private Bitmap downloadImage(String imegeUrl) {
        Bitmap bitmap = null;
        try {
            URL url = new URL(imegeUrl);
            final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            bitmap = BitmapFactory.decodeStream(conn.getInputStream());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bitmap;
    }

}

5.提取出来的缓存类

package com.study.demo.designdemo;

import android.graphics.Bitmap;
import android.util.LruCache;

/**
 * Created by WKC on 2017/7/8.
 */

public class ImageCache {
    //图片缓存
    LruCache<String, Bitmap> mImageCache;

    public ImageCache() {
        initImageCache();
    }

    //初始化缓存内存的参数
    private void initImageCache() {
        //计算可使用的最大内存
        final long maxMemory = Runtime.getRuntime().maxMemory() / 1024;
        //取四分之一的可用内存作为缓存
        final int cacheSize = (int) (maxMemory / 4);
        mImageCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
            }
        };
    }

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

    public Bitmap get(String url) {
        return mImageCache.get(url);
    }
}

这样一拆分为二,ImageLoader负责图片加载并显示的逻辑(读者可以将加载和显示继续拆分,当做
练手),而ImageCache类只负责处理缓存的逻辑。这个代码清晰了,如果需要修改某个逻辑时,如
对ImageCache类的逻辑进行修改,尽管在这个类中改好了,不必去打扰ImageLoader类。

从以上的例子中,再来说下单一职责的意思,所谓单一,就是其职能唯一或者类似的一个功能,但是
没有严格的界限,每个人都有自己的理解,这需要经验和具体的业务逻辑来定的。尽管如此,还是有
一些指导的原则,两个功能完全不一样的就不应该放在同一个类中。一个类中应该是相关性很高的函
数或者数据的封装。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值