美文网首页
设计原则(7) : 合成(组合)聚合复用原则

设计原则(7) : 合成(组合)聚合复用原则

作者: a_salt_fish | 来源:发表于2019-01-14 09:16 被阅读0次

    尽量使用对象组合/聚合, 而不是继承关系达到软件复用的目的


    话不多说先上代码

    • 业务场景: 用mysql数据库保存产品

    定义数据库连接DBConnection, 直接返回Mysql的连接

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

    Dao层

    public class ProductDao1 {
        public void addProduct(DBConnection1 dbConnection){
            String conn = dbConnection.getConnection();
            System.out.println("使用"+conn+"增加产品");
        }
    }
    

    测试类

    public class Test1 {
        public static void main(String[] args) {
            ProductDao1 productDao = new ProductDao1();
            DBConnection1 dbConnection = new DBConnection1();
            productDao.addProduct(dbConnection);
        }
    }
    

    输出

    使用MySQL数据库连接增加产品
    

    没有问题, 产品成功存入mysql, 这时需求变更,要在系统中加入新的数据库如PostgreSQL
    首先我们想到直接在DBConnection1中增加方法

        public String getPostgreSQLConnection(){
            return "PostgreSQL数据库连接";
        }
    

    但是这样就违反了开闭原则


    合成复用原则重构
    重新定义 DBConnection, 修改为一个抽象类

    public abstract class DBConnection2 {
        public abstract String getConnection();
    }
    

    增加Mysql实现

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

    重写DAO层

    public class ProductDao2 {
    
        private DBConnection2 dbConnection2;
    
        public void setDbConnection2(DBConnection2 dbConnection2) {
            this.dbConnection2 = dbConnection2;
        }
    
        public void addProduct(){
            String conn = dbConnection2.getConnection();
            System.out.println("使用"+conn+"增加产品");
        }
    }
    

    测试类

    public class Test2 {
        public static void main(String[] args) {
            ProductDao2 productDao2 = new ProductDao2();
            productDao2.setDbConnection2(new MySQLConnection());
            productDao2.addProduct();
        }
    }
    

    这时需求变更, 需要加入PostgreSQL
    只需要新增 PostgreSQL Connection的实现PostgreSQLConnection

    public class PostgreSQLConnection extends DBConnection2 {
        @Override
        public String getConnection() {
            return "PostgreSQL数据库连接";
        }
    }
    

    测试类修改数据源即可

    public class Test2 {
        public static void main(String[] args) {
            ProductDao2 productDao2 = new ProductDao2();
            //productDao2.setDbConnection2(new MySQLConnection());
            //productDao2.addProduct();
            productDao2.setDbConnection2(new PostgreSQLConnection());
            productDao2.addProduct();
        }
    }
    

    合成复用原则的核心思想是用合成/聚合复用 来代替继承复用
    降低类的耦合性

    合成聚合复用 与 继承复用的比较(为什么使用合成/聚合复用,而不使用继承复用)

    在面向对象的设计里,有两种基本的方法可以在不同的环境中复用已有的设计和实现,即通过合成/聚合复用和通过继承复用。两者的特点和区别,优点和缺点如下。

    1、合成/聚合复用

    由于合成或聚合可以将已有对象纳入到新对象中,使之成为新对象的一部分,因此新对象可以调用已有对象的功能。这样做的好处有

    (1) 新对象存取成分对象的唯一方法是通过成分对象的接口。

    (2) 这种复用是黑箱复用,因为成分对象的内部细节是新对象看不见的。

    (3) 这种复用支持包装。

    (4) 这种复用所需的依赖较少。

    (5) 每一个新的类可以将焦点集中到一个任务上。

    (6) 这种复用可以再运行时间内动态进行,新对象可以动态地引用与成分对象类型相同的对象。

    一般而言,如果一个角色得到了更多的责任,那么可以使用合成/聚合关系将新的责任委派到合适的对象。当然,这种复用也有缺点。最主要的缺点就是通过这种复用建造的系统会有较多的对象需要管理。

    2、继承复用

    继承复用通过扩展一个已有对象的实现来得到新的功能,基类明显的捕获共同的属性和方法,而子类通过增加新的属性和方法来扩展超类的实现。继承是类型的复用。

    继承复用的优点。

    (1) 新的实现较为容易,因为超类的大部分功能可以通过继承关系自动进入子类。

    (2) 修改或扩展继承而来的实现较为容易。

    继承复用的缺点。

    (1) 继承复用破坏包装,因为继承将超类的实现细节暴露给了子类。因为超类的内部细节常常对子类是透明的,因此这种复用是透明的复用,又叫“白箱”复用。

    (2) 如果超类的实现改变了,那么子类的实现也不得不发生改变。因此,当一个基类发生了改变时,这种改变会传导到一级又一级的子类,使得设计师不得不相应的改变这些子类,以适应超类的变化。

    (3) 从超类继承而来的实现是静态的,不可能在运行时间内发生变化,因此没有足够的灵活性。

    由于继承复用有以上的缺点,所有尽量使用合成/聚合而不是继承来达到对实现的复用,是非常重要的设计原则。

    参考 https://blog.csdn.net/u010832572/article/details/45007933


    优点
    • 使系统更加灵活

    github源码

    相关文章

      网友评论

          本文标题:设计原则(7) : 合成(组合)聚合复用原则

          本文链接:https://www.haomeiwen.com/subject/fdwldqtx.html