Chúng ta có thực sự cần ngôn ngữ OO để quản lý độ phức tạp của phần mềm không?


209

Đây sẽ là một câu hỏi rất phi kỹ thuật, mềm mại và tôi không chắc đây có phải là nền tảng phù hợp hay không. Nhưng tôi là một sinh viên CS mới bắt đầu nên tôi hy vọng các bạn chịu đựng được.

Trong học kỳ đầu tiên, chúng tôi đã được giới thiệu các khái niệm OOP như đóng gói, ẩn dữ liệu, mô đun hóa, kế thừa, v.v. thông qua Java và UML. (Java là ngôn ngữ lập trình đầu tiên của tôi)

Theo cách tôi hiểu, OOP là một cách quản lý sự phức tạp của phần mềm. Nhưng các nguyên tắc của nó không phải là mới hay duy nhất, chúng có ý nghĩa phổ quát cho tất cả các lĩnh vực kỹ thuật.

Ví dụ, một chiếc xe hơi là một cấu trúc rất phức tạp, có độ phức tạp được quản lý bởi một hệ thống các thành phần mô đun và đóng gói với các hành vi và giao diện được xác định rõ.

Nhưng tôi không hiểu lý do đằng sau việc giới thiệu một mô hình lập trình mới. Tôi nghĩ rằng tất cả các nguyên tắc được sử dụng để quản lý sự phức tạp có thể được nhận ra bằng các ngôn ngữ lập trình thủ tục. Ví dụ, đối với tính mô đun, chúng ta chỉ có thể chia chương trình thành nhiều chương trình nhỏ thực hiện các tác vụ được xác định rõ có mã được chứa trong các tệp riêng biệt. Các chương trình này sẽ tương tác với nhau thông qua đầu vào và đầu ra được xác định rõ. Các tập tin có thể được bảo vệ (mã hóa?) Để đạt được đóng gói. Để sử dụng lại mã, chúng ta chỉ cần gọi các tệp đó bất cứ khi nào cần trong các chương trình mới. Điều này không nắm bắt được tất cả những gì OOP là hay tôi đang thiếu một cái gì đó rất rõ ràng?

Tôi không yêu cầu một bằng chứng rằng OOP quản lý sự phức tạp. Theo tôi thì chắc chắn là có. Nhưng tôi nghĩ rằng tất cả các nguyên tắc được sử dụng để quản lý sự phức tạp như mô đun hóa, đóng gói, ẩn dữ liệu và có thể được thực hiện rất dễ dàng bằng các ngôn ngữ thủ tục. Vậy tại sao thực sự OOP nếu chúng ta có thể quản lý sự phức tạp mà không có nó?


41
Bạn đang thiếu sự tách biệt của giao diện và thực hiện. Có thể hoán đổi một thực hiện cho một thực thi khác trong thời gian chạy là một tính năng rất quan trọng. Về cơ bản, điều này đạt được bằng cách gửi phương thức động của các ngôn ngữ OO có tính kế thừa. Các ngôn ngữ thủ tục cũng có thể làm điều đó (đọc: void con trỏ), nhưng không có loại an toàn.
marstato 20/03/2017

81
Ở một mức độ lớn, ý tưởng về các ngôn ngữ và thiết kế hướng đối tượng chính xác là làm cho các khái niệm phổ quát, trực quan đó trở nên dễ dàng nhất có thể để biểu diễn và tái tạo trong mã. Nếu bạn có một kế hoạch hoặc một bộ hướng dẫn về cách đạt được tất cả những điều đó mà không có ngôn ngữ hướng đối tượng vốn có, thì đề xuất của bạn về cách thực hiện mọi thứ sẽ được sử dụng một cách hiệu quả phương pháp hướng đối tượng được sử dụng. Các ngôn ngữ OO thực tế chỉ là một cách để chính thức hóa và đơn giản hóa điều đó.
Trở lại

14
@RobbieDee bạn đã thực sự đọc câu hỏi của tôi? Đó là về việc cố gắng hiểu OO ở cấp độ cơ bản hơn bằng cách đặt câu hỏi về thời tiết, độ phức tạp của phần mềm có thể được quản lý mà không cần OO. Tôi không cố làm suy yếu OO, không cố gắng phát minh ra một cái gì đó mới, tôi chỉ đang cố gắng để hiểu nó tốt hơn và nếu câu hỏi đó là 'hiển nhiên' thì tại sao nó lại nhận được câu trả lời xuất sắc từ Jorg?
steakexchange

12
Mã hóa một tập tin không được đóng gói. Bạn có thể bị che khuất khi nhìn thấy nội dung của mã từ nhà phát triển khác, nhưng bạn không nhất thiết phải bảo vệ hoạt động bên trong của mã khỏi mã khác. Tác giả ban đầu có thể làm điều này trước hoặc sau khi mã hóa nếu anh ta có thể nhớ lại như thế nào.
JeffO

8
Bạn không cần bất cứ thứ gì ngoài ngôn ngữ máy - hãy để lập trình viên ghi nhớ các opcode và tự viết các số và số không. Nhưng có một số loại ngôn ngữ "tượng trưng" rất hữu ích trong việc giảm lỗi và tăng năng suất, và, như Dijkstra đã quan sát, một ngôn ngữ áp đặt một số "cấu trúc" (hoặc ít nhất là giúp duy trì "cấu trúc" dễ dàng hơn). Ngôn ngữ OO có thể không phải là công nghệ lý tưởng, với mức độ tinh vi ngôn ngữ hiện tại, nhưng chúng khá tốt cho nhiều ứng dụng. Ý tưởng là để quản lý sự phức tạp mà không cản trở bạn.
Daniel R Hicks

Câu trả lời:


177

Hãy để tôi thử với một câu trả lời lý thuyết thực sự thấp :)

Điều bạn thực sự hỏi là: Tại sao lại bao gồm hỗ trợ cho Định hướng đối tượng (OO) trực tiếp bằng ngôn ngữ khi ngôn ngữ thủ tục có thể được sử dụng để thiết kế và viết mã OO?

Và câu trả lời là: Để có một tiêu chuẩn về cách OO được thể hiện trong mã nguồn để bạn không kết thúc với 22 cách triển khai khác nhau cho cùng một sự trừu tượng hóa.

Ví dụ: giả sử tôi tạo một MagicButtonvà một MagicSlidercái có thể được sử dụng trong hệ thống giao diện người dùng. Tôi cần một cách để nhóm các phương thức có thể được sử dụng với MagicButton, các phương thức chỉ có thể được sử dụng với MagicSlider và các phương thức có thể được sử dụng bởi cả hai. Các đối tượng này chia sẻ một số phương thức vì cả hai đều là đối tượng Magic gui.

Tôi có thể thực hiện việc nhóm bằng cách đặt tên các hàm theo một cách đặc biệt MagicSlider_DoSomething ..., bằng cách bao gồm các phương thức trong các tệp cụ thể được đặt tên theo một cách đặc biệt MagicSliderMethods.XXXhoặc tôi có thể tìm thấy một số cách đặc biệt khác để làm điều tương tự. Nếu không có cách chuẩn trong ngôn ngữ để làm điều đó tôi sẽ làm nó khác với bạn, và khác với bất kỳ ai khác. Điều này làm cho việc chia sẻ mã khó khăn hơn nhiều.

Có, gửi muộn - các phương thức ảo trong các ngôn ngữ OO - có thể được thực hiện bằng các ngôn ngữ thủ tục, nhưng có rất nhiều cách khác nhau để thực hiện nó. Tùy thuộc vào người đã viết mã, bạn sẽ kết thúc với việc triển khai OO khác nhau trong cùng một chương trình.

Hãy suy nghĩ về các nhà phát triển bảo trì kém. Người này phải quản lý các trừu tượng đối tượng khác nhau và các cách khác nhau để gọi các phương thức ảo tùy thuộc vào người đã viết mã gốc.

Ngoài ra: Có các khái niệm trừu tượng trong ngôn ngữ cho phép các trình soạn thảo mã nâng cao như Eclipse thực hiện nhiều phân tích tĩnh trên mã. Ví dụ, Eclipse có thể cung cấp một danh sách tất cả các phương thức có thể được sử dụng trên một đối tượng, cũng như tự động triển khai các "phương thức TODO" trống rỗng. Eclispe biết chính xác các phương thức mà lớp của bạn phải thực hiện dựa trên các lớp bạn mở rộng và giao diện nào bạn thực hiện. Điều này gần như là không thể nếu không có tiêu chuẩn ngôn ngữ để thực hiện OO.


40
Ví dụ cổ điển: Lua. Nó không phải là OO, nhưng có thể được tạo ra, nhưng điều này có nghĩa là có khoảng 5 thư viện OO nổi tiếng khác nhau không hoàn toàn tương thích.
Kroltan 20/03/2017

55
@steakexchange Bạn tập trung vào tuyệt đối quá nhiều. Rất ít có một "mục đích duy nhất". Tất cả các ngôn ngữ đều làm rất nhiều thứ khác nhau cho các mức độ khác nhau về chất lượng. Chọn một ngôn ngữ là chọn tập hợp các sự đánh đổi làm cho nó phù hợp nhất với mục đích mà bạn cần nó.
Tim B

