OOP giải quyết vấn đề gì trong lập trình thủ tục trong thực tế?


17

Tôi đã nghiên cứu cuốn sách "C ++ Demystified" . Bây giờ tôi đã bắt đầu đọc "Lập trình hướng đối tượng trong phiên bản đầu tiên của Turbo C ++ (phiên bản 1)" của Robert Lafore. Tôi không có bất kỳ kiến ​​thức nào về lập trình nằm ngoài những cuốn sách này. Cuốn sách này có thể đã lỗi thời vì nó 20 tuổi. Tôi có phiên bản mới nhất, tôi đang sử dụng bản cũ vì tôi thích nó, chủ yếu tôi chỉ nghiên cứu các khái niệm cơ bản về OOP được sử dụng trong C ++ thông qua phiên bản đầu tiên của cuốn sách của Lafore.

Cuốn sách của Lafore nhấn mạnh rằng "OOP" chỉ hữu ích cho các chương trình lớn hơnphức tạp . Người ta nói trong mọi cuốn sách OOP (cũng trong cuốn sách của Lafore) rằng mô hình thủ tục dễ bị lỗi, ví dụ như dữ liệu toàn cầu dễ bị tổn thương bởi các chức năng. Người ta nói rằng lập trình viên có thể mắc lỗi trung thực trong các ngôn ngữ thủ tục, ví dụ như bằng cách tạo một hàm vô tình làm hỏng dữ liệu.

Nói một cách trung thực, tôi đang đăng câu hỏi của mình vì tôi không nắm bắt được lời giải thích được đưa ra trong cuốn sách này: Lập trình hướng đối tượng trong C ++ (Ấn bản thứ 4) Tôi không nắm bắt được những tuyên bố này được viết trong cuốn sách của Lafore:

Lập trình hướng đối tượng được phát triển vì những hạn chế đã được phát hiện trong các cách tiếp cận lập trình trước đó .... Khi các chương trình ngày càng lớn hơn và phức tạp hơn, ngay cả cách tiếp cận lập trình có cấu trúc cũng bắt đầu có dấu hiệu căng thẳng ... .... Phân tích lý do cho những thất bại này cho thấy rằng có những điểm yếu trong chính mô hình thủ tục. Cho dù phương pháp lập trình có cấu trúc được triển khai tốt đến đâu, các chương trình lớn trở nên quá phức tạp .... ... Có hai vấn đề liên quan. Đầu tiên, các chức năng có quyền truy cập không hạn chế vào dữ liệu toàn cầu. Thứ hai, các chức năng và dữ liệu không liên quan, cơ sở của mô hình thủ tục, cung cấp một mô hình nghèo nàn về thế giới thực ...

Tôi đã nghiên cứu cuốn sách "rối loạn phân ly C ++" của Jeff Kent, tôi rất thích cuốn sách này, trong cuốn sách này chủ yếu là lập trình thủ tục được giải thích. Tôi không hiểu tại sao lập trình thủ tục (có cấu trúc) yếu!

Cuốn sách của Lafore giải thích khái niệm này rất độc đáo với một số ví dụ hay. Ngoài ra, tôi đã nắm bắt được một trực giác bằng cách đọc cuốn sách của Lafore rằng OOP tốt hơn lập trình thủ tục nhưng tôi tò mò muốn biết chính xác trong thực tế lập trình thủ tục yếu hơn OOP như thế nào.

Tôi muốn xem bản thân những vấn đề thực tế mà người ta sẽ gặp phải trong lập trình thủ tục là gì, làm thế nào OOP sẽ giúp việc lập trình dễ dàng hơn. Tôi nghĩ rằng tôi sẽ có câu trả lời của mình chỉ bằng cách đọc cuốn sách của Lafore một cách chiêm nghiệm nhưng tôi muốn tận mắt nhìn thấy các vấn đề trong mã thủ tục, tôi muốn xem mã kiểu OOP của chương trình loại bỏ các lỗi đã nói ở trên như thế nào nếu cùng một chương trình đã được viết bằng cách sử dụng mô hình thủ tục.

Có nhiều tính năng của OOP và tôi hiểu rằng không ai có thể giải thích cho tôi cách tất cả các tính năng này loại bỏ các lỗi đã nêu ở trên bằng cách viết mã theo kiểu thủ tục.

Vì vậy, đây là câu hỏi của tôi:

Những hạn chế nào của lập trình thủ tục không giải quyết OOP và làm thế nào để loại bỏ hiệu quả những hạn chế này trong thực tế?

Cụ thể, có những ví dụ cho các chương trình khó thiết kế bằng mô hình thủ tục nhưng được thiết kế dễ dàng bằng OOP không?

PS: Cross được đăng từ: /programming//q/22510004/3429430


3
Sự khác biệt giữa lập trình thủ tục và lập trình hướng đối tượng ở một mức độ nào đó là vấn đề trình bày và nhấn mạnh. Hầu hết các ngôn ngữ tự quảng cáo là hướng đối tượng cũng mang tính thủ tục - các thuật ngữ xem xét các khía cạnh khác nhau của ngôn ngữ.
Gilles 'SO- ngừng trở nên xấu xa'

2
Tôi mở lại câu hỏi bây giờ; Hãy xem điều này diễn ra như thế nào. Hãy nhớ rằng bất kỳ điều trị nào trình bày OOP đều là chén thánh, giải quyết mọi vấn đề sai lầm của ngôn ngữ lập trình là phun ra vô nghĩa. Có những ưu và nhược điểm. Bạn đang yêu cầu những ưu điểm, và đó là một câu hỏi công bằng; đừng mong đợi nhiều hơn
Raphael

Ý nghĩ thiết kế GUI máy tính để bàn (hiện đại) không có OOP và một số kỹ thuật phát triển trên nó (ví dụ mẫu sự kiện / người nghe) làm tôi sợ. Tuy nhiên, điều đó không có nghĩa là không thể thực hiện được (chắc chắn là có thể ). Ngoài ra, nếu bạn muốn thấy thất bại của PP tại nơi làm việc, hãy xem PHP và so sánh API với, giả sử, Ruby.
Raphael

1
"Cho dù cách tiếp cận lập trình có cấu trúc được triển khai tốt đến đâu, các chương trình lớn cũng trở nên phức tạp quá mức ..." điều đó rất đúng với OOP nhưng về cơ bản, nó quản lý sự phức tạp tốt hơn nếu được các nhà phát triển áp dụng theo cách quy định ... và nó chủ yếu thông qua các giới hạn / giới hạn phạm vi tốt hơn / sắc nét hơn, tức là một loại hệ thống ngăn xếp ... hay còn gọi là APIE: Trừu tượng hóa, Đa hình, Kế thừa, Đóng gói
vzn

Câu trả lời:


9

Trong ngôn ngữ thủ tục, bạn không nhất thiết phải thể hiện các hạn chế bắt buộc để chứng minh rằng người gọi đang sử dụng một mô-đun theo kiểu được hỗ trợ. Trong trường hợp không có giới hạn kiểm tra trình biên dịch, bạn phải viết tài liệu và hy vọng rằng nó được tuân theo và sử dụng các bài kiểm tra đơn vị để chứng minh mục đích sử dụng.

