」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > 組合與繼承

組合與繼承

發佈於2024-11-09
瀏覽:214

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]刪除
最新教學 更多>
  • 在 Go 中使用 WebSocket 進行即時通信
    在 Go 中使用 WebSocket 進行即時通信
    构建需要实时更新的应用程序(例如聊天应用程序、实时通知或协作工具)需要一种比传统 HTTP 更快、更具交互性的通信方法。这就是 WebSockets 发挥作用的地方!今天,我们将探讨如何在 Go 中使用 WebSocket,以便您可以向应用程序添加实时功能。 在这篇文章中,我们将介绍: WebSoc...
    程式設計 發佈於2024-11-13
  • Bootstrap 4 Beta 中的列偏移發生了什麼事?
    Bootstrap 4 Beta 中的列偏移發生了什麼事?
    Bootstrap 4 Beta:列偏移的刪除和恢復Bootstrap 4 在其Beta 1 版本中引入了重大更改柱子偏移了。然而,隨著 Beta 2 的後續發布,這些變化已經逆轉。 從 offset-md-* 到 ml-auto在 Bootstrap 4 Beta 1 中, offset-md-*...
    程式設計 發佈於2024-11-13
  • 大批
    大批
    方法是可以在物件上呼叫的 fns 數組是對象,因此它們在 JS 中也有方法。 slice(begin):將陣列的一部分提取到新數組中,而不改變原始數組。 let arr = ['a','b','c','d','e']; // Usecase: Extract till index ...
    程式設計 發佈於2024-11-13
  • 如何在 PHP 中組合兩個關聯數組,同時保留唯一 ID 並處理重複名稱?
    如何在 PHP 中組合兩個關聯數組,同時保留唯一 ID 並處理重複名稱?
    在 PHP 中組合關聯數組在 PHP 中,將兩個關聯數組組合成一個數組是常見任務。考慮以下請求:問題描述:提供的代碼定義了兩個關聯數組,$array1 和 $array2。目標是建立一個新陣列 $array3,它合併兩個陣列中的所有鍵值對。 此外,提供的陣列具有唯一的 ID,而名稱可能重疊。要求是建...
    程式設計 發佈於2024-11-13
  • 使用 html css 和 javascript 的圖片滑桿 carosual https://www.instagram.com/webstreet_code/
    使用 html css 和 javascript 的圖片滑桿 carosual https://www.instagram.com/webstreet_code/
    ?帶有縮圖和懸停效果的圖像輪播? 嘿,開發社群! ? 在我的最新影片中,我建立了一個優雅的圖像輪播,其縮圖突出顯示具有平滑懸停效果的活動圖像。這種互動式設計增強了使用者參與度,並為您的 Web 專案增添了現代感。 主要特點: 響應式佈局:輪播在所有螢幕尺寸上都能完美調整。 互動式縮圖:可點擊...
    程式設計 發佈於2024-11-12
  • React 的核心:理解元件重新渲染
    React 的核心:理解元件重新渲染
    在學習程式語言時,我們經常深入研究語法並專注於快速建立某些東西,有時會忽略一個關鍵問題:這種語言實際上解決了什麼問題,以及它在幕後如何運作?將我們的注意力轉移到理解語言的核心目的和機制上,可以讓學習速度更快、適應性更強,使我們能夠輕鬆駕馭最複雜的項目。語法總是可以找到的——即使是最經驗豐富的開發人員...
    程式設計 發佈於2024-11-12
  • JavaScript 中的 Deferreds、Promise 和 Future 有什麼區別?
    JavaScript 中的 Deferreds、Promise 和 Future 有什麼區別?
    JavaScript 中 Deferreds、Promise 和 Future 的區別在 JavaScript 中,deferreds、promise 和 futures 通常用於處理非同步操作。這些概念中的每一個都有其獨特的一組特徵:Deferreds在正式文件中從未明確定義,deferreds ...
    程式設計 發佈於2024-11-12
  • 為什麼我的 Web 應用程式中的請求之間沒有維護 Gorilla 會話變數?
    為什麼我的 Web 應用程式中的請求之間沒有維護 Gorilla 會話變數?
    使用 Gorilla 會話時未維護會話變數問題使用 Gorilla Sessions Web 工具包時,會話變數不會跨請求保留。當伺服器啟動並且使用者存取 localhost:8100/ 時,他們將被導向到 login.html,因為會話值不存在。登入後,會話變數將被存儲,並且使用者將被重定向到 h...
    程式設計 發佈於2024-11-12
  • 如何在Python中像“column -t”指令一樣顯示列化資料?
    如何在Python中像“column -t”指令一樣顯示列化資料?
    在 Python 中顯示列式資料在命令列管理工具領域,通常需要以良好對齊的方式呈現資料列。雖然製表符提供了一種簡單的解決方案,但在處理不同長度的資料時它們會失敗。本文旨在透過提出受 Linux「column -t」命令行為啟發的 Python 解決方案來應對這項挑戰。 Python 提供了一個強大的...
    程式設計 發佈於2024-11-12
  • 在 NumPy 數組中尋找特定行的有效方法:問題和解決方案
    在 NumPy 數組中尋找特定行的有效方法:問題和解決方案
    高效查找NumPy 數組中特定行的實例使用NumPy 數組時,可能會遇到需要確定是否array 包含特定行,但ndarray 的標準contains 方法引發了問題。本文針對此問題提出了高效且 Python 的解決方案。 一種方法涉及使用 .tolist() 將 NumPy 數組轉換為 Python...
    程式設計 發佈於2024-11-12
  • 如何解決在伺服器上使用 Matplotlib 的 Python 腳本的「_tkinter.TclError:無顯示名稱且無 $DISPLAY 環境變數」問題?
    如何解決在伺服器上使用 Matplotlib 的 Python 腳本的「_tkinter.TclError:無顯示名稱且無 $DISPLAY 環境變數」問題?
    _tkinter.TclError:沒有顯示名稱,也沒有$DISPLAY 環境變數_tkinter.TclError:沒有顯示名稱,也沒有$DISPLAY 環境變數問題使用Matplotlib 的Python 腳本在伺服器上失敗,並出現錯誤「產生繪圖時沒有顯示名稱和$DISPLAY 環境變數」。出現...
    程式設計 發佈於2024-11-12
  • 如何使用 Apache Commons IO 在 Java 中遞歸刪除目錄?
    如何使用 Apache Commons IO 在 Java 中遞歸刪除目錄?
    在 Java 中遞歸刪除目錄在 Java 中刪除空目錄非常簡單。然而,當處理包含子目錄和檔案的目錄時,該過程變得更加複雜。本文深入探討了使用 Apache Commons IO 函式庫遞歸刪除整個目錄的有效方法。 Apache Commons IO 簡介Apache Commons IO 提供了一套...
    程式設計 發佈於2024-11-12
  • 為什麼即使在同一包中使用 FXML,我的 JavaFX 應用程式也會拋出“Location is required.”錯誤?
    為什麼即使在同一包中使用 FXML,我的 JavaFX 應用程式也會拋出“Location is required.”錯誤?
    JavaFX「需要位置。」儘管FXML 位於同一個套件中仍出現錯誤在JavaFX 應用程式中,遇到「java .lang.NullPointerException: Location is required」錯誤通常表示無法載入FXML 檔案。即使 FXML 檔案與 Application 類別位於...
    程式設計 發佈於2024-11-12
  • `std::enable_if` 是如何運作的:揭開其實現和使用的神秘面紗?
    `std::enable_if` 是如何運作的:揭開其實現和使用的神秘面紗?
    理解std::enable_if:破解其目的和實現理解std::enable_if:破解其目的和實現雖然std::enable_if 的本質是在某些上下文中掌握的,但它的錯綜複雜的問題,特別是模板語句中的第二個參數和對std::enable_if 的賦值,仍然是個謎。深入研究其工作原理將解開這些謎團...
    程式設計 發佈於2024-11-12
  • 如何在 Go 中實作 Python 風格的生成器,同時避免記憶體洩漏?
    如何在 Go 中實作 Python 風格的生成器,同時避免記憶體洩漏?
    Go 中的Python 風格產生器了解通道緩衝區在您的程式碼中,您觀察到增加通道緩衝區大小從1 到10 透過減少上下文切換來增強效能。這個觀念是正確的。較大的緩衝區允許 fibonacci goroutine 提前填充多個點,從而減少 goroutine 之間持續通訊的需要。 通道生命週期和記憶體管...
    程式設計 發佈於2024-11-12

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3