42
@nocomprende Chuẩn hóa các khái niệm trừu tượng gần như theo nghĩa đen của ngôn ngữ lập trình. Ngay cả ngôn ngữ lắp ráp cũng trừu tượng về sự khác biệt giữa mười thế hệ phần cứng trong một thập kỷ rưỡi.
David Moles

56
@DavidMoles Trừu tượng chuẩn hóa theo nghĩa đen là ngôn ngữ lập trình. Đừng lãng phí một cơ hội hoàn hảo để sử dụng "theo nghĩa đen" theo nghĩa đen!
Clement Cherlin

12
Có thể tiêu chuẩn hóa điều này. Khi tôi ở uni vào giữa những năm 90, tôi đã làm một số lượng công việc khá lớn trong X-Windows (chủ yếu dựa trên Motif, cho những người nhớ những thứ như vậy). X-Windows thực sự đã cho phép bạn thực hiện tất cả các tính năng định hướng đối tượng trong C thông thường . Thể dục dụng cụ tinh thần để làm như vậy là khá đáng kể, và phụ thuộc rất nhiều vào những người không nhìn vào bên trong hộp (lúc đó Mã Widget của Schroedinger thường đã chết). Các ngôn ngữ OO che giấu điều này khỏi các lập trình viên, giống như cách mà một trình biên dịch thông thường làm cho trình biên dịch chương trình, và cuộc sống dễ dàng hơn.
Graham

211

Trong học kỳ đầu tiên, chúng tôi đã được giới thiệu các khái niệm OOP như đóng gói, ẩn dữ liệu, mô đun hóa, kế thừa, v.v. thông qua Java và UML. (Java là ngôn ngữ lập trình đầu tiên của tôi)

Không ai trong số đó là khái niệm OOP. Tất cả đều tồn tại bên ngoài OO, độc lập với OO và nhiều người thậm chí đã được phát minh trước OO.

Vì vậy, nếu bạn nghĩ rằng đó là tất cả những gì về OO, thì kết luận của bạn là đúng: bạn có thể thực hiện tất cả những điều đó bằng ngôn ngữ thủ tục, vì chúng không liên quan gì đến OO .

Ví dụ, một trong những bài báo chuyên đề về Modularity là về Tiêu chí được sử dụng trong việc phân tách các hệ thống thành các mô-đun . Không có đề cập đến OO trong đó. (Nó được viết vào năm 1972, khi đó OO vẫn còn là một khoảng trống tối nghĩa, mặc dù đã hơn một thập kỷ.)

Mặc dù Trừu tượng dữ liệu rất quan trọng trong OO, nhưng đó là hệ quả của tính năng chính của OO (Nhắn tin) hơn là một tính năng xác định. Ngoài ra, điều rất quan trọng cần nhớ là có nhiều loại trừu tượng dữ liệu khác nhau . Hai loại trừu tượng dữ liệu phổ biến nhất được sử dụng hiện nay (nếu chúng ta bỏ qua "không trừu tượng hóa gì", có lẽ vẫn được sử dụng nhiều hơn hai loại kết hợp khác), là các loạiđối tượng dữ liệu trừu tượng . Vì vậy, chỉ bằng cách nói "Ẩn thông tin", "Đóng gói" và "Trừu tượng dữ liệu", bạn đã không nói gì về OO, vì OO chỉ là một dạng Trừu tượng dữ liệu và hai thực tế là khác nhau về cơ bản:

  • Với các kiểu dữ liệu trừu tượng, cơ chế trừu tượng hóa là hệ thống kiểu ; nó là hệ thống loại che giấu việc thực hiện. (Hệ thống loại không nhất thiết phải là tĩnh.) Với Đối tượng, việc triển khai bị ẩn sau giao diện thủ tục , không yêu cầu loại. (Ví dụ, nó có thể được thực hiện với các bao đóng, như được thực hiện trong ECMAScript.)
  • Với các loại dữ liệu trừu tượng, các phiên bản của các ADT khác nhau được gói gọn với nhau, nhưng các phiên bản của cùng một ADT có thể kiểm tra và truy cập vào đại diện và thực hiện riêng tư của nhau. Đối tượng luôn được gói gọn từ mọi thứ . Chỉ bản thân đối tượng có thể kiểm tra đại diện của chính nó và truy cập vào việc thực hiện riêng tư của chính nó. Không có đối tượng nào khác , thậm chí không phải các đối tượng khác cùng loại, các thể hiện khác của cùng một lớp, các đối tượng khác có cùng nguyên mẫu, bản sao của đối tượng hoặc bất cứ điều gì có thể làm điều đó. Không có .

Nhân tiện, điều này có nghĩa là trong Java, các lớp không hướng đối tượng. Hai trường hợp của cùng một lớp có thể truy cập đại diện riêng và thực hiện riêng tư của nhau. Do đó, các thể hiện của các lớp không phải là các đối tượng, thực tế chúng là các thể hiện ADT. Java interfaces, tuy nhiên, làm cung cấp hướng đối tượng dữ liệu trừu tượng. Vì vậy, nói cách khác: chỉ các thể hiện của giao diện là các đối tượng trong Java, các thể hiện của các lớp thì không.

Về cơ bản, đối với các loại, bạn chỉ có thể sử dụng giao diện. Điều này có nghĩa là các kiểu tham số của phương thức và hàm tạo, trả về kiểu phương thức, kiểu trường thể hiện, trường tĩnh và trường cục bộ, đối số cho instanceoftoán tử hoặc toán tử truyền và đối số kiểu cho hàm tạo kiểu chung phải luôn là giao diện. Một lớp chỉ có thể được sử dụng trực tiếp sau newtoán tử, không ở đâu khác.

Ví dụ, đối với tính mô đun, chúng ta chỉ có thể chia chương trình thành nhiều chương trình nhỏ thực hiện các tác vụ được xác định rõ có mã được chứa trong các tệp riêng biệt. Các chương trình này sẽ tương tác với nhau thông qua đầu vào và đầu ra được xác định rõ. Các tập tin có thể được bảo vệ (mã hóa?) Để đạt được đóng gói. Để sử dụng lại mã, chúng ta chỉ cần gọi các tệp đó bất cứ khi nào cần trong các chương trình mới. Điều này không nắm bắt được tất cả những gì OOP là hay tôi đang thiếu một cái gì đó rất rõ ràng?

Những gì bạn mô tả OO.

Đó thực sự là một cách tốt để suy nghĩ về OO. Trên thực tế, đó chính xác là những gì mà các nhà phát minh ban đầu của OO đã nghĩ đến. (Alan Kay đã tiến thêm một bước: anh ta hình dung rất nhiều máy tính nhỏ gửi tin nhắn cho nhau qua mạng.) Cái mà bạn gọi là "chương trình" thường được gọi là "đối tượng" và thay vì "gọi" chúng ta thường nói "gửi tin nhắn" ".

Định hướng đối tượng là tất cả về Nhắn tin (còn gọi là công văn động ). Thuật ngữ "Hướng đối tượng" được đặt ra bởi Tiến sĩ Alan Kay, nhà thiết kế chính của Smalltalk, và ông định nghĩa nó như thế này :

OOP với tôi có nghĩa là chỉ nhắn tin, duy trì và bảo vệ cục bộ và che giấu quá trình nhà nước, và ràng buộc cực kỳ muộn của tất cả mọi thứ.

Hãy phá vỡ nó:

  • nhắn tin ("gửi phương thức ảo", nếu bạn không quen với Smalltalk)
  • quy trình nhà nước nên được
    • giữ lại tại địa phương
    • được bảo vệ
    • ẩn
  • cực kỳ muộn của tất cả mọi thứ

Thực hiện khôn ngoan, tin nhắn là lời kêu gọi thủ tục cuối-bound, và nếu cuộc gọi thủ tục trễ-bound, thì bạn không thể biết lúc thiết kế những gì bạn đang đi để gọi, vì vậy bạn không thể thực hiện bất kỳ giả định về đại diện cụ thể của nhà nước. Vì vậy, thực sự là về nhắn tin, ràng buộc muộn là việc thực hiện nhắn tin và đóng gói là hệ quả của nó.

Sau đó, ông đã làm rõ rằng " Ý tưởng lớn là" nhắn tin " ", và hối tiếc vì đã gọi nó là "hướng đối tượng" thay vì "hướng thông điệp", bởi vì thuật ngữ "hướng đối tượng" tập trung vào thứ không quan trọng (đối tượng ) và phân tâm từ những gì thực sự quan trọng (nhắn tin):

Chỉ cần một lời nhắc nhở nhẹ nhàng rằng tôi đã chịu một số đau đớn ở OOPSLA cuối cùng để cố gắng nhắc nhở mọi người rằng Smalltalk không chỉ KHÔNG phải là cú pháp của nó hay thư viện lớp, thậm chí nó không phải là về các lớp. Tôi xin lỗi vì từ lâu tôi đã đặt ra thuật ngữ "đối tượng" cho chủ đề này bởi vì nó khiến nhiều người tập trung vào ý tưởng ít hơn.

