”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 组合与继承

组合与继承

发布于2024-11-09
浏览:873

Composición vs Herencia

介绍

继承和组合是面向对象编程(OOP)中的两个基本概念,但它们的用法不同并且具有不同的目的。这篇文章的目的是回顾这些目的,以及选择它们时要记住的一些事情。

继承的概念

当我们考虑在设计中应用继承时,我们必须了解:

  • 定义:在继承中,一个类(称为派生类或子类)可以从另一个类(称为基类或超类)继承属性和行为。派生类可以扩展或修改基类的功能。
  • Relationship:是一个关系“is a”(is-a)。例如,如果您有一个类“Vehicle”和另一个类“Car”,则类“Car”是“Vehicle”的子类。
  • 优点:促进代码重用并允许轻松扩展功能。

构图概念

另一方面,如果我们考虑将对象组合在一起:

  • 定义:在组合中,一个对象包含其他对象并将其部分功能委托给它们。类不使用继承,而是使用其他类的实例来实现其功能。
  • 关系:这是一种“has-a”关系。例如,如果您有一个类“Engine”和一个类“Car”,则类“Car”可以有一个类“Engine”的对象。
  • 优点:灵活性更大,类之间的耦合更少。一个类的更改不会直接影响另一个类。

为什么要对继承进行组合?

组合是否优于继承,或者反之亦然,这是软件设计中一个有争议的话题。两种方法都有其优点和缺点,选择取决于具体的项目背景和要求。在这里我将向您展示一个示例,其中组合比继承更可取。

让我们探讨一个 Java 示例,该示例说明了在某些情况下组合如何优于继承。假设我们正在开发在线商店的订单处理系统。

  • 继承方式:

首先,让我们考虑一种使用继承来表示不同类型的可购买产品的方法,例如书籍和电子产品:

// Clase base para productos
class Producto {
    String nombre;
    double precio;

    Producto(String nombre, double precio) {
        this.nombre = nombre;
        this.precio = precio;
    }

    void procesarPedido() {
        System.out.println("Procesando pedido para "   nombre);
    }
}

// Clase para productos electrónicos que hereda de Producto
class ProductoElectronico extends Producto {
    String modelo;

    ProductoElectronico(String nombre, double precio, String modelo) {
        super(nombre, precio);
        this.modelo = modelo;
    }
}

// Clase para libros que hereda de Producto
class Libro extends Producto {
    String autor;

    Libro(String nombre, double precio, String autor) {
        super(nombre, precio);
        this.autor = autor;
    }
}

这种方法可行,但是如果您需要引入新的产品类型或为某些产品类型添加特定功能怎么办?

  • 构图重点:

我们可以使用组合来更灵活地处理不同类型的产品,而不是完全依赖继承:

// Clase para productos
class Producto {
    String nombre;
    double precio;

    Producto(String nombre, double precio) {
        this.nombre = nombre;
        this.precio = precio;
    }

    void procesarPedido() {
        System.out.println("Procesando pedido para "   nombre);
    }
}

// Clase para productos electrónicos que utiliza composición
class ProductoElectronico {
    Producto producto;
    String modelo;

    ProductoElectronico(String nombre, double precio, String modelo) {
        this.producto = new Producto(nombre, precio);
        this.modelo = modelo;
    }

    // Puedes agregar lógica específica para productos electrónicos si es necesario
    void procesarPedidoEspecifico() {
        System.out.println("Procesando pedido específico para "   producto.nombre);
    }
}

// Clase para libros que utiliza composición
class Libro {
    Producto producto;
    String autor;

    Libro(String nombre, double precio, String autor) {
        this.producto = new Producto(nombre, precio);
        this.autor = autor;
    }

    // Puedes agregar lógica específica para libros si es necesario
    void procesarPedidoEspecifico() {
        System.out.println("Procesando pedido específico para "   producto.nombre);
    }
}

在此方法中,每种产品类型都有一个 Product 类的实例,允许共享通用逻辑来处理订单。此外,每种产品类型都可以使用 processSpecificOrder() 等方法拥有自己的特定逻辑。这种设计更加灵活,可以更轻松地引入新的产品类型或修改特定于类型的逻辑,而不会影响继承层次结构。

什么时候应用继承?

虽然软件设计中继承和组合之间的选择取决于您正在解决的问题的上下文和特定要求。在某些情况下,您可能会认为继承比组合更合适:

  • “is-a”关系:当类之间存在明确的“is-a”关系时,继承尤其合适。如果类 B 是类 A 的更具体或更专业的版本,那么继承就有意义。例如,如果您有一个 Vehicle 类和一个 Car 类,那么“is-a”关系就很清楚,因为汽车是一种车辆类型。
