《Android 源码设计模式解析与实战》——第1章,第1.1节优化代码的第一步——单一职责原则

第1章 走向灵活软件之路——面向对象的六大原则
Android 源码设计模式解析与实战

1.1 优化代码的第一步——单一职责原则
单一职责原则的英文名称是Single Responsibility Principle,缩写是SRP。SRP的定义是:就一个类而言,应该仅有一个引起它变化的原因。简单来说,一个类中应该是一组相关性很高的函数、数据的封装。就像老师在《设计模式之禅》中说的:“这是一个备受争议却又及其重要的原则。只要你想和别人争执、怄气或者是吵架,这个原则是屡试不爽的”。因为单一职责的划分界限并不是总是那么清晰,很多时候都是需要靠个人经验来界定。当然,最大的问题就是对职责的定义,什么是类的职责,以及怎么划分类的职责。

对于计算机技术,通常只单纯地学习理论知识并不能很好地领会其深意,只有自己动手实践,并在实际运用中发现问题、解决问题、思考问题,才能够将知识吸收到自己的脑海中。下面以我的朋友小民的事情说起。

自从Android系统发布以来,小民就是Android的铁杆粉丝,于是在大学期间一直保持着对Android的关注,并且利用课余时间做些小项目,锻炼自己的实战能力。毕业后,小民如愿地加入了心仪的公司,并且投入到了他热爱的Android应用开发行业中。将爱好、生活、事业融为一体,小民的第一份工作也算是顺风顺水,一切尽在掌握中。

在经历过一周的适应期以及熟悉公司的产品、开发规范之后,小民的开发工作就正式开始了。小民的主管是个工作经验丰富的技术专家,对于小民的工作并不是很满意,尤其小民最薄弱的面向对象设计,而Android开发又是使用Java语言,程序中的抽象、接口、六大原则、23种设计模式等名词把小民弄得晕头转向。小民自己也察觉到了自己的问题所在,于是,小民的主管决定先让小民做一个小项目来锻炼这方面的能力。正所谓养兵千日用兵一时,磨刀不误砍柴工,小民的开发之路才刚刚开始。

在经过一番思考之后,主管挑选了使用范围广、难度也适中的图片加载器(ImageLoader)作为小民的训练项目。既然要训练小民的面向对象设计,那么就必须考虑到可扩展性、灵活性,而检测这一切是否符合需求的最好途径就是开源。用户不断地提出需求、反馈问题,小民的项目需要不断升级以满足用户需求,并且要保证系统的稳定性、灵活性。在主管跟小民说了这一特殊任务之后,小民第一次感到了压力,“生活不容易!”年仅22岁的小民发出了如此深刻的感叹!

挑战总是要面对的,何况是从来不服输的小民。主管的要求很简单,要小民实现图片加载,并且要将图片缓存起来。在分析了需求之后,小民一下就放心下来了,“这么简单,原来我还以为很难呢……”小民胸有成足地喃喃自语。在经历了10分钟的编码之后,小民写下了如下代码:

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

  public ImageLoader() {
    initImageCache();
  }

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

      @Override
      protected int sizeOf(String key, Bitmap bitmap) {
        return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
      }
    };
  }

  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)) {
        imageView.setImageBitmap(bitmap);
        }
      mImageCache.put(url, bitmap);
      }
    });
  }

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

并且使用Git软件进行版本控制,将工程托管到Github上,伴随着git push命令的完成,小民的ImageLoader 0.1版本就正式发布了!如此短的时间内就完成了这个任务,而且还是一个开源项目,小民暗暗自喜,并幻想着待会儿被主管称赞。

在小民给主管报告了ImageLoader的发布消息的几分钟之后,主管就把小民叫到了会议室。这下小民纳闷了,怎么夸人还需要到会议室。“小民,你的ImageLoader耦合太严重啦!简直就没有设计可言,更不要说扩展性、灵活性了。所有的功能都写在一个类里怎么行呢,这样随着功能的增多,ImageLoader类会越来越大,代码也越来越复杂,图片加载系统就越来越脆弱……”这简直就是当头棒喝,小民的脑海里已经听不清主管下面说的内容了,只是觉得自己之前没有考虑清楚就匆匆忙忙完成任务,而且把任务想得太简单了。

“你还是把ImageLoader拆分一下,把各个功能独立出来,让它们满足单一职责原则。”主管最后说道。小民是个聪明人,敏锐地捕捉到了单一职责原则这个关键词,他用Google搜索了一些资料之后,总算是对单一职责原则有了一些认识,于是打算对ImageLoader进行一次重构。这次小民不敢过于草率,也是先画了一幅UML图,如图1-1所示。

ImageLoader代码修改如下所示:

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

        // lk图片缓存
  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)) {
          imageView.setImageBitmap(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;
  }
}  
并且添加了一个ImageCache类用于处理图片缓存,具体代码如下:

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

  public ImageCache() {
    initImageCache();
  }

  private void initImageCache() {
    // 计算可使用的最大内存
    final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
    // 取四分之一的可用内存作为缓存
    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) ;
  }
}

如图1-1和上述代码所示,小民将ImageLoader一拆为二,ImageLoader只负责图片加载的逻辑,而ImageCache只负责处理图片缓存的逻辑,这样ImageLoader的代码量变少了,职责也清晰了;当与缓存相关的逻辑需要改变时,不需要修改ImageLoader类,而图片加载的逻辑需要修改时也不会影响到缓存处理逻辑。主管在审核了小民的第一次重构之后,对小民的工作给予了表扬,大致意思是结构变得清晰了许多,但是可扩展性还是比较欠缺。虽然没有得到主管的完全肯定,但也是颇有进步,再考虑到自己确实有所收获,小民原本沮丧的心里也略微地好转起来。

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

