设计模式(Java版)
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

2.1 单一职责原则

单一职责原则的英文名称是Single Responsibility Principle,简称SRP。

2.1.1 单一职责原则的定义

单一职责原则的英文原话是:

There should never be more than one reason for a class to change.

意思是:一个类,应当只有一个引起它变化的原因;即一个类应该只有一个职责。

就一个类而言,应该只专注于做一件事和仅有一个引起变化的原因,这就是所谓的单一职责原则。该原则提出了对对象职责的一种理想期望,对象不应该承担太多职责,正如人不应该一心分为二用。唯有专注,才能保证对象的高内聚;唯有单一,才能保证对象的细粒度。对象的高内聚与细粒度有利于对象的重用。一个庞大的对象承担了太多的职责,当客户端需要该对象的某一个职责时,就不得不将所有的职责都包含进来,从而造成冗余代码。

单一职责原则还有利于对象的稳定。所谓“职责”,就是对象能够承担的责任,并以某种行为方式来执行。对象的职责总是要提供给其他对象进行调用的,从而形成对象与对象的协作,由此产生对象之间的依赖关系。类的职责越少,则对象之间的依赖关系就越少,耦合度就减弱,受其他对象的约束与牵制就越少,从而保证了系统的可扩展性。因此,在单一职责原则中,也可以把“职责”定义为“变化的原因”。如果存在多个动机去改变一个类,那么这个类就具有多于一个的职责。“变化的原因”只有实际发生时才有意义,可能预测到会有多个原因引起这个类的变化,但这仅仅是预测,并没有真的发生,那么这个类仍可看做具有单一职责,不需要分离职责。

注意 每一个职责都是变化的一个轴线,如果一个类有一个以上的职责,这些职责就耦合在了一起,这会导致脆弱的设计。当一个职责发生变化时,可能会影响其他的职责。另外,多个职责耦合在一起,也会影响复用性。

单一职责原则并不是极端地要求我们只能为类定义一个职责,而是利用极端的表述方式重点强调,在定义对象职责时,必须考虑职责与对象之间的所属关系。职责必须恰如其分地表现对象的行为,而不至于破坏和谐与平衡的美感,甚至格格不入。换言之,该原则描述的单一职责指的是公开在外的与该对象紧密相关的一组职责。例如,如图2-1所示,在媒体播放器中,可以在MediaPlayer类中定义一组与媒体播放相关的方法,如open()、play()、stop()等。这些方法从职责的角度来讲是内聚的,完全符合单一职责原则中“专注于做一件事”的要求。如果需求发生扩充,还需要提供上传、下载媒体文件的功能,那么在设计时,就应该定义一个新类,如MediaTransfer,由它来承担这一职责,而不是为了方便,草率地将其添加到MediaPlayer类中。

图2-1 职责划分后的类图

单一职责原则的优点有以下几个方面:

■ 降低类的复杂性;

■ 提高类的可读性;

■ 提高代码的可维护性和复用性;

■ 降低因变更引起的风险。

2.1.2 单一职责原则的应用

Java EE 中的分层框架模式实际上体现了单一职责原则,它将整个系统按照职责的内聚性分为不同的层,层内的模块与类具有宏观的内聚性,所关注的事情是一致的。例如,业务逻辑层主要关注系统的业务逻辑与业务流,而数据访问层则只负责数据的持久化与访问。

下述内容用于实现任务描述 2.D.1,以用户管理为例演示单一职责原则。在业务逻辑层中定义类 UserManager,在数据访问层定义类 UserDao,在实体对象层定义类 User,每个类具有不同的职责和功能,如图2-2所示。

图2-2 职责清晰的分层框架类图

实体类User的源代码如下所示。

【描述2.D.1】 User.java

    public class User {
        private String username;
        private String userpass;
        private int role;
        ...... // 各个属性的get、set方法
    }

用户的数据访问类UserDao的代码如下所示。

【描述2.D.1】 UserDao.java

    public class UserDao extends BaseDao {
        // 返回所有用户
        public List<User> getAllUser() {
            List<User> userList = new ArrayList<User>();
            String sql = "SELECT * FROM userdetail";
            try {
                ResultSet rs = this.executeQuery(sql, null);
                while (rs.next()) {
                    User user = new User();
                    user.setUsername(rs.getString("username"));
                    user.setUserpass(rs.getString("userpass"));
                    user.setRole(rs.getInt("role"));
                    userList.add(user);
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                this.closeAll();
            }
            return userList;
        }
        // 根据用户名查找用户
        public User getUserByName(String name) {
            User user = null;
            String sql = "SELECT * FROM userdetail WHERE username=?";
            try {
                ResultSet rs = this.executeQuery(sql, new String[] { name });
                if (rs.next()) {
                    user = new User();
                    user.setUsername(rs.getString("username"));
                    user.setUserpass(rs.getString("userpass"));
                    user.setRole(rs.getInt("role"));
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                this.closeAll();
            }
            return user;
        }
        // 添加新用户
        public boolean addUser(User user) {
            boolean r = false;
            try {
                String sql = "INSERT INTO userdetail(username, userpass,role) values
    (?,?,?)";
                int rs = this.executeUpdate(sql,
                        new String[] { user.getUsername(), user.getUserpass(),"" +
    user.getRole()});
                if (rs > 0) {
                    r = true;
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                this.closeAll();
            }
            return r;
        }
    }

用户管理类UserManager的源代码如下所示。

【描述2.D.1】 UserManager.java

    public class UserManager {
        private UserDao userDao = new UserDao();
        // 验证用户名和密码
        public boolean checkUser(String name, String pass) {
            boolean flag = false;
            User user = userDao.getUserByName(name);
            if (user != null && user.getUsername().equals(pass)) {
                flag = true;
            }
            return flag;
        }
        // 注册新用户
        public void registUser(User user) {
            if (userDao.getUserByName(user.getUsername()) != null) {
                System.out.print("此用户名已注册,请换一个新的用户名");
                return;
            }
            if (userDao.addUser(user)) {
                System.out.println("新用户注册成功");
            } else {
                System.out.println("新用户注册失败");
            }
        }
    }

单一职责原则提出了一个编写程序的标准,用“职责”或“变化原因”来衡量接口或类设计是否优良,但“职责”和“变化原因”都是不可度量的,因项目而异,因环境而异。