Các loại khai báo là hạn chế khai báo rõ ràng nhất (nghĩa là: "chứng minh rằng x là một số float"). Buộc các đột biến dữ liệu đi qua một chức năng được biết là được thiết kế cho dữ liệu đó là một chức năng khác. Thực thi giao thức (gọi phương thức) là một hạn chế được hỗ trợ một phần, ví dụ: "constructor -> othermethods * -> killer".

Cũng có một lợi ích thực sự (và một vài nhược điểm) khi trình biên dịch biết về mẫu. Gõ tĩnh với các kiểu đa hình là một vấn đề khi bạn mô phỏng đóng gói dữ liệu từ một ngôn ngữ thủ tục. Ví dụ:

kiểu x1 là kiểu con của x, t1 là kiểu con của t

Đây là một cách để đóng gói dữ liệu trong ngôn ngữ thủ tục để có loại t với các phương thức f và g và một lớp con t1 cũng tương tự như vậy:

t_f (t, x, y, z, ...), t_g (t, x, y, ...) t1_f (t1, x, y, z, ...)

Để sử dụng mã này, bạn phải thực hiện kiểm tra loại và bật loại t trước khi quyết định loại f bạn sẽ gọi. Bạn có thể làm việc xung quanh nó như thế này:

loại t {d: dữ liệu f: chức năng g: chức năng}

Vì vậy, bạn gọi tf (x, y, z) thay vào đó, trong đó một kiểu chữ và chuyển đổi để tìm phương thức bây giờ được thay thế bằng chỉ có mỗi con trỏ phương thức lưu trữ đối tượng rõ ràng. Bây giờ nếu bạn có một số lượng lớn các chức năng cho mỗi loại, đây là một đại diện lãng phí. Sau đó, bạn có thể sử dụng một chiến lược khác như chỉ đến một biến m có chứa tất cả các hàm thành viên. Nếu tính năng này là một phần của ngôn ngữ, thì bạn có thể để trình biên dịch tìm ra cách xử lý để thể hiện hiệu quả của mẫu này.

Nhưng đóng gói dữ liệu là sự thừa nhận rằng trạng thái đột biến là xấu. Giải pháp hướng đối tượng là ẩn nó đằng sau các phương thức. Lý tưởng nhất là tất cả các phương thức trong một đối tượng sẽ có thứ tự gọi được xác định rõ (nghĩa là: hàm tạo -> mở -> [đọc | ghi] -> đóng -> hủy); đôi khi được gọi là "giao thức" (nghiên cứu: "Microsoft Singularity"). Nhưng ngoài việc xây dựng và phá hủy, các yêu cầu này thường không phải là một phần của hệ thống loại - hoặc được ghi chép tốt. Theo nghĩa này, các đối tượng là các thể hiện đồng thời của các máy trạng thái được chuyển đổi bằng các lệnh gọi phương thức; sao cho bạn có thể có nhiều trường hợp và sử dụng chúng theo cách xen kẽ tùy ý.

Nhưng khi nhận ra rằng trạng thái chia sẻ có thể thay đổi là xấu, có thể lưu ý rằng hướng đối tượng có thể tạo ra vấn đề tương tranh vì cấu trúc dữ liệu đối tượng là trạng thái có thể thay đổi mà nhiều đối tượng có tham chiếu. Hầu hết các ngôn ngữ hướng đối tượng thực thi trên luồng của người gọi, điều đó có nghĩa là có các điều kiện chủng tộc trong các yêu cầu phương thức; hãy để một mình trong chuỗi không nguyên tử của các cuộc gọi chức năng. Cách khác, mọi đối tượng có thể nhận các tin nhắn không đồng bộ trong hàng đợi và phục vụ tất cả chúng trên luồng của đối tượng (với các phương thức riêng tư của nó) và trả lời người gọi bằng cách gửi tin nhắn.

So sánh các cuộc gọi phương thức Java trong ngữ cảnh đa luồng với các quá trình Erlang gửi tin nhắn (chỉ tham chiếu các giá trị bất biến) cho nhau.

Định hướng đối tượng không hạn chế kết hợp với song song là một vấn đề do khóa. Có các kỹ thuật từ Bộ nhớ giao dịch phần mềm (nghĩa là: Giao dịch ACID trên đối tượng bộ nhớ tương tự như cơ sở dữ liệu) đến cách sử dụng phương pháp "chia sẻ bộ nhớ bằng cách truyền thông (dữ liệu không thay đổi)" (lai lập trình chức năng).

Theo tôi, tài liệu Định hướng đối tượng tập trung FAR quá nhiều vào tính kế thừa và không đủ cho giao thức (thứ tự yêu cầu phương thức có thể kiểm tra, điều kiện tiên quyết, hậu điều kiện, v.v.). Đầu vào mà một đối tượng tiêu thụ thường phải có một ngữ pháp được xác định rõ ràng, có thể biểu thị thành một loại.


Bạn đang nói rằng trong các ngôn ngữ OO, trình biên dịch có thể kiểm tra xem các phương thức được sử dụng theo thứ tự quy định hay các hạn chế khác đối với việc sử dụng các mô-đun? Tại sao "đóng gói dữ liệu [...] nhận ra rằng trạng thái có thể thay đổi là xấu"? Khi bạn nói về đa hình, bạn có cho rằng bạn đang sử dụng ngôn ngữ OO không?
babou

Trong OO, khả năng quan trọng nhất là có thể ẩn cấu trúc dữ liệu (nghĩa là: yêu cầu các tham chiếu phải từ như thế này.x) để chứng minh rằng tất cả các truy cập đều đi qua các phương thức. Trong ngôn ngữ OO được nhập tĩnh, bạn đang khai báo nhiều hạn chế hơn (dựa trên các loại). Lưu ý về thứ tự phương thức chỉ nói rằng OO buộc hàm tạo được gọi đầu tiên và hàm hủy; đó là bước đầu tiên trong cấu trúc cấm các lệnh gọi xấu. Bằng chứng dễ dàng là một mục tiêu quan trọng trong thiết kế ngôn ngữ.
Rob

8

Lập trình thủ tục / chức năng không yếu hơn OOP , thậm chí không đi sâu vào các đối số Turing (ngôn ngữ của tôi có sức mạnh Turing và có thể làm bất cứ điều gì khác sẽ làm), điều đó không có nghĩa gì nhiều. Trên thực tế, các kỹ thuật hướng đối tượng đã được thử nghiệm đầu tiên bằng các ngôn ngữ không có sẵn chúng. Theo nghĩa này, lập trình OO chỉ là một kiểu lập trình thủ tục cụ thể . Nhưng nó giúp thực thi các quy tắc cụ thể, chẳng hạn như mô-đun, trừu tượng hóa và ẩn thông tin rất cần thiết cho sự hiểu biết và bảo trì chương trình.

Một số mô hình lập trình phát triển từ tầm nhìn lý thuyết về tính toán. Một ngôn ngữ như Lisp phát triển từ lambda-tính toán và ý tưởng về tính tuần hoàn của các ngôn ngữ (tương tự như tính phản xạ trong ngôn ngữ tự nhiên). Mệnh đề sừng cha Prolog và lập trình ràng buộc. Gia đình Algol cũng nợ lambda-tính, nhưng không có tính phản xạ tích hợp.

Lisp là một ví dụ thú vị, vì nó đã là minh chứng cho sự đổi mới ngôn ngữ lập trình, có thể truy nguyên theo di sản kép của nó.

