Java设计模式之设计原则-合成复用原则 (CRP)

👉文章示例代码👈

附链

你也可以在这些平台阅读本文:

定义

尽量使用对象组合(contains-A)/聚合(has-A),而不是通过继承(is-A)达到软件复用的目的。

类的复用一般分为两种:一种是组合/聚合,另一种则是继承

继承复用的优点在于扩展性较好,子类继承父类,父类的大部分功能都可以提供给子类使用,修改和扩展相对比较容易。

继承复用的缺点在于这种方式会破坏包装,继承会将父类的实现细节暴露给子类。

继承复用也叫白箱复用组合聚合复用也叫黑箱复用

场景示例

这里以数据访问层获取数据库连接,同时操作用户数据为例。

创建数据库连接类

创建一个数据库连接类,内部提供一个获取数据库连接的方法。

/**
 * @author zhh
 * @description 数据库连接
 * @date 2020-02-09 13:45
 */
public class DBConnection {

    /**
     * 获取数据库连接
     * @return
     */
    public String getConnection() {
        return "MySQL数据库连接";
    }
}

创建用户数据访问层

创建一个用户数据访问类,基础数据库连接类的同时,提供一个新增用户的方法。

/**
 * @author zhh
 * @description 用户数据访问层
 * @date 2020-02-09 13:51
 */
public class UserDao extends DBConnection {

    /**
     * 新增用户
     */
    public void addUser() {
        String connection = super.getConnection();
        System.out.println(String.format("使用%s添加用户", connection));
    }
}

测试类及输出

/**
 * @author zhh
 * @description 测试类
 * @date 2020-02-09 13:53
 */
public class Test {

    public static void main(String[] args) {
        UserDao userDao = new UserDao();
        userDao.addUser();
    }
}

测试类的输出结果如下:

使用MySQL数据库连接添加用户

类结构图

以上示例类的结构图如下所示

需求扩展

假设现在新旧系统合并,需要将旧有系统的 Oracle 数据库中的数据接入到新系统中。

而目前 DBConnection 中只会返回 MySQL 数据库的连接。

其实也简单,我们在 DBConnection 中再新增一个方法用于获取 Oracle 数据库的连接。功能实现上其实是没什么问题的,但是这样会违反开闭原则。

那如何利用合成复用原则对示例代码进行重构?代码如下:

创建数据库连接类

这里数据库连接类是一个抽象类,内部包含一个获取数据库连接的抽象方法,具体获取哪种数据库的连接则交于具体的子类去实现。

/**
 * @author zhh
 * @description 数据库连接
 * @date 2020-02-09 13:45
 */
public abstract class DBConnection {

    /**
     * 获取数据库连接
     * @return
     */
    public abstract String getConnection();
}

创建具体数据库连接子类

/**
 * @author zhh
 * @description MySQL数据库连接
 * @date 2020-02-09 14:16
 */
public class MySQLConnection extends DBConnection {

    @Override
    public String getConnection() {
        return "MySQL数据库连接";
    }
}

/**
 * @author zhh
 * @description Oracle数据库连接
 * @date 2020-02-09 14:16
 */
public class OracleConnection extends DBConnection {

    @Override
    public String getConnection() {
        return "Oracle数据库连接";
    }
}

创建用户数据访问层

通过组合的方法将数据库连接注入到用户数据访问层当中。

/**
 * @author zhh
 * @description 用户数据访问层
 * @date 2020-02-09 13:51
 */
public class UserDao {

    private DBConnection connection;

    public void setConnection(DBConnection connection) {
        this.connection = connection;
    }

    /**
     * 新增用户
     */
    public void addUser() {
        System.out.println(String.format("使用%s添加用户", connection.getConnection()));
    }
}

测试类及输出

/**
 * @author zhh
 * @description 测试类
 * @date 2020-02-09 13:53
 */
public class Test {

    public static void main(String[] args) {
        UserDao userDao = new UserDao();
        userDao.setConnection(new MySQLConnection());
        userDao.addUser();

        userDao.setConnection(new OracleConnection());
        userDao.addUser();
    }
}

测试类的输出结果如下:

使用MySQL数据库连接添加用户
使用Oracle数据库连接添加用户

如果还有扩展,则只要写与 MySQLConnection 平级的类继承 DBConnection ,而具体的选择则交由应用层。

类结构图

以上示例类的结构图如下所示

通过与场景示例中类图的对比,我们不难发现扩展后去掉了 UserDaoDBConnection 之间的继承关系,改成了一对一的组合关系。而 OracleConnectionMySQLConnection 都是继承 DBConnection 的,这一平级可以实现数据库连接的扩展,是符合开闭原则的。

优点

  • 提高系统的灵活性
  • 降低类与类之间的耦合
  • 一个类的变化对其他类造成的影响相对较少

缺点

建造的系统会有较多的对象需要管理

参考

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值