Ý tưởng lớn là "nhắn tin" - đó là tất cả những gì về hạt nhân của Smalltalk / Squeak (và đó là thứ chưa bao giờ hoàn thành trong giai đoạn Xerox PARC của chúng tôi). Người Nhật có một từ nhỏ - ma - cho "cái nằm ở giữa" - có lẽ tương đương với tiếng Anh gần nhất là "interstitial". Chìa khóa trong việc tạo ra các hệ thống tuyệt vời và có thể phát triển được nhiều hơn là thiết kế cách thức các mô-đun giao tiếp thay vì các đặc tính và hành vi bên trong của chúng. Hãy nghĩ về internet - để sống, nó (a) phải cho phép nhiều loại ý tưởng và hiện thực khác nhau vượt quá mọi tiêu chuẩn duy nhất và (b) để cho phép mức độ tương tác an toàn khác nhau giữa các ý tưởng này.

(Tất nhiên, ngày nay, hầu hết mọi người thậm chí không tập trung vào các đối tượng mà vào các lớp học, điều này thậm chí còn sai hơn.)

Nhắn tin là nền tảng cho OO, cả dưới dạng ẩn dụ và như một cơ chế.

Nếu bạn gửi tin nhắn cho ai đó, bạn sẽ không biết họ làm gì với nó. Điều duy nhất bạn có thể quan sát, là phản ứng của họ. Bạn không biết liệu họ có tự xử lý tin nhắn hay không (tức là nếu đối tượng có phương thức), nếu họ chuyển tiếp tin nhắn cho người khác (ủy quyền / ủy quyền), nếu họ thậm chí hiểu nó. Đó là tất cả những gì đóng gói, đó là tất cả những gì về OO. Bạn thậm chí không thể phân biệt một proxy với thực tế, miễn là nó đáp ứng như bạn mong đợi.

Một thuật ngữ "hiện đại" hơn cho "nhắn tin" là "gửi phương thức động" hoặc "gọi phương thức ảo", nhưng điều đó làm mất đi phép ẩn dụ và tập trung vào cơ chế.

Vì vậy, có hai cách để xem xét định nghĩa của Alan Kay: nếu bạn nhìn vào nó đứng một mình, bạn có thể quan sát rằng nhắn tin về cơ bản là một cuộc gọi thủ tục muộn và ràng buộc muộn có nghĩa là đóng gói, vì vậy chúng tôi có thể kết luận rằng # 1 và # 2 thực sự là dư thừa, và OO là tất cả về ràng buộc muộn.

Tuy nhiên, sau đó ông đã làm rõ rằng điều quan trọng là nhắn tin, và vì vậy chúng ta có thể nhìn nó từ một góc độ khác: nhắn tin bị ràng buộc muộn. Bây giờ, nếu nhắn tin là điều duy nhất có thể, thì số 3 sẽ là sự thật một cách tầm thường: nếu chỉ có một điều, và điều đó bị ràng buộc muộn, thì tất cả mọi thứ đều bị ràng buộc muộn. Và một lần nữa, đóng gói theo sau từ tin nhắn.

Những điểm tương tự cũng được thực hiện trong Tìm hiểu về trừu tượng dữ liệu, được xem xét lại bởi William R. Cook và cũng là Đề xuất của ông về các định nghĩa hiện đại, đơn giản về "Đối tượng" và "Hướng đối tượng" :

Công văn năng động của các hoạt động là đặc tính thiết yếu của các đối tượng. Nó có nghĩa là hoạt động được gọi là một thuộc tính động của chính đối tượng. Các hoạt động không thể được xác định một cách tĩnh và nói chung không có cách nào để [biết] chính xác thao tác nào sẽ được thực hiện để đáp ứng với một yêu cầu nhất định, ngoại trừ bằng cách chạy nó. Điều này hoàn toàn giống với các hàm hạng nhất, luôn được gửi động.

Trong Smalltalk-72, thậm chí không có bất kỳ đối tượng nào! Chỉ các dòng tin nhắn được phân tích cú pháp, viết lại và định tuyến lại. Các phương thức xuất hiện đầu tiên (các cách tiêu chuẩn để phân tích và định tuyến lại các luồng thông báo), sau đó là các đối tượng (các nhóm phương thức chia sẻ một số trạng thái riêng tư). Kế thừa xuất hiện muộn hơn nhiều và các lớp chỉ được giới thiệu như một cách để hỗ trợ kế thừa. Nếu nhóm nghiên cứu của Kay đã biết về các nguyên mẫu, có lẽ họ sẽ không bao giờ giới thiệu các lớp học ở nơi đầu tiên.

Benjamin Pierce trong các loại và ngôn ngữ lập trình lập luận rằng tính năng xác định của Định hướng đối tượng là đệ quy mở .

Vì vậy: theo Alan Kay, OO là tất cả về nhắn tin. Theo William Cook, OO là tất cả về công văn phương thức động (đó thực sự là điều tương tự). Theo Benjamin Pierce, OO là tất cả về Open Recursion, về cơ bản có nghĩa là tự tham chiếu được giải quyết một cách linh hoạt (hoặc ít nhất đó là một cách để suy nghĩ), hay nói cách khác là nhắn tin.

Như bạn có thể thấy, người đặt ra thuật ngữ "OO" có một cái nhìn khá siêu hình về các vật thể, Cook có một cái nhìn khá thực dụng và xuyên thủng một quan điểm toán học rất nghiêm ngặt. Nhưng điều quan trọng là: nhà triết học, nhà thực dụng và nhà lý luận đều đồng ý! Nhắn tin là một trụ cột của OO. Giai đoạn = Stage.

Lưu ý rằng không có đề cập đến thừa kế ở đây! Kế thừa là không cần thiết cho OO. Nói chung, hầu hết các ngôn ngữ OO có một số cách thực hiện sử dụng lại nhưng điều đó không nhất thiết phải là sự kế thừa. Nó cũng có thể là một số hình thức ủy quyền, ví dụ. Trên thực tế, Hiệp ước Orlando thảo luận về sự ủy thác như là một sự thay thế cho sự kế thừa và cách thức các hình thức ủy quyền và kế thừa khác nhau dẫn đến các điểm thiết kế khác nhau trong không gian thiết kế của các ngôn ngữ đối tượng. (Lưu ý rằng thực sự ngay cả trong các ngôn ngữ hỗ trợ kế thừa, như Java, mọi người thực sự được dạy để tránh điều đó, một lần nữa cho thấy rằng nó không cần thiết cho OO.)


16
+100 - Chúng tôi đổ lỗi cho mọi thứ vì chúng được sử dụng không đúng cách.
JeffO

55
Xin lỗi, nhưng điều này là vô cùng sai lầm. Alan Kay có thể đã đưa ra thuật ngữ này, nhưng các nguyên tắc đã có từ trước Smalltalk. Lập trình hướng đối tượng xuất phát từ Simula và phong cách OO của nó không liên quan gì đến "thông điệp". Gần như mọi ngôn ngữ OO thành công đều chạy theo các nguyên tắc cơ bản được nêu trong Simula - giống với ngôn ngữ mà chúng ta thấy trong Java - và OO theo phong cách Smalltalk đã thất bại trong "thị trường ý tưởng" mỗi khi nó được giới thiệu lại, bởi vì nó hoàn toàn không hoạt động tốt Tên là chỉ điều thực sự có ý nghĩa Kay đóng góp.
Mason Wheeler

23
@steakexchange số "Bản chất của OO", điều làm cho nó thực sự đặc biệt, là các đối tượng với các phương thức ảo. Có một lý do không ai sử dụng Smalltalk: hệ thống truyền tin nhắn hoạt động rất kém ở quy mô máy tính cá nhân. Mỗi khi một số nhà thiết kế ngôn ngữ có thiện chí nhưng ngây thơ cố gắng thực hiện lại các nguyên tắc của Smalltalk, thì cuối cùng lại thất bại. (Ví dụ gần đây nhất là Objective-C, thứ mà không ai từng sử dụng nếu Steve Jobs đã đẩy nó xuống toàn bộ cổ họng của cộng đồng iOS. Nó không bao giờ tìm thấy bất kỳ lực kéo nào bên ngoài hệ sinh thái Apple và có lý do cho điều đó. )
Mason Wheeler

28
@MasonWheeler Bạn có thể giải thích về nhận xét của mình trong câu trả lời vì bạn có quan điểm hoàn toàn trái ngược với những gì Jorg đang nói?
steakexchange

20
Cũng đáng lưu ý rằng khái niệm về ngôn ngữ hướng đối tượng đã phát triển rất nhiều. Những khái niệm tổ tiên đó có thể không đúng như ngày nay với rất nhiều ngôn ngữ bỏ các mô hình cũ và chấp nhận nhiều mô hình. Ví dụ, nhìn vào C # - ngôn ngữ đó trộn lẫn hầu hết mọi thứ dưới ánh mặt trời cùng một lúc và, trong khi chủ yếu được gọi là ngôn ngữ OO, nó thực sự là một sự pha trộn của các mô hình khác nhau. Điều đó cho phép nó trở thành một công cụ thực sự biểu cảm cho các nhà phát triển xung quanh. Ngoài ra, OO dựa trên lớp là một trong nhiều hương vị hợp lệ của lập trình OO.
T. Sar

66