Tuy nhiên, ngôn ngữ sau đó phát triển, thường dưới tên mới. Một yếu tố chính của sự tiến hóa là thực hành lập trình. Người dùng xác định các thực tiễn lập trình cải thiện các thuộc tính của các chương trình như khả năng đọc, khả năng duy trì, khả năng chính xác. Sau đó, họ cố gắng thêm vào các tính năng ngôn ngữ hoặc các ràng buộc sẽ hỗ trợ và đôi khi thực thi các thực tiễn này để cải thiện chất lượng của các chương trình.

Điều này có nghĩa là những thực tiễn này đã có sẵn trong ngôn ngữ lập trình cũ hơn, nhưng cần có sự hiểu biết và kỷ luật để sử dụng chúng. Việc kết hợp chúng vào các ngôn ngữ mới như các khái niệm chính với cú pháp cụ thể làm cho các thực tiễn này dễ sử dụng và dễ hiểu hơn, đặc biệt đối với người dùng ít tinh vi hơn (ví dụ, đại đa số). Nó cũng làm cho cuộc sống dễ dàng hơn một chút cho người dùng tinh vi.

Theo một cách nào đó, nó là để thiết kế ngôn ngữ chương trình con / hàm / thủ tục là gì đối với chương trình. Khi khái niệm hữu ích được xác định, nó được đặt tên (có thể) và cú pháp, để có thể dễ dàng sử dụng nó trong tất cả các chương trình được phát triển với ngôn ngữ đó. Và khi thành công, nó cũng sẽ được kết hợp trong các ngôn ngữ trong tương lai.

Ví dụ: tái tạo hướng đối tượng

Bây giờ tôi cố gắng minh họa điều đó trên một ví dụ (chắc chắn có thể được đánh bóng thêm, theo thời gian nhất định). Mục đích của ví dụ này không phải là để chỉ ra rằng một chương trình hướng đối tượng có thể được viết theo kiểu lập trình thủ tục, có thể phải trả giá bằng tính khả thi và khả năng duy trì. Tôi sẽ cố gắng chỉ ra rằng một số ngôn ngữ không có cơ sở OO thực sự có thể sử dụng các hàm và cấu trúc dữ liệu bậc cao hơn để thực sự tạo ra các phương tiện để bắt chước Định hướng đối tượng một cách hiệu quả , để hưởng lợi từ các phẩm chất của nó đối với tổ chức chương trình, bao gồm mô đun hóa, trừu tượng hóa và ẩn thông tin .

Như tôi đã nói, Lisp là người thử nghiệm nhiều tiến hóa ngôn ngữ, bao gồm cả mô hình OO (mặc dù thứ có thể được coi là ngôn ngữ OO đầu tiên là Simula 67, thuộc họ Algol). Lisp rất đơn giản và mã cho trình thông dịch cơ bản của nó ít hơn một trang. Nhưng bạn có thể làm lập trình OO trong Lisp. Tất cả bạn cần là chức năng thứ tự cao hơn.

Tôi sẽ không sử dụng cú pháp Lisp bí truyền, mà là mã giả, để đơn giản hóa việc trình bày. Và tôi sẽ xem xét một vấn đề thiết yếu đơn giản: ẩn thông tinmô đun hóa . Xác định một lớp đối tượng trong khi ngăn người dùng truy cập (hầu hết) việc thực hiện.

Giả sử tôi muốn tạo một lớp gọi là vectơ, đại diện cho vectơ 2 chiều, với các phương thức bao gồm: thêm vectơ, kích thước vectơ và song song.

function vectorrec () {  
  function createrec(x,y) { return [x,y] }  
  function xcoordrec(v) { return v[0] }  
  function ycoordrec(v) { return v[1] }  
  function plusrec (u,v) { return [u[0]+v[0], u[1]+v[1]] }  
  function sizerec(v) { return sqrt(v[0]*v[0]+v[1]*v[1]) }  
  function parallelrec(u,v) { return u[0]*v[1]==u[1]*v[0]] }  
  return [createrec, xcoordrec, ycoordrec, plusrec, sizerec, parallelrec]  
  }  

Sau đó tôi có thể gán vector đã tạo cho các tên hàm thực tế sẽ được sử dụng.

[vector, xcoord, ycoord, vplus, vsize, vabul] = vectơ ()

Tại sao lại phức tạp như vậy? Bởi vì tôi có thể định nghĩa trong các cấu trúc trung gian vectorrec mà tôi không muốn hiển thị với phần còn lại của chương trình, để duy trì tính mô đun.

Chúng ta có thể tạo một bộ sưu tập khác trong tọa độ cực

function vectorpol () {  
  ...  
  function pluspol (u,v) { ... }  
  function sizepol (v) { return v[0] }  
  ...  
  return [createpol, xcoordpol, ycoordpol, pluspol, sizepol, parallelpol]  
  }  

Nhưng tôi có thể muốn sử dụng một cách thờ ơ cả hai triển khai. Một cách để làm điều đó là thêm một thành phần kiểu cho tất cả các giá trị, xác định tất cả các hàm trên trong cùng một môi trường: Sau đó tôi có thể xác định từng hàm được trả về để trước tiên nó sẽ kiểm tra loại tọa độ, sau đó áp dụng hàm cụ thể cho nó.

function vector () {  
    ...  
    function plusrec (u,v) { ... }  
    ...  
    function pluspol (u,v) { ... }  
    ...  
    function plus (u,v) { if u[2]='rec' and v[2]='rec'  
                            then return plusrec (u,v) ... }  

    return [ ..., plus, ...]  
    }

Những gì tôi đã đạt được: các hàm cụ thể vẫn ở chế độ ẩn (vì phạm vi định danh cục bộ) và phần còn lại của chương trình chỉ có thể sử dụng các hàm trừu tượng nhất được trả về bởi lệnh gọi đến lớp vectơ.

Một sự phản đối là tôi có thể định nghĩa trực tiếp từng hàm trừu tượng trong chương trình và để lại bên trong định nghĩa của các hàm phụ thuộc kiểu tọa độ. Sau đó cũng sẽ được ẩn. Điều đó là đúng, nhưng sau đó mã cho từng loại tọa độ sẽ được cắt thành từng phần nhỏ trải đều trên chương trình, ít có thể điều chỉnh và duy trì được.

Trên thực tế, tôi thậm chí không cần đặt tên cho chúng và tôi chỉ có thể giữ các giá trị chức năng ẩn danh trong cấu trúc dữ liệu được lập chỉ mục theo loại và chuỗi đại diện cho tên hàm. Cấu trúc này là cục bộ của vectơ chức năng sẽ vô hình từ phần còn lại của chương trình.

Để đơn giản hóa việc sử dụng, thay vì trả về một danh sách hàm, tôi có thể trả về một hàm duy nhất gọi là áp dụng làm đối số một giá trị kiểu rõ ràng và một chuỗi, và áp dụng hàm với loại và tên thích hợp. Điều này trông rất giống như gọi một phương thức cho một lớp OO.

Tôi sẽ dừng lại ở đây, trong lần tái thiết này của một cơ sở hướng đối tượng.

Những gì tôi đã cố gắng làm là chỉ ra rằng không quá khó để xây dựng hướng đối tượng có thể sử dụng được bằng một ngôn ngữ đủ mạnh, bao gồm cả tính kế thừa và các tính năng khác như vậy. Metacircularity của trình thông dịch có thể giúp đỡ, nhưng chủ yếu ở mức độ cú pháp, vẫn còn xa không đáng kể.

