Android 优化代码的第一步 —— 单一职责原则

116 阅读3分钟

单一职责原则

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

例子

比如图片加载类 ImageLoader 中如果要实现缓存功能,则应该增加一个 ImageCache 类用来处理图片缓存,而不是把图片缓存的实现放在 ImageLoader 中。

优化前

/**
 * 图片加载类
 */
public class ImageLoader {
    // 图片缓存
    LruCache<String, Bitmap> mImageCache;
    // 线程池,线程数量为CPU的数量
    ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime()
            .availableProcessors());
    private Handler mUiHandler = new Handler() ;

    public ImageLoader() {
        initImageCache();
    }

    private void initImageCache() {
        // 计算可使用的最大内存
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        // 取4分之一的可用内存作为缓存
        final int cacheSize = maxMemory / 4;
        mImageCache = new LruCache<String, Bitmap>(cacheSize) {

            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
            }
        };
    }
    
    private void updateImageView(final ImageView imageView, final Bitmap bitmap) {
      mUiHandler.post(new Runnable() {
         
         @Override
         public void run() {
             imageView.setImageBitmap(bitmap); ;            
         }
      });
   }

    public void displayImage(final String url, final ImageView imageView) {
        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);
            }
        });
    }

    public Bitmap downloadImage(String imageUrl) {
        Bitmap bitmap = null;
        try {
            URL url = new URL(imageUrl);
            final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            bitmap = BitmapFactory.decodeStream(conn.getInputStream());
            conn.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return bitmap;
    }
}

这样写 ImageLoader 耦合太严重,缺少扩展性、灵活性。所有的功能都写在一个类里,这样随着功能的增多,ImageLoader 类会越来越大,代码也越来越复杂,图片加载系统就越来越脆弱。

优化后

image.png

/**
 * 图片加载类
 */
public class ImageLoader {
    // 图片缓存
    ImageCache mImageCache = new ImageCache();
    // 线程池,线程数量为CPU的数量
    ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime()
            .availableProcessors());
    private Handler mUiHandler = new Handler() ;

    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);
            }
        });
    }
    
    private void updateImageView(final ImageView imageView, final Bitmap bitmap) {
      mUiHandler.post(new Runnable() {
         
         @Override
         public void run() {
             imageView.setImageBitmap(bitmap); ;            
         }
      });
   }

    public Bitmap downloadImage(String imageUrl) {
        Bitmap bitmap = null;
        try {
            URL url = new URL(imageUrl);
            final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            bitmap = BitmapFactory.decodeStream(conn.getInputStream());
            conn.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }

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

    public ImageCache() {
        initImageCache();
    }

    private void initImageCache() {
        // 计算可使用的最大内存
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        // 取4分之一的可用内存作为缓存
        final int cacheSize = 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 一拆为二,ImageLoader 只负责图片加载的逻辑,而 ImageCache 只负责处理图片缓存的逻辑,这样 ImageLoader 的代码量变少了,职责也清晰了;当与缓存相关的逻辑需要改变时,不需要修改 ImageLoader 类,而图片加载的逻辑需要修改时也不会影响到缓存处理逻辑。

总结

从上述的例子中我们能够体会到,单一职责所表达出的用意就是“单一”二字。如何划分一个类、一个函数的职责,每个人都有自己的看法,这需要根据个人经验、具体的业务逻辑而定。但是,它也有一些基本的指导原则,例如,两个完全不一样的功能就不应该放在一个类中。一个类中应该是一组相关性很高的函数、数据的封装。工程师可以不断地审视自己的代码,根据具体的业务、功能对类进行相应拆分,这是程序员优化代码迈出的第一步。