Nhưng tôi nghĩ rằng tất cả các nguyên tắc được sử dụng để quản lý sự phức tạp như mô đun hóa, đóng gói, ẩn dữ liệu và có thể được thực hiện rất dễ dàng bằng các ngôn ngữ thủ tục.

Khi bạn nói, "rất dễ dàng" bạn đang đưa ra một tuyên bố rất táo bạo. Cách tôi đọc là: "Tôi không thấy khó khăn, vì vậy nó không phải là rất lớn." Khi nói theo cách đó, rõ ràng là bạn không hỏi "tại sao chúng ta cần OO", bạn đang hỏi "tại sao những khó khăn mà các mô hình lập trình khác gặp phải lại dẫn đến việc phát minh ra OO ngay lập tức rõ ràng với tôi? "

Một câu trả lời cho câu hỏi đó là nhiều khó khăn trong số này không tồn tại trong các loại chương trình bạn đang làm việc. Bạn không được yêu cầu cập nhật mã spaghetti 40 tuổi. Bạn không cố viết trình quản lý hiển thị mới cho hệ điều hành. Bạn không gỡ lỗi các ứng dụng phân tán đa luồng.

Đối với nhiều loại chương trình đồ chơi mà sinh viên CS được giao nhiệm vụ viết, chúng tôi cũng có thể viết chúng bằng BASIC hoặc lắp ráp như Java hoặc Python. Đó là bởi vì độ phức tạp vốn có của các tác vụ rất thấp, chỉ có một nhà phát triển, không có vấn đề về khả năng tương tác kế thừa, hiệu suất không thành vấn đề và mã có thể sẽ chỉ được chạy một vài lần trên một máy.

Hãy tưởng tượng bắt một tài xế sinh viên và yêu cầu họ nhập vào một con phố đông đúc vào giờ cao điểm, trên một hộp số tay không có đồng bộ, hướng lên một ngọn đồi dốc. Thảm họa. Tại sao? Họ không thể quản lý mức độ phức tạp cần thiết để đồng thời tuân theo tất cả các quy tắc mà nhiệm vụ yêu cầu.

Bây giờ hãy tưởng tượng cùng một học sinh, cùng một phương tiện, lái xe với tốc độ đi bộ trong một bãi đậu xe trống. Họ ổn, vì mức độ kỹ năng của họ phù hợp với nhiệm vụ. Không có áp lực, ít rủi ro, và họ có thể thực hiện các nhiệm vụ riêng lẻ để bắt đầu, bám, chuyển, tăng tốc, điều khiển từng lúc.

Học sinh đó có thể hỏi tại sao chúng ta có hộp số tự động, nếu một người lái xe lành nghề có thể làm tất cả những điều này cùng một lúc? Câu trả lời là một trình điều khiển đủ kỹ năng không, trong điều kiện tối ưu, cần tự động. Nhưng chúng tôi không phải là tất cả các tài xế chuyên nghiệp trong điều kiện cao điểm và chúng tôi thường muốn sự tiện lợi của việc các nhà thiết kế xe hơi chăm sóc tất cả sự phức tạp đó cho chúng tôi.

Một lập trình viên đủ kỹ năng, có kỷ luật thực sự có thể tạo ra một hệ thống có độ phức tạp cao hoạt động trong C hoặc lắp ráp. Nhưng chúng ta không phải là tất cả Linus Torvalds. Chúng ta cũng không nên, để tạo ra phần mềm hữu ích.

Cá nhân tôi không có hứng thú với việc phải phát minh lại tất cả các tính năng của một ngôn ngữ hiện đại trước khi tôi có thể giải quyết vấn đề trong tay. Nếu tôi có thể tận dụng một ngôn ngữ bao gồm các giải pháp để giải quyết vấn đề, tại sao tôi lại không?

Vì vậy, tôi sẽ chuyển câu hỏi của bạn và hỏi bạn, nếu các ngôn ngữ cung cấp các tính năng tiện lợi như đóng gói và đa hình, tại sao chúng ta không nên sử dụng chúng?


13
Vì vậy, về cơ bản có thể thực hiện OO với ngôn ngữ thủ tục nhưng đó là thực hiện thủ công trong khi sử dụng ngôn ngữ OO được tiêu chuẩn hóa tự động hóa và đơn giản hóa.
steakexchange

6
@steakexchange Chính xác là khá nhiều.
Tim B

3
@steakexchange một ví dụ lịch sử tốt về điều này là mô hình đối tượng cho X Windows trở lại trong ngày. Nó được viết bằng C nhưng dựa trên một hệ thống hướng đối tượng. Vì vậy, bạn phải tuân theo một số quy ước nhất định để giao tiếp với nó để các lớp học của bạn chơi tốt với mọi người khác.
Ukko

7
@nocomprende Chắc chắn. Nhưng người ta có thể khiến máy tính của một người không thể khởi động bằng cách ghi đĩa thô thay vì dựa vào hệ thống tập tin, và sẽ rất khó để gỡ lỗi các vấn đề trong hệ thống đối tượng đặc biệt được xây dựng bởi người mới. Đóng gói, khi được thực hiện một cách chính xác, giữ cho chúng ta khỏi cố tình hoặc vô tình can thiệp vào những thứ chúng ta không nên.
Clement Cherlin

3
Điều thú vị đối với tôi là các ví dụ bạn đưa ra cho các ứng dụng sẽ được hưởng lợi từ OO thường không được viết bằng các ngôn ngữ OO. Spaghetti 40 tuổi có khả năng được viết bằng C, COBOL, FORTRAN hoặc REXX. Trình quản lý hiển thị có thể được viết bằng C (mặc dù có các quy ước OO-ish) và nhiều hệ thống đa luồng phân tán thành công được viết bằng Erlang, Go hoặc C.
James_pic 21/03/17

22

Những gì bạn đang mô tả không phải là OOP, đó là sự trừu tượng. Trừu tượng có mặt trong tất cả các mô hình thiết kế hiện đại, ngay cả những mô hình không OOP. Và OOP là một loại trừu tượng rất cụ thể.

Đầu tiên, đáng lưu ý rằng không có định nghĩa duy nhất về OOP, vì vậy có thể có những người không đồng ý với những gì tôi mô tả là OOP.

Thứ hai, điều quan trọng cần nhớ là OOP được lấy cảm hứng từ các mô hình thiết kế truyền thống, do đó, sự tương đồng với thiết kế xe hơi không phải là ngẫu nhiên.

Tuy nhiên, đây là một số cách OOP mang nhiều sắc thái hơn những gì bạn đã nói:

  • Đóng gói: đây không chỉ là về việc có một giao diện được thiết lập cho một mô-đun (tức là trừu tượng hóa), mà là về việc cấm truy cập ngoài giao diện này. Trong Java, truy cập một biến riêng là một lỗi biên dịch, trong khi trong thiết kế xe hơi của bạn, bạn có thể (trong một số trường hợp) sử dụng mọi thứ theo cách khác với giao diện dự định.

  • Kế thừa: Đây thực sự là điều làm cho OOP trở nên độc đáo. Khi bạn đã xác định một giao diện, bạn có thể thực hiện nhiều thứ khi thực hiện giao diện đó và bạn có thể thực hiện việc này theo cách bá đạo, thay đổi các phần cụ thể của việc triển khai chúng, đồng thời kế thừa tất cả các phần trước đó, giảm ồ ạt sao chép mã.

    Nếu bạn nghĩ về các thành phần được đóng gói của một chiếc xe hơi, thì thực sự không tương đương với điều này. Không có cách nào để tôi chế tạo một thiết bị bằng cách lấy một thiết bị khác và thay đổi một phần cụ thể trong quá trình thực hiện. (Ít nhất tôi không nghĩ vậy, tôi không biết nhiều về xe hơi).

  • Đa hình : Một khi bạn đã xác định một giao diện, mọi thứ sử dụng giao diện đó sẽ không thể phân biệt được, từ quan điểm của những hoạt động có sẵn và bạn không cần phải biết cách triển khai nào đang được sử dụng để sử dụng giao diện. Đây là nơi phân nhóm và Nguyên tắc thay thế Liskov trở nên quan trọng.

  • Khớp nối : Một khía cạnh quan trọng của OOP là chúng tôi liên kết chặt chẽ mọi thứ với cùng một hoạt động và trải rộng các hình thức khác nhau mà chúng có thể có. Dữ liệu được đóng gói với các hoạt động trên dữ liệu đó. Điều này có nghĩa là rất dễ dàng để thêm một dạng dữ liệu mới (triển khai mới), nhưng rất khó để thêm một thao tác mới vào giao diện (vì bạn phải cập nhật từng lớp thực hiện giao diện). Điều này trái ngược với các kiểu dữ liệu đại số trong các ngôn ngữ chức năng, nơi rất dễ dàng để thêm một thao tác mới (bạn chỉ cần viết một hàm xử lý tất cả các trường hợp), nhưng khó có thể thêm một biến thể mới (vì bạn cần thêm một biến thể mới trường hợp cho tất cả các chức năng của bạn).


1
Câu trả lời tốt! Một phần tôi không đồng ý: sự khác biệt mà bạn vẽ về đóng gói là không hợp lệ. Đóng gói luôn có nghĩa là "cấm truy cập ngoài giao diện này" - điều đó đúng với các cài đặt OOP và không OOP. Vì vậy, phần này không thực sự là một cái gì đó duy nhất cho OOP.
DW