Những người dùng đầu tiên của định hướng đối tượng đã thử nghiệm các khái niệm theo cách đó. Và điều đó thường đúng với nhiều cải tiến đối với các ngôn ngữ lập trình. Tất nhiên, phân tích lý thuyết cũng có một vai trò và giúp hiểu hoặc tinh chỉnh các khái niệm này.

Nhưng ý tưởng rằng các ngôn ngữ không có tính năng OO chắc chắn sẽ thất bại trong một số dự án chỉ đơn giản là không có cơ sở. Nếu cần, họ có thể bắt chước việc thực hiện các tính năng này khá hiệu quả. Nhiều ngôn ngữ có sức mạnh cú pháp và ngữ nghĩa để thực hiện định hướng đối tượng khá hiệu quả, ngay cả khi nó không được tích hợp. Và đó là nhiều hơn một đối số Turing.

OOP không giải quyết các hạn chế của các ngôn ngữ khác, nhưng nó hỗ trợ hoặc thực thi các phương pháp lập trình giúp viết chương trình tốt hơn, do đó giúp người dùng ít kinh nghiệm tuân theo các thực tiễn tốt mà các lập trình viên tiên tiến hơn đã sử dụng và phát triển mà không cần sự hỗ trợ đó.

Tôi tin rằng một cuốn sách tốt để hiểu tất cả điều này có thể là Abelson & Sussman: cấu trúc và giải thích các chương trình máy tính .


8

Một chút của lịch sử là theo thứ tự, tôi nghĩ.

Thời đại từ giữa thập niên 1960 đến giữa thập niên 1970 được gọi là "khủng hoảng phần mềm". Tôi không thể đặt nó tốt hơn Dijkstra trong bài giảng về giải thưởng Turing từ năm 1972:

Nguyên nhân chính của cuộc khủng hoảng phần mềm là các máy móc đã trở thành một số đơn đặt hàng mạnh mẽ hơn! Nói một cách khá thẳng thắn: miễn là không có máy móc, lập trình không có vấn đề gì cả; Khi chúng ta có một vài máy tính yếu, lập trình trở thành một vấn đề nhẹ, và bây giờ chúng ta có máy tính khổng lồ, lập trình đã trở thành một vấn đề không kém.

Đây là thời của các máy tính 32 bit đầu tiên, bộ đa xử lý thực sự đầu tiên và máy tính nhúng đầu tiên, và rõ ràng với các nhà nghiên cứu rằng những thứ này sẽ rất quan trọng để lập trình trong tương lai. Đó là một thời gian trong lịch sử khi nhu cầu của khách hàng vượt xa khả năng lập trình viên lần đầu tiên.

Không có gì đáng ngạc nhiên, đó là một thời gian màu mỡ đáng kể trong nghiên cứu lập trình. Trước giữa những năm 1960, chúng tôi đã có LISP và AP / L, nhưng các ngôn ngữ "chính" về cơ bản là thủ tục: FORTRAN, ALGOL, COBOL, PL / I, v.v. Từ giữa những năm 1960 đến giữa những năm 1970, chúng ta đã có Logo, Pascal, C, Forth, Smalltalk, Prolog, ML và Modula và không tính DSL như SQL và các tiền thân của nó.

Đó cũng là một thời gian trong lịch sử khi nhiều kỹ thuật chính để thực hiện các ngôn ngữ lập trình đang được phát triển. Trong giai đoạn này, chúng tôi đã phân tích cú pháp LR, phân tích dataflow, loại bỏ phổ biến phụ và nhận ra đầu tiên rằng một số vấn đề của trình biên dịch (ví dụ phân bổ đăng ký) là NP-hard và cố gắng giải quyết chúng như vậy.

Đây là bối cảnh mà OOP xuất hiện. Vì vậy, câu trả lời đầu thập niên 1970 cho câu hỏi của bạn về vấn đề OOP giải quyết trong thực tế, câu trả lời đầu tiên là nó xuất hiện để giải quyết nhiều vấn đề (cả đương đại và dự đoán) đang đối mặt với các lập trình viên trong giai đoạn lịch sử đó. Tuy nhiên, đây không phải là lúc OO trở thành xu hướng. Chúng tôi sẽ sớm nhận được điều đó.

Khi Alan Kay đặt ra thuật ngữ "hướng đối tượng", bức tranh mà anh có trong đầu là các hệ thống phần mềm sẽ có cấu trúc như hệ thống sinh học. Bạn sẽ có một cái gì đó giống như các tế bào riêng lẻ ("đối tượng") tương tác với nhau bằng cách gửi một cái gì đó tương tự như tín hiệu hóa học ("tin nhắn"). Bạn không thể (hoặc, ít nhất, sẽ không) nhìn vào trong một tế bào; bạn sẽ chỉ tương tác với nó thông qua các đường dẫn tín hiệu. Hơn nữa, bạn có thể có nhiều hơn một loại tế bào nếu bạn cần.

Bạn có thể thấy rằng có một vài chủ đề quan trọng ở đây: khái niệm về giao thức báo hiệu được xác định rõ (theo thuật ngữ hiện đại, giao diện), khái niệm ẩn thực hiện từ bên ngoài (theo thuật ngữ hiện đại, quyền riêng tư) và khái niệm về có nhiều "thứ" cùng loại treo xung quanh cùng một lúc (theo thuật ngữ hiện đại, thuyết minh).

Một điều bạn có thể lưu ý là thiếu, và đó là sự kế thừa, và có một lý do cho việc này.

Lập trình hướng đối tượng là một khái niệm trừu tượng và khái niệm trừu tượng có thể được thực hiện theo những cách khác nhau trong các ngôn ngữ lập trình khác nhau. Ví dụ, khái niệm trừu tượng của một "phương thức" có thể được triển khai trong C bằng cách sử dụng các con trỏ hàm và trong C ++ bằng các hàm thành viên và trong Smalltalk bằng các phương thức (không đáng ngạc nhiên, vì Smalltalk thực hiện khái niệm trừu tượng khá nhiều). Đây là những gì mọi người muốn nói khi họ chỉ ra (hoàn toàn đúng) rằng bạn có thể "làm" OOP bằng (gần như) bất kỳ ngôn ngữ nào.

Kế thừa, mặt khác, là một tính năng ngôn ngữ lập trình cụ thể. Kế thừa có thể hữu ích để thực hiện các hệ thống OOP. Hoặc ít nhất, đây là trường hợp cho đến đầu những năm 1990.

Thời gian từ giữa những năm 1980 đến giữa những năm 1990 cũng là thời điểm trong lịch sử khi mọi thứ đang thay đổi. Trong thời gian này, chúng tôi đã có sự phát triển của máy tính 32 bit giá rẻ, phổ biến, vì vậy các doanh nghiệp và nhiều gia đình có thể đủ khả năng để đặt một máy tính mạnh gần như các máy tính lớn cấp thấp nhất trong ngày trên mỗi bàn. Đó cũng là thời hoàng kim của Đây cũng là thời đại của sự phát triển của GUI hiện đại và hệ điều hành được nối mạng.

Chính trong bối cảnh này, Phân tích và Thiết kế hướng đối tượng đã nảy sinh.

