介绍
继承和组合是面向对象编程(oop)中的两个基本概念,但它们的用法不同,用途也不同。这篇文章的目的是回顾这些目的,以及选择它们时要记住的一些事情。
继承的概念
当我们考虑在设计中应用传统时,我们必须了解:
- 定义:在继承中,一个类(称为派生类或子类)可以从另一个类(称为基类或超类)继承属性和行为。派生类可以扩展或修改基类的功能。
- 关系:是一个“是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 } }
- 功能扩展:如果您要扩展或专门化现有功能,继承可能会更自然。例如,如果您正在开发一个库并提供具有基本功能的基类,则您的库的用户可以从该基类派生来扩展该功能。
如果我们继承了不好的遗产,会发生什么?
如果我们继续评估继承的利弊,不良继承可能产生的问题之一是我们将违反接口隔离原则,该原则规定客户端不应被迫依赖于他们所使用的接口不使用。如果接口以包含与所有实现不相关的方法的方式扩展,则使用该接口的客户端可能被迫实现或依赖于他们不需要的方法,这可能导致设计不太干净且更困难。维护。
结论
总之,继承侧重于“是”关系,用于对类层次结构进行建模,而组合侧重于“有”关系,用于从其他更简单的对象构造复杂对象。两种方法都有其特定的用例,并根据软件设计中关系的结构和性质进行选择。
以上就是组合与继承的详细内容,更多请关注php中文网其它相关文章!