@DW Tôi đã cố gắng làm rõ điều này, nói rằng nó không phải là duy nhất đối với OOP mà đó là sự khác biệt giữa đóng gói và trừu tượng hóa. Cảm ơn vì bạn đã phản hồi!
jmite

2
ĐỒNG Ý. Nhưng tôi vẫn có một cái nhìn khác về những gì được viết ở đây về chủ đề đó. Bạn đã viết rằng "đây là một số cách OOP mang nhiều sắc thái hơn những gì bạn đã nói", nhưng đóng gói không phải là cách mà OOP mang nhiều sắc thái hơn những gì được viết trong câu hỏi. Đóng gói là những gì nó là, trong bất kỳ mô hình. Và nơi bạn đã viết rằng "Những gì bạn đang mô tả không phải là OOP, đó là sự trừu tượng", tôi nghĩ rằng câu hỏi ban đầu là cố gắng mô tả sự đóng gói (không chỉ là trừu tượng). Tôi đoán tôi sẽ chỉ để lại nhận xét này như một quan điểm khác. Tôi nghĩ rằng câu trả lời là rất hữu ích!
DW

Kế thừa là một tính năng phổ biến, nhưng một số ngôn ngữ OO quan trọng thiếu nó.
Bradd Szonye

Câu trả lời tốt, nhưng IMO bạn đang phóng đại ví dụ về xe hơi. Một động cơ cho một mô hình nhất định có giao diện được xác định rõ (trục cam, giá đỡ "giá đỡ", v.v.). Bạn có thể thay thế bộ chế hòa khí cũ đơn giản bằng bộ phun nhiên liệu, thêm bộ sạc turbo, vv mà không ảnh hưởng đến việc truyền. (Mặc dù động cơ diesel không yêu cầu bình xăng IIRC đã được sửa đổi.) Ngược lại, bạn có thể thay thế hộp số tay bằng tự động và AFAIK hoàn toàn không ảnh hưởng đến động cơ.
David

11

Chúng ta có thực sự cần ngôn ngữ OO để quản lý độ phức tạp của phần mềm không?

Điều này phụ thuộc vào ý nghĩa của từ "cần".

Nếu "cần" có nghĩa là yêu cầu, không có chúng tôi không yêu cầu nó.

Nếu "cần" có nghĩa là "cung cấp lợi ích mạnh mẽ", thì tôi sẽ nói "Có", chúng tôi mong muốn điều đó.

Bức tranh lớn

Ngôn ngữ OO liên kết chức năng với dữ liệu.

Bạn có thể tránh các hàm ràng buộc và ghi này đi qua các giá trị dữ liệu.

Nhưng sau đó, có lẽ bạn sẽ kết thúc với các chòm sao dữ liệu đi cùng nhau và bạn sẽ bắt đầu chuyển qua các bộ dữ liệu, bản ghi hoặc từ điển dữ liệu.

Và thực sự, đó là tất cả các cuộc gọi phương thức là: các hàm một phần trên các tập dữ liệu bị ràng buộc.

Tính năng theo tính năng

Các tính năng của OOP:

  • Kế thừa cho phép tái sử dụng mã (mixins) và khái niệm (Các lớp / giao diện cơ sở trừu tượng) - nhưng bạn có thể có được điều này bằng cách trang trí lại các hàm và biến trong phạm vi phụ.
  • Đóng gói cho phép ẩn thông tin để chúng ta có thể làm việc ở mức độ trừu tượng cao hơn - nhưng bạn có thể làm điều này với các tệp tiêu đề, hàm và mô-đun.
  • Đa hình cho phép chúng ta sử dụng các đối số thuộc các loại khác nhau miễn là các đối số đó hỗ trợ các giao diện giống nhau - nhưng chúng ta cũng có thể làm điều đó với các hàm.

Tuy nhiên, không có điều nào trong số đó xảy ra dễ dàng như với một ngôn ngữ hướng đối tượng với sự hỗ trợ hạng nhất của các tính năng đó.

Người giới thiệu

Có rất nhiều chỉ trích về OOP .

Tuy nhiên, các nghiên cứu dường như chỉ ra rằng chúng ta có được năng suất lập trình lớn hơn từ việc sử dụng lại mã thông qua OOP. Đây là một phát hiện gây tranh cãi và một số nhà nghiên cứu nói rằng họ không thể tái tạo những mức tăng năng suất này, với những hạn chế nhất định. (nguồn)

Phần kết luận

Chúng tôi không "cần" OOP. Nhưng trong một số trường hợp, người dùng muốn OOP.

Hiểu biết của tôi là các lập trình viên trưởng thành có thể khá năng suất trong phong cách hướng đối tượng. Và khi các gói có các đối tượng cốt lõi với các giao diện đơn giản dễ hiểu, ngay cả các lập trình viên mới cũng có thể trở nên khá hiệu quả nhanh chóng.


10

Tôi sẽ cố gắng ngắn gọn.

Nguyên tắc cốt lõi của OO là sự kết hợp giữa dữ liệu và hành vi trong một đơn vị tổ chức (một đối tượng).

Đây là những gì cho phép chúng ta kiểm soát sự phức tạp và nó là một khái niệm khá sáng tạo khi nó xuất hiện. So sánh điều đó với các tệp một mặt (dữ liệu thuần túy), các chương trình đọc và xử lý các tệp đó mặt khác (logic thuần túy) và đầu ra (lại là dữ liệu thuần túy).

Chỉ khi bạn có gói dữ liệu và logic đó cùng nhau, mô hình hóa một số thực thể trong thế giới thực, bạn có thể bắt đầu trao đổi tin nhắn, tạo các lớp con, tách riêng khỏi dữ liệu và hành vi công khai, thực hiện hành vi đa hình, thực hiện tất cả các phép thuật cụ thể OO này.

Vì vậy, vâng, OO là một vấn đề lớn. Và không, nó không chỉ là một loạt các công cụ cũ với một cái tên lạ mắt.

Tách tất cả ra, nhìn vào các yếu tố và sau đó nói "ồ, không có gì ở đây tôi chưa từng thấy trước đây" không nhận ra hội nghị nắm giữ sự đổi mới. Kết quả là nhiều hơn tổng của các bộ phận của nó.


8

Không có định nghĩa "chính thức" về lập trình hướng đối tượng và những người hợp lý không đồng ý với những gì thực sự xác định chất lượng của OO. Một số nói nhắn tin, một số nói phân nhóm, một số nói thừa kế, một số nói bó dữ liệu và hành vi. Điều đó không có nghĩa là thuật ngữ này là vô nghĩa, chỉ là bạn không nên quá mải mê cãi nhau về việc OO thực sự là gì.

Đóng gói và mô đun hóa là các nguyên tắc cơ bản hơn của thiết kế, và nên được áp dụng trong tất cả các mô hình lập trình. Những người đề xuất OO không cho rằng những đặc tính này chỉ có thể đạt được với OO - chỉ có điều OO đặc biệt phù hợp để đạt được điều này. Tất nhiên những người đề xuất các mô hình khác như nói lập trình chức năng yêu cầu tương tự cho mô hình của họ. Trong thực tế, nhiều ngôn ngữ thành công là đa mô hình và OO, chức năng, v.v. nên được coi là công cụ chứ không phải là "một cách thực sự".

Tôi nghĩ rằng tất cả các nguyên tắc được sử dụng để quản lý sự phức tạp có thể được nhận ra bằng các ngôn ngữ lập trình thủ tục.

Đúng, bởi vì cuối cùng bạn có thể làm bất cứ điều gì trong bất kỳ ngôn ngữ lập trình. Nó có thể dễ dàng hơn trong một số ngôn ngữ so với các ngôn ngữ khác, vì tất cả các ngôn ngữ có điểm mạnh và điểm yếu khác nhau.


7

Một cái gì đó mà các câu trả lời khác chưa được đề cập: trạng thái.

Bạn nói về OO như một công cụ để quản lý sự phức tạp . Phức tạp là gì? Đó là một thuật ngữ mờ. Tất cả chúng ta đều có ý nghĩa này về ý nghĩa của nó, nhưng khó khăn hơn để xác định nó. Chúng ta có thể đo độ phức tạp theo chu kỳ, tức là số lượng đường dẫn thời gian chạy qua mã, nhưng tôi không biết đó là những gì chúng ta đang nói về khi chúng ta sử dụng OO để quản lý độ phức tạp.

Những gì tôi nghĩ rằng chúng ta đang nói về sự phức tạp liên quan đến nhà nước.

Có hai ý tưởng chính đằng sau việc đóng gói . Một trong số đó, việc che giấu các chi tiết thực hiện , được trình bày khá rõ trong các câu trả lời khác. Nhưng một cái khác đang che giấu trạng thái thời gian chạy của nó . Chúng tôi không làm phiền với dữ liệu nội bộ của các đối tượng; chúng tôi chuyển các thông điệp (hoặc phương thức gọi nếu bạn thích chi tiết triển khai hơn khái niệm như Jörg Mittag đã chỉ ra). Tại sao?