Ảnh hưởng của OOAD, công việc của "ba Amigos" (Booch, Rumbar và Jacobson) và những người khác (ví dụ: phương pháp Shlaer Thẻ Mellor, thiết kế dựa trên trách nhiệm, v.v.), không thể được nhấn mạnh. Đó là lý do tại sao hầu hết các ngôn ngữ mới được phát triển từ đầu những năm 1990 (ít nhất, hầu hết những ngôn ngữ bạn đã nghe nói) đều có các đối tượng kiểu Simula.

Vì vậy, câu trả lời của thập niên 1990 cho câu hỏi của bạn là nó hỗ trợ giải pháp tốt nhất (vào thời điểm đó) cho phương pháp phân tích và thiết kế hướng miền.

Kể từ đó, vì chúng tôi có một cái búa, chúng tôi đã áp dụng OOP cho hầu hết mọi vấn đề phát sinh kể từ đó. OOAD và mô hình đối tượng mà nó sử dụng đã khuyến khích và cho phép phát triển dựa trên thử nghiệm và nhanh nhẹn, cụm và các hệ thống phân tán khác, v.v.

GUI hiện đại và bất kỳ hệ điều hành nào được thiết kế trong 20 năm qua có xu hướng cung cấp dịch vụ của nó dưới dạng đối tượng, do đó, bất kỳ ngôn ngữ lập trình thực tế mới nào cũng cần, ít nhất là một cách để liên kết với các hệ thống mà chúng ta sử dụng ngày nay.

Vì vậy, câu trả lời hiện đại là: Nó giải quyết vấn đề giao thoa với thế giới hiện đại. Thế giới hiện đại được xây dựng trên OOP với cùng lý do là thế giới năm 1880 được xây dựng trên hơi nước: chúng tôi hiểu nó, chúng tôi có thể kiểm soát nó và nó thực hiện công việc đủ tốt.

Điều đó không có nghĩa là nghiên cứu dừng lại ở đây, tất nhiên, nhưng nó chỉ ra mạnh mẽ rằng bất kỳ công nghệ mới nào cũng sẽ cần OO như một trường hợp hạn chế. Bạn không phải là OO, nhưng về cơ bản bạn không thể tương thích với nó.


Một khía cạnh mà tôi không muốn đưa vào bài tiểu luận chính là GUI WIMP và OOP dường như là một sự phù hợp cực kỳ tự nhiên. Nhiều điều tồi tệ có thể được nói về hệ thống phân cấp thừa kế sâu sắc, nhưng đây là một tình huống (có thể là tình huống CHỈ) trong đó nó dường như có ý nghĩa gì đó.
Bút danh

1
OOP xuất hiện đầu tiên trong Simula-67 (mô phỏng), trong tổ chức nội bộ của các hệ điều hành (ý tưởng về "lớp thiết bị" trong Unix về cơ bản là một lớp mà trình điều khiển kế thừa). Parnas' 'Trên tiêu chí được sử dụng trong quá trình phân huỷ các hệ thống thành các module' , CACM 15:12 (1972), pp. 1052-1058, Wirth của Modula ngôn ngữ từ những năm bảy mươi, 'các kiểu dữ liệu trừu tượng' là tất cả những tiền chất trong cách này hay cách khác
vonbrand

Điều đó hoàn toàn đúng, nhưng tôi cho rằng OOP không được coi là "giải pháp cho vấn đề lập trình thủ tục" cho đến giữa thập niên 70. Xác định "OOP" nổi tiếng là khó. Việc sử dụng thuật ngữ ban đầu của Alan Kay không đồng ý với mô hình của Simula và thật không may, thế giới đã tiêu chuẩn hóa mô hình của Simula. Một số mô hình đối tượng có cách giải thích giống Curry-Howard, nhưng Simula thì không. Stepanov có lẽ đã đúng khi ông lưu ý rằng thừa kế là không có căn cứ.
Bút danh

6

Không, thực sự. OOP không thực sự giải quyết vấn đề, nói đúng ra; bạn không thể làm gì với hệ thống hướng đối tượng mà bạn không thể làm với hệ thống không hướng đối tượng - thực sự, bạn không thể làm gì với điều đó không thể thực hiện được với máy Turing. Cuối cùng tất cả biến thành mã máy và ASM chắc chắn không hướng đối tượng.

Những gì mô hình OOP làm cho bạn là nó giúp tổ chức các biến và chức năng dễ dàng hơn và cho phép bạn di chuyển chúng xung quanh dễ dàng hơn.

Nói rằng tôi muốn viết một trò chơi bài bằng Python. Làm thế nào tôi đại diện cho các thẻ?

Nếu tôi không biết về OOP, tôi có thể làm theo cách này:

cards=["1S","2S","3S","4S","5S","6S","7S","8S","9S","10S","JS","QS","KS","1H","2H",...,"10C","JC","QC","KC"]

Có lẽ tôi sẽ viết một số mã để tạo ra các thẻ đó thay vì chỉ viết chúng ra bằng tay, nhưng bạn có được điểm. "1S" đại diện cho 1 of Spades, "JD" đại diện cho Jack of Diamonds, v.v. Tôi cũng cần một mã cho Joker, nhưng bây giờ chúng ta sẽ giả vờ không có Joker.

Bây giờ khi tôi muốn xáo trộn bộ bài, tôi chỉ cần "xáo trộn" danh sách. Sau đó, để lấy một thẻ ra khỏi đầu cỗ bài, tôi bật mục trên cùng ra khỏi danh sách, đưa cho tôi chuỗi. Đơn giản.

Bây giờ nếu tôi muốn tìm ra loại thẻ nào tôi đang làm việc với mục đích hiển thị nó cho người chơi, tôi cần một chức năng như thế này:

def card_code_to_name(code):
    suit=code[1]

    if suit=="S":
        suit="Spades"
    elif suit=="H"
        suit="Hearts"
    elif suit=="D"
        suit="Diamonds"
    elif suit=="C"
        suit="Clubs"

    value=code[0]

    if value=="J":
        value="Jack"
    elif value="Q":
        value="Queen"
    elif value="K"
        value="King"

    return value+" of "+suit

Một chút lớn, dài và không hiệu quả, nhưng nó hoạt động (và rất không linh hoạt, nhưng đó là bên cạnh điểm ở đây).

Bây giờ nếu tôi muốn thẻ có thể di chuyển xung quanh màn hình thì sao? Tôi phải lưu trữ vị trí của họ bằng cách nào đó. Tôi có thể thêm nó vào cuối mã thẻ của họ, nhưng điều đó có thể hơi khó sử dụng. Thay vào đó, hãy tạo một danh sách khác về nơi mỗi thẻ là:

cardpositions=( (1,1), (2,1), (3,1) ...)

Sau đó, tôi viết mã của mình để chỉ số của vị trí của mỗi thẻ trong danh sách giống với chỉ mục của chính thẻ trong bộ bài.

Hoặc ít nhất nó nên được. Trừ khi tôi phạm sai lầm. Điều mà tôi rất có thể, bởi vì mã của tôi sẽ phải khá phức tạp để xử lý thiết lập này. Khi tôi muốn xáo trộn các thẻ, tôi phải xáo trộn các vị trí theo cùng một thứ tự. Điều gì xảy ra nếu tôi lấy một thẻ ra khỏi bộ bài hoàn toàn? Tôi cũng sẽ phải đưa vị trí của nó ra, và đặt nó ở một nơi khác.