时间: 2025-01-27 07:14:27

《Android 源码设计模式解析与实战》——第1章,第1.1节优化代码的第一步——单一职责原则的相关文章

《Android 源码设计模式解析与实战》——导读

目 录 自序一 自序二 前言 致谢 第1章 走向灵活软件之路--面向对象的六大原则 1.1节优化代码的第一步--单一职责原则1.2节让程序更稳定.更灵活--开闭原则1.3节构建扩展性更好的系统--里氏替换原则1.4节让项目拥有变化的能力--依赖倒置原则1.5节系统有更高的灵活性--接口隔离原则1.6节更好的可扩展性--迪米特原则1.7节总结 第2章 应用最广的模式--单例模式 2.1节单例模式介绍2.2节单例模式的定义2.3节单例模式的使用场景2.4节单例模式UML类图2.5节单例模式的简单示例

《Android 源码设计模式解析与实战》——第2章,第2.1节单例模式介绍

第2章 应用最广的模式--单例模式Android 源码设计模式解析与实战 2.1 单例模式介绍单例模式是应用最广的模式之一,也可能是很多初级工程师唯一会使用的设计模式.在应用这个模式时,单例对象的类必须保证只有一个实例存在.许多时候整个系统只需要拥有一个全局对象,这样有利于我们协调系统整体的行为.如在一个应用中,应该只有一个ImageLoader实例,这个ImageLoader中又含有线程池.缓存系统.网络请求等,很消耗资源,因此,没有理由让它构造多个实例.这种不能自由构造对象的情况,就是单例模

《Android 源码设计模式解析与实战》——第2章,第2.7节Android源码中的单例模式

2.7 Android源码中的单例模式在Android系统中,我们经常会通过Context获取系统级别的服务,如WindowsManagerService.ActivityManagerService等,更常用的是一个LayoutInflater的类,这些服务会在合适的时候以单例的形式注册在系统中,在我们需要的时候就通过Context的getSystemService(String name)获取.我们以LayoutInflater为例来说明,平时我们使用LayoutInflater较为常见的地

《Android 源码设计模式解析与实战》——第2章,第2.8节无名英雄——深入理解LayoutInflater

2.8 无名英雄--深入理解LayoutInflater LayoutInflater在我们的开发中扮演着重要的角色,但很多时候我们都不知道它的重要性,因为它的重要性被隐藏在了Activity.Fragment等组件的光环之下. LayoutInflater是一个抽象类,具体代码如下: public abstract class LayoutInflater { // 代码省略 } 既然是抽象不是具体的,那我们必须把这个深藏功与名的"家伙"找出来!需要先从layoutInflater的

《Android 源码设计模式解析与实战》——第2章,第2.5节单例模式的简单示例

2.5 单例模式的简单示例单例模式是设计模式中比较简单的,只有一个单例类,没有其他的层次结构与抽象.该模式需要确保该类只能生成一个对象,通常是该类需要消耗较多的资源或者没有多个实例的情况.例如,一个公司只有一个CEO.一个应用只有一个Application对象等.下面以公司里的CEO为例来简单演示一下,一个公司可以有几个VP.无数个员工,但是CEO只有一个,请看下面示例. 示例实现代码 package com.dp.example.singleton; // 普通员工 public class

《Android 源码设计模式解析与实战》——第2章,第2.9节运用单例模式

2.9 运用单例模式 在Android应用开发过程中,ImageLoader是我们最为常用的开发工具库之一.Android中最著名的ImageLoader就是Universal-Image-Loader(https://github.com/nostra13/Android-Universal-Image- Loader),它的使用过程大概是这样的: public void initImageLoader(Context context) { // 1. 使用Builder构建ImageLoad

《Android 源码设计模式解析与实战》——第1章,第1.2节让程序更稳定、更灵活——开闭原则

1.2 让程序更稳定.更灵活--开闭原则 开闭原则的英文全称是Open Close Principle,缩写是OCP,它是Java世界里最基础的设计原则,它指导我们如何建立一个稳定的.灵活的系统.开闭原则的定义是:软件中的对象(类.模块.函数等)应该对于扩展是开放的,但是,对于修改是封闭的.在软件的生命周期内,因为变化.升级和维护等原因需要对软件原有代码进行修改时,可能会将错误引入原本已经经过测试的旧代码中,破坏原有系统.因此,当软件需要变化时,我们应该尽量通过扩展的方式来实现变化,而不是通过修

《Android 源码设计模式解析与实战》——第2章,第2.6节单例模式的其他实现方式

2.6 单例模式的其他实现方式 2.6.1 懒汉模式 懒汉模式是声明一个静态对象,并且在用户第一次调用getInstance时进行初始化,而上述的饿汉模式(CEO类)是在声明静态对象时就已经初始化.懒汉单例模式实现如下. public class Singleton { private static Singleton instance; private Singleton () {} public static synchronized Singleton getInstance() { if

《Android 源码设计模式解析与实战》——第1章,第1.5节系统有更高的灵活性——接口隔离原则

1.5 系统有更高的灵活性--接口隔离原则 接口隔离原则英文全称是InterfaceSegregation Principles,缩写是ISP.ISP的定义是:客户端不应该依赖它不需要的接口.另一种定义是:类间的依赖关系应该建立在最小的接口上.接口隔离原则将非常庞大.臃肿的接口拆分成更小的和更具体的接口,这样客户将会只需要知道他们感兴趣的方法.接口隔离原则的目的是系统解开耦合,从而容易重构.更改和重新部署. 接口隔离原则说白了就是,让客户端依赖的接口尽可能地小,这样说可能还是有点抽象,我们还是以