class Vehiculo {
    // ...
}

class Automovil extends Vehiculo {
    // ...
}
  • 代码重用:继承允许代码重用,因为派生类继承了基类的成员和方法。当相关类之间存在大量公共代码时,这会很有用。
class Animal {
    void comer() {
        // Lógica común para comer
    }
}

class Perro extends Animal {
    void ladrar() {
        // Lógica específica para ladrar
    }
}
  • 多态性:继承是多态性实现的基础,它允许派生类提供从基类继承的方法的特定实现。这在需要统一处理派生类的对象的场景中非常有用。
class Figura {
    void dibujar() {
        // Lógica común para dibujar una figura
    }
}

class Circulo extends Figura {
    void dibujar() {
        // Lógica específica para dibujar un círculo
    }
}

class Cuadrado extends Figura {
    void dibujar() {
        // Lógica específica para dibujar un cuadrado
    }
}

  • 功能扩展:如果您要扩展或专门化现有功能,继承可能会更自然。例如,如果您正在开发一个库并提供具有基本功能的基类,则您的库的用户可以从该基类派生来扩展该功能。

如果我们继承了不好的遗产,会发生什么?

如果我们继续评估继承的利弊,不良继承可能引起的问题之一是我们将违反接口隔离原则,该原则规定客户端不应被迫依赖于他们所做的接口不使用。如果接口以包含与所有实现不相关的方法的方式扩展,则使用该接口的客户端可能被迫实现或依赖于他们不需要的方法,这可能导致设计不太干净且更困难。维持。

结论

总之,继承侧重于“是”关系,用于对类层次结构进行建模,而组合则侧重于“有”关系,用于从其他更简单的对象构造复杂对象。两种方法都有其特定的用例,并根据软件设计中关系的结构和性质进行选择。