Và nếu tôi muốn lưu trữ nhiều thông tin hơn về thẻ thì sao? Điều gì sẽ xảy ra nếu tôi muốn lưu trữ cho dù mỗi thẻ được lật? Điều gì sẽ xảy ra nếu tôi muốn một động cơ vật lý nào đó và cũng cần biết vận tốc của thẻ? Tôi sẽ cần một danh sách khác hoàn toàn để lưu trữ đồ họa cho mỗi thẻ! Và đối với tất cả các điểm dữ liệu này, tôi sẽ cần mã riêng để giữ cho tất cả chúng được sắp xếp hợp lý để mỗi thẻ ánh xạ bằng cách nào đó với tất cả dữ liệu của nó!

Bây giờ chúng ta hãy thử cách này OOP.

Thay vì danh sách mã, hãy xác định một lớp Thẻ và xây dựng danh sách các đối tượng Thẻ từ đó.

class Card:

    def __init__(self,value,suit,pos,sprite,flipped=False):
        self.value=value
        self.suit=suit
        self.pos=pos
        self.sprite=sprite
        self.flipped=flipped

    def __str__(self):
        return self.value+" of "+self.suit

    def flip(self):
        if self.flipped:
            self.flipped=False
            self.sprite=load_card_sprite(value, suit)
        else:
            self.flipped=True
            self.sprite=load_card_back_sprite()

deck=[]
for suit in ("Spades","Hearts","Diamonds","Clubs"):
    for value in ("1","2","3","4","5","6","7","8","9","10","Jack","Queen","King"):
        sprite=load_card_sprite(value, suit)
        thecard=Card(value,suit,(0,0),sprite)
        deck.append(thecard)

Bây giờ, đột nhiên, mọi thứ đơn giản hơn nhiều. Nếu tôi muốn di chuyển thẻ, tôi không phải tìm ra vị trí của nó trong bộ bài, sau đó sử dụng thẻ đó để đưa vị trí của nó ra khỏi mảng vị trí. Tôi chỉ cần nói thecard.pos=newpos. Khi tôi lấy thẻ ra khỏi danh sách boong chính, tôi không phải lập một danh sách mới để lưu trữ tất cả dữ liệu khác đó; khi đối tượng thẻ di chuyển, tất cả các thuộc tính của nó di chuyển với nó. Và nếu tôi muốn một thẻ hoạt động khác khi lật, tôi không phải sửa đổi chức năng lật trong mã chính của mình để nó phát hiện các thẻ này và thực hiện hành vi khác đó; Tôi chỉ phải phân lớp Thẻ và sửa đổi hàm flip () trên lớp con.

Nhưng không có gì tôi làm ở đó không thể được thực hiện mà không có OO. Chỉ là với một ngôn ngữ hướng đối tượng, ngôn ngữ này đang thực hiện rất nhiều công việc để giữ mọi thứ lại với bạn, nghĩa là bạn có ít cơ hội để mắc lỗi hơn, và mã của bạn ngắn hơn và dễ đọc và viết hơn.

Hoặc, để tóm tắt hơn nữa, OO cho phép bạn viết các chương trình có vẻ đơn giản hơn, thực hiện công việc tương tự như các chương trình phức tạp hơn bằng cách che giấu rất nhiều sự phức tạp phổ biến của việc xử lý dữ liệu đằng sau một bức màn trừu tượng.


1
Nếu điều duy nhất bạn rút khỏi OOP là "quản lý bộ nhớ" thì tôi không nghĩ bạn đã hiểu nó rất rõ. Có cả một triết lý thiết kế và một chai "chính xác theo thiết kế" ở đó! Ngoài ra, quản lý bộ nhớ chắc chắn không có gì vốn có đối với định hướng đối tượng (C ++?), Mặc dù nhu cầu về nó trở nên rõ rệt hơn.
Raphael

Chắc chắn, nhưng đó là phiên bản một câu. Và tôi đã sử dụng thuật ngữ này theo một cách khá không chuẩn. Có lẽ tốt hơn là nói "xử lý thông tin" hơn là "quản lý bộ nhớ".
Schilcote

Có bất kỳ ngôn ngữ không phải OOP nào cho phép một hàm lấy một con trỏ tới một thứ gì đó, cũng như một con trỏ tới một hàm có tham số đầu tiên là một con trỏ tới cùng loại đó và trình biên dịch xác nhận rằng hàm đó là phù hợp cho con trỏ truyền vào?
supercat

3

Đã viết C nhúng trong một vài năm để quản lý những thứ như thiết bị, cổng nối tiếp và gói liên lạc giữa các cổng nối tiếp, cổng mạng và máy chủ; Tôi thấy mình, một kỹ sư điện được đào tạo với kinh nghiệm lập trình thủ tục hạn chế, pha chế những thứ trừu tượng của riêng tôi từ phần cứng mà cuối cùng tôi nhận ra là thứ mà mọi người bình thường gọi là 'Lập trình hướng đối tượng'.

Khi tôi chuyển sang phía máy chủ, tôi đã tiếp xúc với một nhà máy thiết lập biểu diễn đối tượng của từng thiết bị trong bộ nhớ khi khởi tạo. Tôi đã không hiểu những từ hoặc những gì đang xảy ra lúc đầu-- Tôi chỉ biết rằng tôi đã đi đến tập tin có tên như vậy và viết mã như vậy. Sau đó, tôi lại thấy mình, một lần nữa, cuối cùng cũng nhận ra giá trị của OOP.

Tôi, cá nhân, nghĩ rằng đây là cách duy nhất để dạy định hướng đối tượng. Tôi đã có một lớp Giới thiệu về OOP (Java) vào năm thứ nhất và nó hoàn toàn nằm trên đầu tôi. Mô tả OOP được xây dựng dựa trên phân loại mèo con-> mèo-> động vật có vú-> còn sống-> vật hoặc lá-> nhánh-> cây-> vườn, theo ý kiến ​​khiêm tốn của tôi, là phương pháp hoàn toàn vô lý bởi vì không ai sẽ cố gắng giải quyết chúng vấn đề, nếu bạn thậm chí có thể gọi chúng là vấn đề ...

Tôi nghĩ sẽ dễ dàng hơn để trả lời câu hỏi của bạn nếu bạn xem xét nó theo cách ít tuyệt đối hơn - không phải "nó giải quyết được gì", mà nhiều hơn từ góc độ "đây là một vấn đề, và đây là cách làm cho nó dễ dàng hơn". Trong trường hợp cổng nối tiếp cụ thể của tôi, chúng tôi đã có một loạt #ifdefs thời gian biên dịch đã thêm và xóa mã mà các cổng nối tiếp mở và đóng tĩnh. Các chức năng mở cổng được gọi ở khắp mọi nơi và có thể được đặt ở bất cứ đâu trong 100 nghìn dòng mã hệ điều hành chúng tôi có và IDE không làm mờ đi những gì không được xác định - bạn phải theo dõi thủ công và mang nó trong đầu của bạn. Chắc chắn bạn có thể có một số nhiệm vụ cố gắng mở một cổng nối tiếp nhất định mong đợi thiết bị của họ ở đầu bên kia, và sau đó không có mã nào bạn vừa viết hoạt động, và bạn không thể hiểu tại sao.