Mọi người đã đề cập đến điều đó bởi vì bạn không thể thay đổi cấu trúc bên trong dữ liệu của mình mà không thay đổi mã truy cập và bạn muốn thực hiện điều đó ở một nơi (phương thức truy cập) thay vì 300 địa điểm.

Nhưng đó cũng là vì nó làm cho mã khó lý giải về : mã thủ tục (cho dù theo ngôn ngữ có tính chất thủ tục hoặc được viết đơn giản theo kiểu đó) cung cấp ít trợ giúp cho việc áp đặt các hạn chế đối với đột biến trạng thái. Bất cứ điều gì có thể thay đổi bất cứ lúc nào từ bất cứ nơi nào. Các chức năng / phương thức gọi có thể có hành động ma quái ở khoảng cách xa. Kiểm tra tự động là khó khăn hơn, vì sự thành công của các thử nghiệm được xác định bởi giá trị của các biến không cục bộ được truy cập / truy cập rộng rãi.

Hai mô hình lập trình lớn khác (OO và chức năng) đưa ra các giải pháp thú vị, nhưng gần như đối lập với vấn đề phức tạp liên quan đến trạng thái. Trong lập trình chức năng, người ta cố gắng tránh nó hoàn toàn: các chức năng nói chung là thuần túy, các hoạt động trên cấu trúc dữ liệu trả về các bản sao thay vì cập nhật bản gốc tại chỗ, v.v.

Mặt khác, OO cung cấp các công cụ để đối phó với trạng thái quản lý (thay vì các công cụ để tránh nó). Ngoài các công cụ ở cấp độ ngôn ngữ như công cụ sửa đổi truy cập (được bảo vệ / công khai / riêng tư), getters và setters, v.v., cũng có một số quy ước liên quan như Luật Demeter khuyên bạn không nên truy cập các đối tượng để lấy dữ liệu của các đối tượng khác .

Lưu ý rằng bạn không cần các đối tượng thực hiện bất kỳ điều nào trong số này: bạn có thể có một bao đóng có dữ liệu không thể truy cập và trả về cấu trúc dữ liệu của các hàm để thao tác với nó. Nhưng đó không phải là một đối tượng sao? Điều đó không phù hợp với quan niệm của chúng ta về đối tượng là gì, bằng trực giác? Và nếu chúng ta có khái niệm này, không phải tốt hơn là nên sử dụng lại nó bằng ngôn ngữ chứ không phải (như các câu trả lời khác đã nói) dựa vào sự bùng nổ kết hợp của việc triển khai quảng cáo cạnh tranh?


5

Chúng ta có thực sự cần ngôn ngữ OO để quản lý độ phức tạp của phần mềm không?

Không. Nhưng họ có thể giúp đỡ trong nhiều tình huống.

Tôi đã sử dụng chủ yếu một ngôn ngữ OO trong nhiều thập kỷ, nhưng hầu hết mã của tôi thực sự là thủ tục theo kiểu OO. Tuy nhiên, đối với mọi thứ liên quan đến GUI, tôi sử dụng thư viện OO rộng lớn của các phương thức và đối tượng tích hợp sẵn của ngôn ngữ, vì nó đơn giản hóa rất nhiều mã của tôi.

Ví dụ: ứng dụng Windows sử dụng API Windows cấp thấp ban đầu để hiển thị biểu mẫu, nút và trường chỉnh sửa yêu cầu nhiều mã, trong khi thay vào đó, sử dụng các thư viện của các đối tượng đi kèm với Visual Basic hoặc C # hoặc Delphi giống nhau chương trình nhỏ bé và tầm thường. Vì vậy, mã OO của tôi thường tương đối nhỏ và đối với GUI, trong khi mã của tôi mà các đối tượng đó gọi thường lớn hơn nhiều và thường không liên quan đến OO (mặc dù nó có thể thay đổi tùy thuộc vào vấn đề tôi đang cố gắng giải quyết).

Tôi đã thấy các chương trình OO quá phức tạp, dựa trên các quy tắc bí truyền phức tạp về cách các đối tượng được thực hiện và có thể đơn giản hơn nhiều nếu được viết mà không có khái niệm OO. Tôi cũng đã thấy điều ngược lại: các hệ thống phức tạp đang kêu gọi được thực hiện lại và đơn giản hóa bằng cách sử dụng các đối tượng.

Khi bạn có kinh nghiệm, bạn sẽ thấy các tình huống khác nhau đòi hỏi các công cụ và giải pháp khác nhau và một kích thước không phù hợp với tất cả.


3

Là một người có liên quan đến một dự án rất lớn được viết hoàn toàn bằng C, tôi chắc chắn có thể nói rằng câu trả lời là "không" rõ ràng.

Tính mô đun là quan trọng. Nhưng mô-đun có thể được thực hiện trong thực tế bất kỳ ngôn ngữ phong nha. Ví dụ, C hỗ trợ biên dịch mô-đun, tệp tiêu đề và các loại cấu trúc. Điều này là đủ cho 99% các trường hợp. Xác định một mô-đun cho từng loại dữ liệu trừu tượng mới mà bạn cần và xác định các chức năng để vận hành trên loại dữ liệu. Đôi khi, bạn muốn hiệu suất và các chức năng đó nằm trong tệp tiêu đề dưới dạng hàm nội tuyến, những lần khác bạn sẽ sử dụng các chức năng tiêu chuẩn. Đó là tất cả vô hình cho người dùng mà cách được chọn.

Cấu trúc hỗ trợ thành phần. Ví dụ: bạn có thể có một bảng băm bị khóa bao gồm khóa mutex và bảng băm thông thường. Đây không phải là lập trình hướng đối tượng; không có phân lớp được thực hiện. Thành phần là một công cụ cũ hơn nhiều so với ý tưởng lập trình hướng đối tượng.

Đối với 1% các trường hợp trong đó mô đun mức biên dịch là không đủ và bạn cần mô đun hóa thời gian chạy, có một thứ gọi là con trỏ hàm. Chúng cho phép có các triển khai riêng lẻ của một giao diện được xác định rõ. Lưu ý rằng đây không phải là lập trình hướng đối tượng trong một ngôn ngữ không hướng đối tượng. Đây là xác định một giao diện và sau đó thực hiện nó. Ví dụ, phân lớp không được sử dụng ở đây.

Có lẽ xem xét dự án nguồn mở phức tạp nhất hiện có. Cụ thể là nhân Linux. Nó được viết hoàn toàn bằng ngôn ngữ C. Nó được thực hiện chủ yếu bằng cách sử dụng các công cụ mô đun mức biên dịch tiêu chuẩn bao gồm cả thành phần và đôi khi bất cứ khi nào cần mô đun thời gian chạy, các con trỏ hàm được sử dụng để xác định và thực hiện giao diện.

Nếu bạn cố gắng tìm một ví dụ về lập trình hướng đối tượng trong nhân Linux, tôi chắc chắn việc tìm ví dụ đó rất khó, trừ khi bạn mở rộng lập trình hướng đối tượng để bao gồm các tác vụ tiêu chuẩn như "xác định giao diện và sau đó thực hiện nó".

Lưu ý rằng ngay cả ngôn ngữ lập trình C cũng hỗ trợ lập trình hướng đối tượng nếu bạn thực sự cần nó. Ví dụ, hãy xem xét bộ công cụ giao diện người dùng đồ họa GTK. Nó thực sự là hướng đối tượng, mặc dù được viết bằng ngôn ngữ không hướng đối tượng. Vì vậy, điều này cho thấy rằng ý tưởng rằng bạn cần một "ngôn ngữ hướng đối tượng" là rất thiếu sót. Không có gì một ngôn ngữ hướng đối tượng có thể làm mà một loại ngôn ngữ khác không thể làm được. Hơn nữa, nếu bạn là một lập trình viên chuyên gia, bạn biết cách viết mã hướng đối tượng bằng bất kỳ ngôn ngữ nào rất dễ dàng. Nó không phải là một gánh nặng để sử dụng C, ví dụ.

Vì vậy, kết luận là các ngôn ngữ hướng đối tượng có lẽ chỉ hữu ích cho các lập trình viên mới làm quen, những người không hiểu cách thức thực hiện khái niệm này. Tuy nhiên, tôi không muốn ở gần bất kỳ dự án nào mà các lập trình viên là những lập trình viên mới làm việc như vậy.


1
"[...] kết luận là các ngôn ngữ hướng đối tượng có lẽ chỉ hữu ích cho các lập trình viên mới làm quen, những người không hiểu cách thức thực hiện khái niệm này." Hấp dẫn. Những ngôn ngữ bạn có trong tâm trí? Bạn có ví dụ về các dự án nguồn mở được viết bằng các ngôn ngữ này thất bại hoặc không thất bại không?
Vincent Savard

3
Bạn nêu lên một số điểm tốt, nhưng ý tưởng chính của bạn là thiếu sót. Có, có thể thực hiện các khái niệm OO như đóng gói, gửi ảo, kế thừa và thậm chí thu gom rác bằng ngôn ngữ như C. Cũng có thể thực hiện việc đó trong quá trình lắp ráp. Nó không làm cho lập trình dễ dàng hơn. Lập trình và đặc biệt là thiết kế bằng một ngôn ngữ như C chắc chắn khó khăn hơn so với ngôn ngữ OO. Trong C, bạn cần ánh xạ các khái niệm đến việc triển khai chúng, bằng ngôn ngữ OO, bạn không cần thực hiện bước đó (ít nhất là không áp dụng cho các khái niệm OO).

