Có một lý do sử dụng khác nhau cho các lớp / giao diện trừu tượng trong C ++ và Java


13

Theo Herb Sutter, người ta nên thích các giao diện trừu tượng (tất cả các hàm ảo thuần túy) cho các lớp trừu tượng trong C ++ để tách rời việc thực hiện càng nhiều càng tốt. Trong khi cá nhân tôi thấy quy tắc này rất hữu ích, gần đây tôi đã tham gia một nhóm có nhiều lập trình viên Java và trong mã Java, hướng dẫn này dường như không tồn tại. Các hàm và các cài đặt của chúng được đặt rất thường xuyên trong các lớp trừu tượng. Tôi đã nhận được Herb Sutter hoàn toàn sai ngay cả đối với C ++ hoặc có sự khác biệt chung trong việc sử dụng các hàm trừu tượng trong C ++ so với Java. Các lớp trừu tượng với mã triển khai có ý nghĩa hơn trong Java so với C ++ và nếu có thì tại sao?


1
Tôi đã có một số nghi ngờ và cuối cùng đã đặt nó ở đây bởi vì nó có thể là do một số nguyên tắc thiết kế mà tôi thiếu về java oo. Vì vậy, đó không phải là về một lời khuyên chung chung mà là về cách sử dụng ngôn ngữ đúng và sai
Martin

Các giao diện có nghĩa là hoàn toàn ảo. Ý tưởng của các lớp trừu tượng là chúng được triển khai một phần và việc triển khai để lấp đầy các khoảng trống mà không cần lặp lại mã một cách không cần thiết (ví dụ, tại sao lại có ghi (byte) và ghi (int) trong mỗi lớp con khi bạn có thể có cuộc gọi lớp trừu tượng write (byte) từ write (int))

1
Có thể liên quan: stackoverflow.com/q/1231985/484230 đưa ra lý do các lớp trừu tượng ưa thích trong java. Đối với C ++, lý do này dường như không đúng do sự tồn tại của các chức năng miễn phí có thể thêm chức năng ở cấp giao diện
Martin

1
Tôi nghĩ rằng Nguyên tắc Vàng là "làm cho các lớp không lá trở nên trừu tượng", nhưng điều đó không đưa ra bất kỳ yêu cầu "chỉ thuần túy" hoặc "trống rỗng" nào.
Kerrek SB

1
Nếu nó làm việc cho bạn, nó làm việc cho bạn. Tôi thực sự không thấy lý do tại sao mọi người hoảng sợ khi mã của họ không còn tuân thủ các ý kiến ​​mới nhất.
James

Câu trả lời:


5

OOP có thành phần và thay thế.

C ++ có nhiều kế thừa, chuyên môn hóa mẫu, nhúng và ngữ nghĩa giá trị / di chuyển / con trỏ.

Java có sự kế thừa và giao diện đơn, ngữ nghĩa nhúng và tham chiếu.

Cách phổ biến mà trường OOP sử dụng các ngôn ngữ này là sử dụng tính kế thừa để thay thế đối tượng và nhúng cho thành phần. Nhưng bạn cũng cần một tổ tiên chung và một cách để tạo thời gian chạy (trong C ++ được gọi dynamic_cast, trong Java chỉ là yêu cầu một giao diện từ một giao diện khác).

Java thực hiện tất cả điều này bằng java.lang.Objecthierachy bắt nguồn từ chính nó . C ++ không có gốc chung được xác định trước, do đó, ít nhất bạn nên xác định nó, để đi đến cùng một "bức tranh" (nhưng điều này hạn chế một số khả năng của C ++ ...).

Sau đó, khả năng có đa hình thời gian biên dịch (nghĩ theo CRTP) và giá trị ngữ nghĩa cũng có thể đưa ra các lựa chọn thay thế khác cho cách khái niệm "đối tượng OOP" có thể được chuyển vào chương trình C ++.

Bạn thậm chí có thể tưởng tượng dị giáo để sử dụng nhúng và chuyển đổi ngầm định để quản lý thay thế và thừa kế riêng để quản lý thành phần, trong thực tế đảo ngược mô hình trường học truyền thống. (Tất nhiên, cách này trẻ hơn 20 tuổi so với cách khác, vì vậy đừng mong đợi sự hỗ trợ cộng đồng rộng rãi trong việc đó)

Hoặc bạn có thể tưởng tượng một cơ sở chung ảo cho tất cả các lớp, giao diện biểu mẫu (không triển khai) đến các lớp cuối cùng (được thực hiện đầy đủ) đi qua các giao diện được triển khai một phần, một cụm giao diện chẵn, sử dụng "sự thống trị" khi gửi từ giao diện đến các triển khai thông qua một "nhiều ngăn xếp Sơ đồ thừa kế ".

So sánh OOP với java với C ++ giả sử chỉ có một và chỉ có cách OOP là giới hạn khả năng của cả hai ngôn ngữ.

Việc buộc C ++ phải tuân thủ nghiêm ngặt các thành ngữ mã hóa Java đang làm mất màu C ++ khi buộc Java phải hành xử như một ngôn ngữ giống như C ++ đang làm mất màu Java.