Sự trừu tượng hóa, mặc dù vẫn ở C, một 'lớp' cổng nối tiếp (tốt, chỉ là một kiểu dữ liệu cấu trúc) mà chúng ta có một mảng - một cho mỗi cổng nối tiếp - và thay vì có [tương đương DMA trong các cổng nối tiếp] Các hàm "OpenSerialPortA" "SetBaudRate", v.v. được gọi trực tiếp trên phần cứng từ tác vụ, chúng tôi đã gọi một hàm trợ giúp mà bạn đã truyền tất cả các tham số truyền thông (baud, parity, v.v.), trước tiên kiểm tra mảng cấu trúc để xem nếu đó cổng đã được mở-- nếu vậy, bằng nhiệm vụ nào, nó sẽ cho bạn biết như là một bản in gỡ lỗi, để bạn có thể ngay lập tức nhảy đến phần mã bạn cần vô hiệu hóa - và nếu không, thì nó đã tiến hành thiết lập tất cả các tham số thông qua các chức năng lắp ráp HAL của chúng, và cuối cùng đã mở cổng.

Tất nhiên, cũng có những nguy hiểm đối với OOP. Cuối cùng khi tôi dọn sạch codebase đó và làm cho mọi thứ gọn gàng và sạch sẽ - viết trình điều khiển mới cho dòng sản phẩm đó cuối cùng đã trở thành một khoa học có thể tính toán được, người quản lý của tôi EOL sẽ là sản phẩm đặc biệt bởi vì đó là một dự án ít hơn anh ta cần để quản lý, và ông là quản lý trung gian có thể tháo rời. Điều đó đã lấy đi rất nhiều của tôi / Tôi thấy rất nản lòng nên tôi đã nghỉ việc. CƯỜI LỚN.


1
Chào! Điều này có vẻ giống như một lịch sử cá nhân hơn là một câu trả lời cho câu hỏi. Từ ví dụ của bạn, tôi thấy rằng bạn viết lại một số mã khủng khiếp theo phong cách hướng đối tượng, làm cho nó tốt hơn. Nhưng không rõ liệu sự cải tiến có liên quan nhiều đến định hướng đối tượng hay chỉ là vì lúc đó bạn là một lập trình viên thành thạo hơn. Ví dụ, rất nhiều vấn đề của bạn dường như xuất phát từ việc mã bị phân tán willy-nilly về địa điểm. Điều đó có thể đã được giải quyết bằng cách viết một thư viện thủ tục, không có đối tượng nào cả.
David Richerby

2
@DavidR Richby chúng tôi có thư viện thủ tục, nhưng đó là những gì chúng tôi không tán thành, đó không chỉ là về mã ở khắp mọi nơi. Vấn đề là chúng tôi đã làm điều này ngược. Không ai cố gắng OOP bất cứ điều gì, nó chỉ xảy ra một cách tự nhiên.
paIncreas

@DavidR Richby bạn có thể đưa ra bất kỳ ví dụ nào về việc triển khai thư viện thủ tục để tôi có thể chắc chắn rằng chúng ta đang nói về điều tương tự không?
paIncreas

2
Cảm ơn câu trả lời của bạn và +1. Cách đây rất lâu, một lập trình viên giàu kinh nghiệm khác đã chia sẻ cách OOP làm cho dự án của anh ấy trở nên đáng tin cậy hơn.devshed.com/ programming-42/. Tôi đoán OOP được thiết kế rất thông minh bởi một số chuyên gia có thể gặp phải một số vấn đề trong cách tiếp cận thủ tục.
user31782

2

có nhiều tuyên bốý định về những gì / nơi lập trình OOP có lợi thế hơn so với lập trình thủ tục bao gồm bởi các nhà phát minh và người dùng của nó. nhưng chỉ vì một công nghệ được thiết kế cho một mục đích nhất định bởi các nhà thiết kế của nó không đảm bảo rằng nó sẽ thành công trong các mục tiêu đó. đây là một sự hiểu biết chính trong lĩnh vực công nghệ phần mềm có niên đại với bài tiểu luận nổi tiếng "Không có viên đạn bạc" của Brooks vẫn còn phù hợp bất chấp cuộc cách mạng mã hóa OOP. (xem thêm Chu kỳ Gartner Hype cho các công nghệ mới.)

nhiều người đã sử dụng cả hai cũng có ý kiến từ kinh nghiệm giai thoại, và điều này có một số giá trị, nhưng được biết trong nhiều nghiên cứu khoa học rằng phân tích tự báo cáo có thể không chính xác. dường như có rất ít phân tích định lượng về những khác biệt này hoặc nếu có, nó không được trích dẫn quá nhiều. Thật đáng ngạc nhiên khi nhiều nhà khoa học máy tính nói một cách có thẩm quyền về một số chủ đề nhất định trong lĩnh vực của họ nhưng không thực sự trích dẫn nghiên cứu khoa học để hỗ trợ quan điểm của họ và không nhận ra rằng họ đang thực sự truyền lại sự khôn ngoan thông thường trong lĩnh vực của họ (mặc dù phổ biến rộng rãi ).

vì đây là một trang web / diễn đàn khoa học , đây là một nỗ lực ngắn gọn để đưa ra nhiều ý kiến về bước đi vững chắc hơn và định lượng sự khác biệt thực tế. có thể có những nghiên cứu khác và hy vọng những người khác có thể chỉ ra nếu họ nghe thấy bất kỳ. (câu hỏi zen: nếu thực sự có một sự khác biệt lớn, và rất nhiều nỗ lực lớn trong lĩnh vực kỹ thuật phần mềm thương mại và các nơi khác đã được áp dụng / đầu tư để hiện thực hóa nó, tại sao phải có bằng chứng khoa học về điều này rất khó xảy ra? một số tài liệu tham khảo cổ điển, được trích dẫn nhiều trong lĩnh vực mà chắc chắn định lượng sự khác biệt?)

bài viết này sử dụng phân tích thực nghiệm / định lượng / khoa học & đặc biệt hỗ trợ sự hiểu biết của các lập trình viên mới làm quen được cải thiện với các phương pháp mã hóa OOP trong một số trường hợp nhưng không thuyết phục trong các trường hợp khác (liên quan đến kích thước chương trình). lưu ý rằng đây chỉ là một trong nhiều / tuyên bố chính về ưu thế OOP được nâng cao trong các câu trả lời khác & bởi những người ủng hộ OOP. nghiên cứu có thể đo lường một yếu tố tâm lý được gọi là sự hiểu biết về mã hóa "tải nhận thức / chi phí" .

  • Một so sánh về sự hiểu biết của các chương trình hướng đối tượng và thủ tục của các lập trình viên mới làm việc Tương tác với Máy tính. Susan Wiedenbeck, Vennila Ramalingam, Suseela Sarasamma, Cynthia L Corritore (1999)

    Bài viết này báo cáo về hai thí nghiệm so sánh các biểu diễn tinh thần và hiểu chương trình bởi người mới trong các phong cách thủ tục và hướng đối tượng. Các đối tượng là những lập trình viên mới làm việc đã đăng ký khóa học thứ hai về lập trình, dạy về mô hình hướng đối tượng hoặc mô hình thủ tục. Thí nghiệm đầu tiên so sánh các biểu diễn tinh thần và sự hiểu biết về các chương trình ngắn được viết theo phong cách thủ tục và hướng đối tượng. Thí nghiệm thứ hai đã mở rộng nghiên cứu sang một chương trình lớn hơn kết hợp các tính năng ngôn ngữ nâng cao hơn. Đối với các chương trình ngắn, không có sự khác biệt đáng kể giữa hai nhóm về tổng số câu hỏi được trả lời đúng, nhưng các đối tượng hướng đối tượng lại vượt trội so với các đối tượng thủ tục khi trả lời các câu hỏi về chức năng của chương trình. Điều này cho thấy rằng thông tin chức năng đã có sẵn hơn trong các biểu diễn tinh thần của các chương trình và hỗ trợ một lập luận rằng ký hiệu hướng đối tượng làm nổi bật chức năng ở cấp độ của từng lớp. Đối với chương trình dài, không tìm thấy hiệu ứng tương ứng. Sự hiểu biết của các đối tượng tố tụng là vượt trội so với các đối tượng hướng đối tượng trên tất cả các loại câu hỏi. Những khó khăn mà các đối tượng hướng đối tượng gặp phải trong việc trả lời các câu hỏi trong một chương trình lớn hơn cho thấy rằng họ phải đối mặt với các vấn đề trong việc sắp xếp thông tin và rút ra các kết luận từ đó. Chúng tôi đề xuất rằng kết quả này có thể liên quan đến thời gian học tập dài hơn đối với người mới về phong cách hướng đối tượng, cũng như các tính năng của phong cách OO và ký hiệu ngôn ngữ OO cụ thể.

