面向对象原则之合成复用原则

合成复用原则

基本介绍

合成复用原则(Composite Reuse Principle, CRP),意为尽量使用对象组合,而不是继承来达到复用的目的。

合成复用原则就是在一个新的对象里通过关联关系(包括组合关系和聚合关系)来使用一些已有的对象,使之成为新对象的一部分;新对象通过委派调用已有对象的方法达到复用功能的目的。简言之:复用时要尽量使用组合/聚合关系(关联关系),少用继承。

在面向对象设计中,可以通过两种方法在不同的环境中复用已有的设计和实现,即通过组合/聚合关系或通过继承,但首先应该考虑使用组合/聚合,组合/聚合可以使系统更加灵活,降低类与类之间的耦合度,一个类的变化对其他类造成的影响相对较少;其次才考虑继承,在使用继承时,需要严格遵循里氏代换原则,有效使用继承会有助于对问题的理解,降低复杂度,而滥用继承反而会增加系统构建和维护的难度以及系统的复杂度,因此需要慎重使用继
承复用。

通过继承来进行复用的主要问题在于继承复用会破坏系统的封装性,因为继承会将基类的实现细节暴露给子类,由于基类的内部细节通常对子类来说是可见的,所以这种复用又称“白箱”复用,如果基类发生改变,那么子类的实现也不得不发生改变;从基类继承而来的实现是静态的,不可能在运行时发生改变,没有足够的灵活性。

由于组合或聚合关系可以将已有的对象(也可称为成员对象)纳入到新对象中,使之成为新对象的一部分,因此新对象可以调用已有对象的功能,这样做可以使得成员对象的内部实现细节对于新对象不可见,所以这种复用又称为“黑箱”复用,相对继承关系而言,其耦合度相对较低,成员对象的变化对新对象的影响不大,可以在新对象中根据实际需要有选择性地调用成员对象的操作。

合成复用可以在运行时动态进行,新对象可以动态地引用与成员对象类型相同的其他对象。一般而言,如果两个类之间是“Has-A”的关系应使用组合或聚合,如果是“Is-A”关系可使用继承。“Is-A"是严格的分类学意义上的定义,意思是一个类是另一个类的"一种”;而"Has-A"则不同,它表示某一个角色具有某一项责任。

代码示例

下面我们一个例子来加深对合成复用原则的理解:

public class DBUtil {
    public void connectionDataBase(){

    }
}

public class UserDao extends DBUtil{
    public void createUser(){
        super.connectionDataBase();
    }
}

上述代码中的DBUtilconnectionDataBase方法是用来连接数据库的,UserDao呢因为需要连接数据库,所以继承了DBUtil,最开始是使用MySQL,假如我们现在要换成Oracle,怎么办呢?要么新增一个OracleDBUtil,要么修改现有的DBUtil不管哪种方式都会违反开闭原则。下面我们根据合成复用原则对上述代码进行重构:

public class DBUtil {
    public void connectionDataBase(){

    }
}

public class OracleDBUtil extends DBUtil {
    public void connectionDataBase(){

    }
}
public class UserDao {
    private DBUtil dbUtil;

    public void setDbUtil(DBUtil dbUtil) {
        this.dbUtil = dbUtil;
    }

    public void createUser(){
        dbUtil.connectionDataBase();
    }
}

重构之后,我们将UserDaoDBUtil关系由继承变为了引用,可通过set方法设置对象,如果需要对DBUtil的功能进行扩展,可以通过其子类来实现,例如通过子类OracleDBUtil来连接Oracle数据库,这样只需要在UserDao中设置子类对象即可,原有的代码无需修改。

发布了107 篇原创文章 · 获赞 19 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/chen_changying/article/details/103657139
今日推荐