版本声明 本文转载于:https://dev.to/rlgino/composicion-vs-herencia-4664?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • Java是否允许多种返回类型:仔细研究通用方法?
    Java是否允许多种返回类型:仔细研究通用方法?
    在Java中的多个返回类型:一种误解类型:在Java编程中揭示,在Java编程中,Peculiar方法签名可能会出现,可能会出现,使开发人员陷入困境,使开发人员陷入困境。 getResult(string s); ,其中foo是自定义类。该方法声明似乎拥有两种返回类型:列表和E。但这确实是如此吗...
    编程 发布于2025-04-11
  • 如何限制动态大小的父元素中元素的滚动范围?
    如何限制动态大小的父元素中元素的滚动范围?
    在交互式接口中实现垂直滚动元素的CSS高度限制,控制元素的滚动行为对于确保用户体验和可访问性是必不可少的。一种这样的方案涉及限制动态大小的父元素中元素的滚动范围。问题:考虑一个布局,其中我们具有与用户垂直滚动一起移动的可滚动地图div,同时与固定的固定sidebar保持一致。但是,地图的滚动无限期...
    编程 发布于2025-04-11
  • 如何将多种用户类型(学生,老师和管理员)重定向到Firebase应用中的各自活动?
    如何将多种用户类型(学生,老师和管理员)重定向到Firebase应用中的各自活动?
    Red: How to Redirect Multiple User Types to Respective ActivitiesUnderstanding the ProblemIn a Firebase-based voting app with three distinct user type...
    编程 发布于2025-04-11
  • 如何在GO编译器中自定义编译优化?
    如何在GO编译器中自定义编译优化?
    在GO编译器中自定义编译优化 GO中的默认编译过程遵循特定的优化策略。 However, users may need to adjust these optimizations for specific requirements.Optimization Control in Go Compi...
    编程 发布于2025-04-11
  • 如何在php中使用卷发发送原始帖子请求?
    如何在php中使用卷发发送原始帖子请求?
    如何使用php 然后,配置以下选项: curlopt_url:请求 [要发送的原始数据指定内容类型,为原始的帖子请求指定身体的内容类型很重要。在这种情况下,它是文本/平原。要执行此操作,请使用包含以下标头的数组使用curlopt_httpheader选项:响应将存储在变量$ result。示例代码...
    编程 发布于2025-04-11
  • 如何实时捕获和流媒体以进行聊天机器人命令执行?
    如何实时捕获和流媒体以进行聊天机器人命令执行?
    在开发能够执行命令的chatbots的领域中,实时从命令执行实时捕获Stdout,一个常见的需求是能够检索和显示标准输出(stdout)在cath cath cant cant cant cant cant cant cant cant interfaces in Chate cant inter...
    编程 发布于2025-04-11
  • 如何将PANDAS DataFrame列转换为DateTime格式并按日期过滤?
    如何将PANDAS DataFrame列转换为DateTime格式并按日期过滤?
    Transform Pandas DataFrame Column to DateTime FormatScenario:Data within a Pandas DataFrame often exists in various formats, including strings.使用时间数据时...
    编程 发布于2025-04-11
  • 如何检查对象是否具有Python中的特定属性?
    如何检查对象是否具有Python中的特定属性?
    方法来确定对象属性存在寻求一种方法来验证对象中特定属性的存在。考虑以下示例,其中尝试访问不确定属性会引起错误: >>> a = someClass() >>> A.property Trackback(最近的最新电话): 文件“ ”,第1行, AttributeError: SomeClass...
    编程 发布于2025-04-11
  • 如何在其容器中为DIV创建平滑的左右CSS动画?
    如何在其容器中为DIV创建平滑的左右CSS动画?
    通用CSS动画,用于左右运动 ,我们将探索创建一个通用的CSS动画,以向左和右移动DIV,从而到达其容器的边缘。该动画可以应用于具有绝对定位的任何div,无论其未知长度如何。问题:使用左直接导致瞬时消失 更加流畅的解决方案:混合转换和左 [并实现平稳的,线性的运动,我们介绍了线性的转换。这...
    编程 发布于2025-04-11
  • PHP阵列键值异常:了解07和08的好奇情况
    PHP阵列键值异常:了解07和08的好奇情况
    PHP数组键值问题,使用07&08 在给定数月的数组中,键值07和08呈现令人困惑的行为时,就会出现一个不寻常的问题。运行print_r($月)返回意外结果:键“ 07”丢失,而键“ 08”分配给了9月的值。此问题源于PHP对领先零的解释。当一个数字带有0(例如07或08)的前缀时,PHP将其...
    编程 发布于2025-04-11
  • 如何克服PHP的功能重新定义限制?
    如何克服PHP的功能重新定义限制?
    克服PHP的函数重新定义限制在PHP中,多次定义一个相同名称的函数是一个no-no。尝试这样做,如提供的代码段所示,将导致可怕的“不能重新列出”错误。 但是,PHP工具腰带中有一个隐藏的宝石:runkit扩展。它使您能够灵活地重新定义函数。 runkit_function_renction_re...
    编程 发布于2025-04-11
  • 如何使用组在MySQL中旋转数据?
    如何使用组在MySQL中旋转数据?
    在关系数据库中使用mySQL组使用mySQL组进行查询结果,在关系数据库中使用MySQL组,转移数据的数据是指重新排列的行和列的重排以增强数据可视化。在这里,我们面对一个共同的挑战:使用组的组将数据从基于行的基于列的转换为基于列。 Let's consider the following ...
    编程 发布于2025-04-11
  • 如何干净地删除匿名JavaScript事件处理程序?
    如何干净地删除匿名JavaScript事件处理程序?
    删除匿名事件侦听器将匿名事件侦听器添加到元素中会提供灵活性和简单性,但是当要删除它们时,可以构成挑战,而无需替换元素本身就可以替换一个问题。 element? element.addeventlistener(event,function(){/在这里工作/},false); 要解决此问题,请考虑...
    编程 发布于2025-04-11
  • 如何使用Python理解有效地创建字典?
    如何使用Python理解有效地创建字典?
    在python中,词典综合提供了一种生成新词典的简洁方法。尽管它们与列表综合相似,但存在一些显着差异。与问题所暗示的不同,您无法为钥匙创建字典理解。您必须明确指定键和值。 For example:d = {n: n**2 for n in range(5)}This creates a dicti...
    编程 发布于2025-04-11
  • 我可以将加密从McRypt迁移到OpenSSL,并使用OpenSSL迁移MCRYPT加密数据?
    我可以将加密从McRypt迁移到OpenSSL,并使用OpenSSL迁移MCRYPT加密数据?
    将我的加密库从mcrypt升级到openssl 问题:是否可以将我的加密库从McRypt升级到OpenSSL?如果是这样,如何?答案:是的,可以将您的Encryption库从McRypt升级到OpenSSL。可以使用openssl。附加说明: [openssl_decrypt()函数要求iv参...
    编程 发布于2025-04-11

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3