1
"[con trỏ hàm] cho phép có các triển khai riêng lẻ của giao diện được xác định rõ. Lưu ý rằng đây không phải là lập trình hướng đối tượng trong ngôn ngữ không hướng đối tượng. Đây là xác định giao diện và sau đó thực hiện giao diện." Xin lỗi, nhưng điều đó hoàn toàn sai, bởi vì đây chính xác là OOP. "Ví dụ: phân lớp không được sử dụng ở đây" Phân lớp không phải là một tính năng bắt buộc của OOP. Ví dụ, lưu ý rằng JavaScript là ngôn ngữ hướng đối tượng không có tính năng phân lớp (hoặc, đối với vấn đề đó, tất cả các lớp ... Chỉ là các đối tượng có chứa các tham chiếu hàm).
Jules

1
Để làm rõ nhận xét cuối cùng của tôi, quan điểm của tôi là yếu tố phân biệt chính b giữa OOP (không nhất thiết là bất kỳ ngôn ngữ OO cụ thể nào) và các phương pháp khác là trong OOP, chúng tôi xác định các giao diện hoạt động trừu tượng trên dữ liệu mà không cần biết định dạng của dữ liệu đó bằng cách ràng buộc việc thực hiện các giao diện với chính dữ liệu. Đó là những gì OOP là. Phương thức triển khai là không liên quan, cho dù đó là lớp kiểu Java, đối tượng JavaScript (thực chất là ánh xạ tên đến thuộc tính, có thể là dữ liệu hoặc mã) hoặc cấu trúc chứa con trỏ hàm và khoảng trống * cho dữ liệu.
Jules

1
"Vì vậy, kết luận là các ngôn ngữ hướng đối tượng có lẽ chỉ hữu ích cho các lập trình viên mới làm quen, những người không hiểu cách thức thực hiện khái niệm này." .... Và điều này, thẳng thắn, là xúc phạm đơn giản. Tôi biết khá rõ cách thức các khái niệm này được thực hiện. Tôi đã thực hiện đủ công việc bằng cách sử dụng để thành thạo, thậm chí. Tôi thậm chí đã triển khai cả trình thông dịch và trình biên dịch cho các ngôn ngữ OO trong quá khứ. Nhưng vì tôi thích làm việc với các ngôn ngữ cấp cao hơn có các đối tượng hạng nhất nên tôi phải là một lập trình viên mới làm việc mà bạn không muốn làm việc với?!
Jules

2

Lý do giới thiệu các mô hình lập trình, bao gồm các phương pháp hướng đối tượng, là để dễ dàng thực hiện các chương trình tinh vi và mạnh mẽ hơn. Trong số tháng 8 năm 1981 của Tạp chí Byte, Daniel Ingalls , một trong những người sáng tạo chính của Smalltalk, đã định nghĩa "hướng đối tượng" là liên quan đến các khả năng sau:

  • quản lý lưu trữ tự động
  • khả năng trao đổi tin nhắn
  • một phép ẩn dụ thống nhất áp dụng cho tất cả các hoạt động của ngôn ngữ
  • không có thành phần nào phụ thuộc vào bên trong của thành phần khác (tính mô đun)
  • chương trình chỉ định nghĩa hành vi của các đối tượng, không phải đại diện của chúng (đa hình)
  • mỗi thành phần chỉ xuất hiện ở một nơi (bao thanh toán)
  • việc sử dụng máy ảo độc lập với phần cứng
  • mọi thành phần người dùng có thể truy cập nên có sẵn để quan sát và kiểm soát (nguyên tắc phản ứng)
  • không nên có bộ điều khiển tổng thể (không có hệ điều hành)

Đây là những nguyên tắc Ingalls được xác định là cân nhắc thiết kế lái xe cho SmallTalk-80 được phát triển bởi Xerox Parc Research. Trong bài viết trên tạp chí đã nói ở trên, bạn có thể đọc một mô tả chi tiết về từng nguyên tắc này và cách chúng đóng góp cho mô hình hướng đối tượng theo Ingalls.

Tất cả các nguyên tắc này có thể được áp dụng bằng bất kỳ ngôn ngữ hoàn chỉnh Turing nào, cho dù đó là ngôn ngữ thủ tục, ngôn ngữ lắp ráp hay bất cứ thứ gì. Đây là những nguyên tắc thiết kế, không phải là một đặc điểm kỹ thuật ngôn ngữ. Một ngôn ngữ hướng đối tượng được dự định để làm cho việc sử dụng các nguyên tắc này dễ dàng hơn khi tạo phần mềm.

Ví dụ, để thực hiện các nguyên tắc đầu tiên của Ingall (quản lý lưu trữ tự động), bất kỳ ai cũng có thể viết hệ thống quản lý lưu trữ tự động của riêng mình bằng ngôn ngữ thủ tục, nhưng sẽ rất nhiều việc phải làm. Khi sử dụng một ngôn ngữ như SmallTalk hoặc Java có tích hợp quản lý lưu trữ tự động, lập trình viên không phải thực hiện nhiều công việc quản lý bộ nhớ. Sự đánh đổi là lập trình viên sẽ kiểm soát ít hơn cách sử dụng bộ nhớ. Vì vậy, có một lợi ích và một nhược điểm. Ý tưởng của một mô hình thiết kế như lập trình hướng đối tượng là những lợi ích của mô hình này sẽ vượt trội hơn những nhược điểm đối với ít nhất một số lập trình viên.


Tôi nghĩ rằng các quy tắc tương tự sẽ được áp dụng trong trường hợp Ngôn ngữ cụ thể miền. Cùng một ưu điểm và nhược điểm ... Một điểm khác biệt là DSL có thể được tạo ra đủ đơn giản để người dùng cuối có thể làm việc, vì 'ngôn ngữ' tương ứng với sự hiểu biết của họ về không gian vấn đề và không có gì khác được đưa vào.

0

Một cách để quản lý độ phức tạp của phần mềm là tách biệt khung công tác khỏi các hành động mong muốn hoàn toàn bằng Ngôn ngữ cụ thể miền . Điều này có nghĩa là mức mã lập trình khác với cấp độ mà kết quả mong muốn được cấu hình - một ngôn ngữ hoặc hệ thống hoàn toàn khác. Khi điều này được thực hiện đúng, mã thông thường về cơ bản sẽ trở thành một thư viện và người dùng hoặc người khác tạo ra kết quả mong muốn sẽ kết nối mọi thứ với ngôn ngữ kịch bản hoặc công cụ thiết kế trực quan, như trình tạo báo cáo.

Để làm việc, điều này đòi hỏi phải vẽ một ranh giới nghiêm ngặt xung quanh những hoạt động nào sẽ có thể và cách chúng liên kết với nhau (ngôn ngữ kịch bản hoặc thiết kế trực quan, giống như một công cụ xây dựng biểu mẫu). Siêu dữ liệu là một cách quan trọng để trừu tượng hóa cấu hình thời gian chạy khỏi các chi tiết mã hóa, giúp hệ thống có thể hỗ trợ một loạt các kết quả mong muốn. Nếu các ranh giới được đặt ra và giữ (không chấp nhận mọi yêu cầu gia hạn đi kèm), bạn có thể có một hệ thống lâu dài và mạnh mẽ, phù hợp với mọi người, mà không cần phải lập trình viên để thực hiện những gì họ muốn.

Martin Fowler đã viết một cuốn sách về điều này, và kỹ thuật này gần như cũ như chính lập trình. Bạn gần như có thể nói rằng tất cả các ngôn ngữ lập trình là Ngôn ngữ cụ thể của miền, và vì vậy ý ​​tưởng này là đặc hữu, bị bỏ qua vì nó quá rõ ràng. Nhưng bạn vẫn có thể tạo các công cụ thiết kế kịch bản hoặc hình ảnh của riêng mình để làm cho cuộc sống của bạn dễ dàng hơn. Đôi khi khái quát hóa một vấn đề làm cho nó dễ dàng hơn để giải quyết!


0

Đây là một câu hỏi rất hay và tôi cảm thấy câu trả lời được đưa ra ở đây chưa thực hiện công lý, vì vậy tôi sẽ tiếp tục và thêm suy nghĩ của mình.

Mục đích là - Quản lý độ phức tạp phần mềm . Mục đích không phải là "sử dụng ngôn ngữ OO".

Không có "lý do" đằng sau việc giới thiệu một mô hình mới. Đó là điều xảy ra một cách tự nhiên khi tiền mã hóa trở nên trưởng thành hơn. Sẽ có ý nghĩa hơn khi viết mã khi chúng ta thêm một huấn luyện viên ở cuối tàu (tàu được mô hình hóa bằng danh sách được liên kết) thay vì thêm một nút mới vào cuối danh sách được liên kết.


Mã hóa trong các điều khoản của các tổ chức thế giới thực chỉ đơn giản là một cách rõ ràng và chính xác theo mã khi chúng ta được mã hóa về các đối tượng thế giới thực.