Xem thêm:


1
Tôi tôn trọng các nghiên cứu thực nghiệm. Tuy nhiên, có vấn đề xác định rằng họ giải quyết các câu hỏi đúng. Có quá nhiều biến số trong cái có thể được gọi là OOP, và theo cách sử dụng nó, cho một nghiên cứu duy nhất có ý nghĩa, imho. Giống như nhiều thứ trong lập trình, OOP được tạo ra bởi các chuyên gia để đáp ứng nhu cầu của chính họ . Khi thảo luận về tính hữu ích của OOP (mà tôi không coi là chủ đề của OP, mà là liệu nó có giải quyết được sự thiếu sót của lập trình thủ tục hay không), người ta có thể hỏi: tính năng nào, cho ai, cho mục đích gì? Sau đó, chỉ làm nghiên cứu thực địa trở nên đầy đủ ý nghĩa.
babou

1
Cảnh báo giai thoại: Nếu một vấn đề nhỏ (ví dụ lên tới khoảng 500-1000 dòng mã), OOP không có sự khác biệt trong trải nghiệm của tôi, nó thậm chí có thể gây cản trở bằng cách phải lo lắng về những thứ tạo ra rất ít sự khác biệt. Nếu sự cố lớn và có một số dạng "phần có thể hoán đổi" thì hơn nữa phải có thể thêm vào sau (cửa sổ trong GUI, các thiết bị trong hệ điều hành, ...) không thể tránh khỏi kỷ luật OOP của tổ chức . Bạn chắc chắn có thể lập trình OOP mà không cần hỗ trợ ngôn ngữ (xem ví dụ: nhân Linux).
vonbrand

1

Hãy cẩn thận. Đọc tác phẩm kinh điển của R. King "Con mèo của tôi hướng đối tượng" trong "Khái niệm, cơ sở dữ liệu và ứng dụng hướng đối tượng" (Kim và Lochovsky, eds) (ACM, 1989). "Hướng đối tượng" đã trở thành một từ thông dụng hơn là một khái niệm rõ ràng.

Bên cạnh đó, có nhiều biến thể về chủ đề, ít có điểm chung. Có các ngôn ngữ dựa trên nguyên mẫu (kế thừa là từ các đối tượng, không có các lớp như vậy) và các ngôn ngữ dựa trên lớp. Có những ngôn ngữ cho phép nhiều kế thừa, những ngôn ngữ khác thì không. Một số ngôn ngữ có một ý tưởng như giao diện của Java (có thể được coi là một hình thức thừa kế nhiều kế thừa). Có ý tưởng về mixins. Kế thừa có thể khá nghiêm ngặt (như trong C ++, thực sự không thể thay đổi những gì bạn nhận được trong một lớp con) hoặc được xử lý rất tự do (trong Perl, lớp con có thể xác định lại hầu hết mọi thứ). Một số ngôn ngữ có một gốc duy nhất để kế thừa (thường được gọi là Object, với hành vi mặc định), một số ngôn ngữ khác cho phép lập trình viên tạo nhiều cây. Một số ngôn ngữ nhấn mạnh rằng "mọi thứ là một đối tượng", những ngôn ngữ khác xử lý các đối tượng và phi đối tượng, một số (như Java) có "hầu hết là các đối tượng, nhưng một số loại ở đây thì không". Một số ngôn ngữ nhấn mạnh vào việc đóng gói trạng thái nghiêm ngặt trong các đối tượng, những ngôn ngữ khác làm cho nó trở thành tùy chọn (riêng tư, được bảo vệ, công khai), những ngôn ngữ khác hoàn toàn không có sự đóng gói. Nếu bạn nheo mắt với một ngôn ngữ như Scheme từ góc phải, bạn sẽ thấy OOP được tích hợp mà không cần bất kỳ nỗ lực đặc biệt nào (có thể xác định các hàm trả về các hàm đóng gói một số trạng thái cục bộ).


0

Để được ngắn gọn Lập trình hướng đối tượng giải quyết các vấn đề bảo mật dữ liệu có trong lập trình thủ tục. Điều này được thực hiện bằng cách sử dụng khái niệm đóng gói dữ liệu, chỉ cho phép các lớp hợp pháp kế thừa dữ liệu. Sửa đổi truy cập tạo điều kiện đạt được mục tiêu này. Mong rằng sẽ giúp. :)


Các vấn đề bảo mật dữ liệu hiện diện trong lập trình thủ tục là gì?
dùng31782

Trong lập trình thủ tục, người ta không thể hạn chế việc sử dụng biến toàn cục. Bất kỳ chức năng có thể sử dụng giá trị của nó. Tuy nhiên, trong OOP tôi có thể giới hạn việc sử dụng một biến cho một lớp nhất định hoặc chỉ các lớp kế thừa nó có lẽ.
manu

Trong lập trình thủ tục cũng vậy, chúng ta có thể hạn chế việc sử dụng biến toàn cục bằng cách sử dụng biến cho các hàm nhất định, nghĩa là không khai báo bất kỳ dữ liệu toàn cầu nào.
dùng31782

Nếu bạn không khai báo nó trên toàn cầu thì đó không phải là biến toàn cục.
manu

1
"Bảo mật" hoặc "Chính xác" không có nghĩa gì nếu không có thông số kỹ thuật. Những điều này là cố gắng đưa một đặc tả về mã hướng tới mục tiêu đó: Các loại, Định nghĩa lớp, DesignByContract, v.v. Bạn nhận được "Bảo mật" theo nghĩa là bạn có thể làm cho ranh giới dữ liệu riêng tư trở nên bất khả xâm phạm; giả sử rằng bạn phải tuân theo tập lệnh máy ảo để thực thi. Định hướng đối tượng sẽ không ẩn bộ nhớ trong với ai đó có thể đọc bộ nhớ trực tiếp và thiết kế giao thức đối tượng xấu đang đưa ra các bí mật theo thiết kế.
Rob
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.