Không phải là vấn đề "nhạy cảm" mà là "cơ chế tổng hợp" khác nhau, hai ngôn ngữ có và cách kết hợp chúng khác nhau làm cho một số thành ngữ có lợi hơn trong một ngôn ngữ so với ngôn ngữ kia và ngược lại.


1
Tôi nghĩ rằng câu trả lời này rất thú vị, vì nó mô tả ngắn gọn các tính năng ngôn ngữ như một công cụ cho oo và các nguyên tắc thiết kế chỉ như một sự trợ giúp chứ không phải là một học thuyết. Tuy nhiên, bạn không cần một root chung nếu bạn muốn làm oo trong c ++. Điều này chỉ đơn giản là sai, vì thực tế là bạn có các toán tử và các mẫu (là một thay thế rất mạnh cho thiết kế cây chính của java như bạn cũng đã chỉ ra). Ngoài ra, điểm của bạn là đáng giá nhất trong tất cả các câu trả lời
Martin

1
@ Martin: Trong "theo nghĩa kỹ thuật" Cậu nói đúng, nhưng nếu bạn cần thời gian chạy polymorophism (vì loại thực tế của các đối tượng được thuyết minh phụ thuộc vào đầu vào chương trình) một "root" ( 'a' là một bài viết, không phải là một phím tắt cho " một và chỉ ") là những gì làm cho tất cả các đối tượng" anh em họ "và hệ thống phân cấp có thể chạy theo thời gian. Rễ khác nhau có nguồn gốc tổ tiên khác nhau không liên quan với nhau. Cho dù điều này là "tốt" hay "xấu" là một vấn đề bối cảnh, không phải thành ngữ.
Emilio Garavaglia

Đúng. Tôi nghĩ rằng bạn đang đề cập đến một cách giả tạo một gốc chung cho toàn bộ chương trình c ++ và thấy nó là khiếm khuyết mà nó không có mặt, so với java. Nhưng sau khi chỉnh sửa, bạn làm cho điểm khá rõ ràng. Cảm ơn một lần nữa
Martin

12

Nguyên tắc giữ cho cả hai ngôn ngữ, nhưng bạn không làm một so sánh công bằng. Bạn nên so sánh các lớp trừu tượng thuần túy C ++ với các giao diện Java.

Ngay cả trong C ++, bạn có thể có các lớp trừu tượng có một số hàm được triển khai, nhưng xuất phát từ một lớp trừu tượng thuần túy (không có triển khai). Trong Java, bạn có cùng các lớp trừu tượng (với một số triển khai), có thể xuất phát từ các giao diện (không triển khai).


Vì vậy, khi nào bạn muốn một lớp trừu tượng hơn một lớp giao diện trong c ++. Tôi luôn chọn giao diện cộng với các chức năng không phải thành viên trong c ++.
Martin

1
@Martin mà phụ thuộc vào thiết kế. Về cơ bản, luôn luôn thích một giao diện. Nhưng quy tắc " luôn luôn " có ngoại lệ ...
Luchian Grigore

Đúng như vậy nhưng trong Mã Java tôi đang thấy các lớp trừu tượng đại diện cho đa số. Điều này có thể là do thực tế là các chức năng miễn phí làm việc trên các giao diện là không thể trong java?
Martin

3
Các chức năng miễn phí tốt của @Martin hoàn toàn không có trong Java, vì vậy đó có thể là một lý do, vâng. Điểm tốt! Trả lời câu hỏi của riêng bạn! Bạn có thể tự thêm một câu trả lời, tôi nghĩ đó là nó.
Luchian Grigore

4

Nói chung, các nguyên tắc OO tương tự đều đúng với Java và C ++. Tuy nhiên, một điểm khác biệt lớn là C ++ hỗ trợ đa kế thừa trong khi trong Java, bạn chỉ có thể kế thừa từ một lớp. Đây là lý do chính tại sao Java có giao diện mà tôi tin, để bổ sung cho việc thiếu nhiều kế thừa và có lẽ để hạn chế những gì bạn có thể làm với nó (vì có rất nhiều chỉ trích về việc lạm dụng nhiều kế thừa). Vì vậy, có lẽ trong suy nghĩ của các lập trình viên Java, có sự phân biệt mạnh mẽ hơn giữa các lớp và giao diện trừu tượng. Các lớp trừu tượng được sử dụng để chia sẻ và kế thừa hành vi trong khi các giao diện được sử dụng đơn giản để thêm chức năng bổ sung. Hãy nhớ rằng, trong Java bạn có thể kế thừa chỉ từ một lớp, nhưng bạn có thể có nhiều giao diện. Trong C ++ tuy nhiên, lớp trừu tượng thuần túy (tức là một "C ++ giao diện") Được sử dụng để chia sẻ và kế thừa hành vi không giống như mục đích của giao diện Java (mặc dù bạn vẫn được yêu cầu thực hiện các chức năng), do đó việc sử dụng khác với giao diện Java.


0

Đôi khi nó có ý nghĩa để có một số thực hiện mặc định. Ví dụ, một phương thức PrintError (chuỗi thông điệp) chung có thể áp dụng cho tất cả các lớp con.

virtual PrintError(string msg) { cout << msg; }

Nó vẫn có thể bị ghi đè nếu thực sự cần thiết, nhưng bạn có thể tiết kiệm cho khách hàng một số rắc rối bằng cách cho phép họ chỉ gọi phiên bản chung.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.