Một máy tính có thể hoạt động với việc thêm một nút vào cuối danh sách được liên kết một cách dễ dàng vì nó có thể hoạt động với việc thêm một huấn luyện viên phụ vào cuối tàu. Nhưng đối với con người, làm việc với tàu và huấn luyện viên sẽ dễ dàng hơn so với danh sách và nút được liên kết mặc dù khi chúng tôi đi sâu vào cấp độ, chúng tôi thấy tàu được mô hình hóa bằng phương tiện của danh sách được liên kết.

Bảo vệ hoặc mã hóa các tập tin có thể đạt được đóng gói. Trái ngược với mã hóa là giải mã. Đối lập với đóng gói là Decapsulation có nghĩa là Phân rã các cấu trúc và các lớp trong các ngôn ngữ lập trình để đạt được hiệu suất tốt hơn. Hiệu suất đạt được từ việc giảm lưu lượng bộ nhớ và tránh kiểm tra quy tắc OOP.

Do đó, bạn có thể viết mã được mã hóa và đóng gói tốt vì hai khái niệm này là khác nhau.

Đóng gói giúp quản lý sự phức tạp nhờ vào sự gần gũi với thực tế.

Do đó, hãy lập trình trong các đối tượng bởi vì, nó dễ dàng hơn cho bạn viết mã và nó nhanh hơn cho bạn và mọi người khác hiểu.


0

Một điều cần nhớ là:
OOP không phải là về các tính năng ngôn ngữ; đó là về cách bạn cấu trúc mã của bạn .

OOP là một cách suy nghĩ và thiết kế kiến ​​trúc mã của bạn, và nó có thể được thực hiện bằng bất kỳ ngôn ngữ nào. Điều này đặc biệt bao gồm các ngôn ngữ cấp thấp, không phải OO, được gọi là trình biên dịch chương trình và C. Bạn có thể thực hiện lập trình hướng đối tượng hoàn hảo trong trình biên dịch chương trình và nhân Linux, được viết bằng C, khá hướng đối tượng về nhiều mặt .

Điều đó nói rằng, các tính năng OO trong một ngôn ngữ làm giảm đáng kể số lượng mã soạn sẵn mà bạn cần viết để đạt được kết quả mong muốn . Khi bạn cần xác định rõ ràng một bảng chức năng ảo và điền vào nó với các con trỏ hàm thích hợp trong C, bạn không cần làm trong Java và bạn đã hoàn thành. Các ngôn ngữ OO chỉ đơn giản là loại bỏ tất cả những gì cho phép cruft khỏi mã nguồn, ẩn nó sau các tóm tắt cấp độ ngôn ngữ tốt đẹp (như các lớp, phương thức, thành viên, lớp cơ sở, các hàm gọi hàm / hàm hủy ẩn, v.v.).

Vì vậy, không, chúng tôi không cần ngôn ngữ OO để thực hiện OOP. Chỉ là OOP dễ thực hiện hơn với ngôn ngữ OO đàng hoàng.


-1

Lập trình hướng đối tượng không chỉ là mô-đun + đóng gói. Như bạn nói, có thể sử dụng các mô-đun + đóng gói trong một ngôn ngữ (thủ tục) không hướng đối tượng. OOP không chỉ liên quan đến điều đó: nó liên quan đến các đối tượng và phương thức. Vì vậy, không, điều đó không chiếm được OOP. Xem, ví dụ: https://en.wikipedia.org/wiki/Object-oriented_programming hoặc giới thiệu sách giáo khoa tốt về OOP.


Cảm ơn bạn đã trả lời. Bạn có thể giới thiệu một?
steakexchange

-2

Lý do lớn nhất là, khi một chương trình trở nên phức tạp hơn, bạn cần làm cho một số phần của nó không nhìn thấy được từ các phần khác, hoặc sự phức tạp của ứng dụng và số lượng chức năng sẽ khiến não bạn chảy ra khỏi tai bạn.

Chúng ta hãy tưởng tượng một hệ thống gồm 100 lớp, mỗi lớp có khoảng 20 thao tác có thể được thực hiện trên chúng; Đó là 2.000 chức năng. Tuy nhiên, trong số đó, có thể chỉ có 500 hoạt động hoàn chỉnh như 'Lưu' và 'Xóa', trong khi 1500 là các chức năng nội bộ thực hiện một chút bảo trì hoặc có một số vai trò tiện ích. Xem xét;

// intentionally in a non-specific language!

setName(person, name) {
    nameParts = splitPersonName(name);
    person.firstName = nameParts[0];
    person.lastName = nameParts[1];
    person.modified = true;
}

splitPersonName(name) {
    var result = [];
    result.add(name.substring(0, name.indexOf(" ")));
    result.add(name.substring(name.indexOf(" ") + 1));
    return result;
}

Vì vậy, SetNamemột chức năng mọi người nên làm cho một người, nhưng SplitPersonNamelà một chức năng tiện ích được sử dụng bởi người đó.

Lập trình thủ tục thẳng làm cho không có sự phân biệt giữa hai hoạt động. Điều đó có nghĩa là 2.000 chức năng của bạn tất cả vie cho sự chú ý của bạn. Tuy nhiên, nếu chúng ta có thể đánh dấu các chức năng này là 'khả dụng cho mọi người có hồ sơ cá nhân' và 'chỉ được sử dụng làm chức năng tiện ích trong hồ sơ cá nhân' thì hiện tại chúng tôi đã có sẵn 500 'cho các chức năng của mọi người và 15' tiện ích ' các chức năng cho lớp bạn đang chỉnh sửa.

Đó là những gì publicprivatelàm;

public class Person {
    public void setName(...) {...}
    private string[] splitPersonName(...) { ...}
}

1
Tôi không chắc chắn nếu bạn hiểu chính xác câu hỏi của tôi. Tôi đã nhận thức được việc đóng gói và ẩn dữ liệu như một cách để quản lý sự phức tạp. Tôi chỉ nghĩ rằng điều này có thể được thực hiện dễ dàng bằng các ngôn ngữ thủ tục bằng cách chia chương trình thành các mô đun thực hiện các nhiệm vụ đơn giản được xác định rõ có hoạt động bên trong được chỉ định trong các nguồn được bảo vệ riêng biệt. Vậy tại sao OOP khi chúng ta có thể làm những điều đó bằng ngôn ngữ thủ tục? Đó là câu hỏi của tôi.
steakexchange

2
Tôi đoán câu trả lời của tôi là nếu bạn muốn làm điều này, bạn sẽ phải viết công cụ đặc biệt và cấu trúc ngôn ngữ để làm như vậy (ví dụ: một cuộc gọi 'bao gồm' đặc biệt đến một tệp bao gồm có thể không được bao gồm ở nơi khác). Khi bạn đã bắt đầu con đường đó, bạn thực sự bắt đầu thực hiện một ngôn ngữ hướng đối tượng theo cách riêng của bạn. Ví dụ: C ++ ban đầu là một bộ tiền xử lý tạo ra C đơn giản và tôi nghi ngờ một khi bạn triển khai hệ thống của mình, nó trông rất giống C ++ trên đầu C.
user62575 20/03/2017

3
@steakexchange Lưu ý rằng OOP được phát triển vào thời điểm mà nhiều ngôn ngữ thủ tục không có các mô-đun như thế. Một số thậm chí sẽ không gọi các ngôn ngữ như vậy theo thủ tục. Thật vậy, không có gì nói một ngôn ngữ thủ tục không được là OO hoặc ngược lại. Hãy cẩn thận với nhãn - chúng có thể dễ dàng đánh lừa bạn. Nếu ngôn ngữ thủ tục của bạn hỗ trợ các mô-đun có các trường và thủ tục công khai và riêng tư, tốt cho bạn :) Sự khác biệt chính giữa "thủ tục truyền thống" và "OOP" là việc gửi cuộc gọi linh hoạt hơn trong OOP - thực sự, trong OOP nghiêm ngặt, bạn không bao giờ biết bạn gọi mã gì
Luaan

2
@steakexchange Trong các ngôn ngữ gia đình ML, các mô-đun hoạt động rất tốt và kết hợp với lambdas, chúng cung cấp cho bạn tất cả sức mạnh của bất kỳ ngôn ngữ OO nào (xét cho cùng, một chức năng là một giao diện với một phương thức duy nhất - không phải là hầu hết những gì được đề xuất bởi Những người "mã tốt" trong OO ?: P). Vì nhiều lý do, chúng vẫn ít được sử dụng hơn các ngôn ngữ thủ tục hơn như C ++ hoặc Java, nhưng chúng có sức hấp dẫn và nhiều người đang cố gắng giáo dục mọi người về cách họ có thể đơn giản hóa cuộc sống của họ (với ít nhiều thành công).
Luaan

C có hiệu quả có những điều này. Nó có các mô-đun (tệp .c) với giao diện (tệp .h) và nó có thể có các phương thức công khai (extern) và không công khai (extern). Bạn thậm chí có thể có tính đa hình của người nghèo với các mảng con trỏ hàm, tôi không nói OO dễ dàng trong C (hoặc có lẽ, lành mạnh) nhưng đóng gói khá dễ dàng,
Nick Keighley
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.