thông điệp truyền qua trong OO là gì?


35

Tôi đã học lập trình OO, chủ yếu bằng C ++, C # và Java. Tôi nghĩ rằng tôi đã nắm bắt tốt về nó với sự hiểu biết của tôi về đóng gói, kế thừa và đa hình (cũng như đọc rất nhiều câu hỏi trên trang web này).

Một điều dường như xuất hiện ở đây và đó là khái niệm "thông điệp truyền qua". Rõ ràng, đây là thứ không được sử dụng trong khi lập trình OO bằng các ngôn ngữ chính hiện nay, nhưng được Smalltalk hỗ trợ.

Câu hỏi của tôi là:

  • Tin nhắn đi qua là gì? (Ai đó có thể đưa ra một ví dụ thực tế không?)
  • Có sự hỗ trợ nào cho "thông điệp truyền" này trong C ++, C # hoặc Java không?

4
Tôi đã trả lời câu hỏi này trên SO một lúc trước: stackoverflow.com/a/3104741/10259
Frank Shearar

1
Bạn đã đọc bài viết Wikipedia ?
yannis

4
Theo ý kiến ​​khiêm tốn của tôi, Objective-C sẽ đủ điều kiện trở thành ngôn ngữ chính. Ít nhất là nhiều như C #.
mouviciel

Tôi đồng ý với điều đó, tôi đã nêu các ngôn ngữ "chính thống" từ kinh nghiệm của mình
Tom

Một cuộc gọi chức năng thành viên là một trong việc thực hiện truyền tin nhắn. Thông báo được truyền đi được xác định bởi tên hàm và bao gồm thông tin từ các tham số. Ràng buộc muộn cho phép lớp nhận xử lý cùng một thông điệp theo cách khác với các lớp khác. Đó không phải là mục đích của những người tạo ra Simula, và nhiều người sẽ phản đối việc gọi nó là tin nhắn truyền đi và nói rõ (với lý do chính đáng) rằng việc truyền tin nhắn là một điều quan trọng khiến Simula khác biệt, nhưng các cuộc gọi chức năng thành viên vẫn thực hiện giống nhau việc làm.
Steve314

Câu trả lời:


60

Tin nhắn đi qua là gì? (Ai đó có thể đưa ra một ví dụ thực tế không?)

Truyền tin nhắn đơn giản có nghĩa là (ở mức rất trừu tượng) cơ chế cơ bản của thực thi chương trình là các đối tượng gửi tin nhắn cho nhau. Điểm quan trọng là tên và cấu trúc của các thông báo này không nhất thiết phải được cố định trước trong mã nguồn và bản thân nó có thể là thông tin bổ sung. Đây là một phần quan trọng của những gì Alan Kay ban đầu hình dung là "lập trình hướng đối tượng".

Có sự hỗ trợ nào cho "thông điệp truyền" này trong C ++, C # hoặc Java không?

Các ngôn ngữ này thực hiện một phiên bản giới hạn của tin nhắn thông qua các cuộc gọi phương thức. Hạn chế vì tập hợp các tin nhắn có thể được gửi bị giới hạn ở các phương thức được khai báo trong một lớp. Ưu điểm của phương pháp này là nó có thể được thực hiện rất hiệu quả và cho phép phân tích mã tĩnh rất chi tiết (dẫn đến tất cả các loại lợi ích hữu ích, như hoàn thành mã).

Ngược lại, các ngôn ngữ truyền thông điệp "thực" cũng thường có định nghĩa phương thức, như một cách thuận tiện để triển khai trình xử lý thông báo, nhưng cho phép các lớp thực hiện các trình xử lý thông báo linh hoạt hơn cho phép đối tượng nhận "cuộc gọi phương thức" với tên tùy ý (không cố định tại thời gian biên dịch).

Một ví dụ trong Groovy thể hiện sức mạnh của khái niệm này:

def xml = new MarkupBuilder(writer)
xml.records() {
  car(name:'HSV Maloo', make:'Holden', year:2006) {
    country('Australia')
    record(type:'speed', 'Production Pickup Truck with speed of 271kph')
  }
}

sẽ tạo ra XML này:

<records>
  <car name='HSV Maloo' make='Holden' year='2006'>
    <country>Australia</country>
    <record type='speed'>Production Pickup Truck with speed of 271kph</record>
  </car>
</records>

Lưu ý rằng records, car, countryrecordlà cú pháp các cuộc gọi phương pháp, nhưng không có phương pháp cái tên đó theo quy định tại MarkupBuilder. Thay vào đó, nó có một trình xử lý thông báo bắt kịp chấp nhận tất cả các thông báo và diễn giải các tên thông báo là tên của một phần tử XML, các tham số là các thuộc tính và đóng như các phần tử con.


+1 thẳng đến câu trả lời điểm. Được chấp nhận cho ví dụ mã. Cảm ơn sự giúp đỡ của bạn :)
Tom

Vì vậy, nó không thể được thực hiện đơn giản với một ngôn ngữ đơn giản sendMessage(property_name, Array of arguments)getMessage(property_name, Array of arguments)tĩnh?
Pacerier

1
@Pacerier: chắc chắn, nhưng đó là sự kết hợp những nhược điểm của cả hai phương pháp - bạn mất an toàn kiểu và vẫn có "sendMessage" làm ô nhiễm mã của bạn ở mọi nơi, vì vậy bạn không nhận được cú pháp tao nhã.
Michael Borgwardt

Sẽ đúng hơn nếu nói rằng, trong ví dụ Groovy, trình xử lý tin nhắn đang nhận một tin nhắn, thay vì một cuộc gọi phương thức? Ban đầu, bạn đặt dấu ngoặc kép quanh cụm từ "phương thức gọi", nhưng trong câu cuối cùng của bạn, bạn nói rằng nó "chấp nhận tất cả các phương thức ", thay vì "tin nhắn".
Adam Zerner

@AdamZerner: bạn nói đúng, tôi đã sửa nó.
Michael Borgwardt

28

Truyền tin nhắn là một cách khác nhau để xử lý nhu cầu trong mã OO cho một đối tượng để có được một đối tượng khác (hoặc có khả năng là chính nó) để làm một việc gì đó.

Trong hầu hết các ngôn ngữ hiện đại xuất phát từ phương pháp C ++, chúng tôi thực hiện điều đó bằng các cuộc gọi phương thức. Trong trường hợp này, đối tượng được gọi (thông qua định nghĩa lớp của nó) đưa ra một danh sách lớn về phương thức gọi nó là phương thức nào và sau đó bộ mã hóa của đối tượng gọi đơn giản chỉ cần viết lệnh gọi:

public void doSomething ( String input )
...
other_object.dosomething ( local )

Đối với các ngôn ngữ được nhập tĩnh, sau đó trình biên dịch có thể kiểm tra loại của sự vật được gọi và xác nhận rằng phương thức đã được khai báo. Đối với các ngôn ngữ gõ động thì điều đó được thực hiện trong thời gian chạy.

Nhưng về bản chất, điều xảy ra là một bó các biến được gửi đến một khối mã cụ thể.

Thông qua

Trong các ngôn ngữ truyền thông điệp (như Mục tiêu C) thay vì các phương thức có người nhận, nhưng nhìn chung cách tiếp cận xác định chúng và gọi chúng là giống nhau - sự khác biệt là cách xử lý của nó.

Trong một thông điệp được truyền bằng ngôn ngữ, trình biên dịch có thể kiểm tra xem máy thu mà bạn đã gọi có tồn tại hay không, nhưng tệ nhất là nó sẽ hiện ra một cảnh báo để nói rằng nó không chắc chắn rằng nó ở đó. Điều này là do tại thời điểm chạy, điều sẽ xảy ra là một khối mã trên đối tượng nhận sẽ được gọi chuyển qua cả bó biến và chữ ký của người nhận mà bạn muốn gọi. Khối mã đó sau đó tìm kiếm người nhận và gọi nó. Tuy nhiên, nếu người nhận không tồn tại thì mã sẽ chỉ trả về một giá trị mặc định.

Kết quả là một trong những điều kỳ lạ được tìm thấy khi chuyển từ C ++ / Java -> Mục tiêu C hiểu rằng bạn có thể "gọi một phương thức" trên một đối tượng không được khai báo theo kiểu thời gian biên dịch và thậm chí không tồn tại loại thời gian chạy ... và rằng cuộc gọi sẽ không dẫn đến ngoại lệ bị ném nhưng thực tế là kết quả được truyền lại.

Ưu điểm của phương pháp này là nó làm phẳng lớp hierachy và tránh hầu hết các nhu cầu cho giao diện / nhiều kiểu thừa kế / vịt. Nó cũng cho phép các đối tượng xác định hành vi mặc định khi được yêu cầu làm điều gì đó mà họ không có người nhận (thường là "nếu tôi không làm điều đó, hãy chuyển tiếp yêu cầu đến đối tượng khác này"). Nó cũng có thể đơn giản hóa việc liên kết đến các cuộc gọi lại (ví dụ: đối với các thành phần UI và các sự kiện được định thời gian) đặc biệt là các ngôn ngữ được nhập tĩnh như Java (để bạn có thể gọi nút gọi là "runTest" thay vì gọi phương thức "actionPerformed" trên lớp bên trong "RunTestButtonListener" thực hiện cuộc gọi cho bạn).

Tuy nhiên, có vẻ như phải trả giá cho nhu cầu kiểm tra bổ sung của nhà phát triển rằng cuộc gọi mà họ nghĩ rằng họ đang thực hiện đúng đối tượng với đúng loại và truyền đúng tham số theo đúng thứ tự, bởi vì trình biên dịch có thể không cảnh báo bạn và nó sẽ chạy hoàn toàn tốt trong thời gian chạy (chỉ cần trả về một phản hồi mặc định). Ngoài ra còn có một hiệu suất đạt được từ việc tìm kiếm thêm và thông qua.

Ngày nay, các ngôn ngữ được gõ động có thể mang lại rất nhiều lợi ích của thông điệp được truyền qua OO với ít vấn đề hơn.


1
Tôi thích câu trả lời này - giải thích sự khác biệt và ý nghĩa của chúng.
HappyCat

@Gavin, vậy nó có chính xác giống như trình xử lý phương thức động của PHP và Javascript không?
Pacerier

11

Kiến trúc truyền thông điệp chỉ đơn giản là các hệ thống trong đó mỗi thành phần độc lập với các thành phần khác, với một cơ chế chung để truyền dữ liệu giữa chúng. Bạn có thể coi các cuộc gọi phương thức như một hình thức truyền thông điệp, nhưng nó không thực tế để làm điều đó - nó gây nhầm lẫn vấn đề. Điều này là do nếu bạn có một lớp với các phương thức được xác định rõ và một số mã gọi các phương thức đó, toàn bộ điều phải được biên dịch lại với nhau, do đó ghép mã và đối tượng. bạn có thể thấy nó gần như thế nào (vì một thông điệp đang được truyền đi và trình biên dịch đang thực thi tính chính xác, nhưng nó làm mất đi tính linh hoạt của một hệ thống tách rời).

Kiến trúc chuyển thông điệp thường cho phép các đối tượng được thêm vào trong thời gian chạy và thường không cho phép các thông điệp được chuyển hướng đến một hoặc nhiều đối tượng. Vì vậy, tôi có thể có một số mã phát thông báo 'dữ liệu x được cập nhật' cho tất cả các đối tượng đã được tải vào hệ thống và mỗi đối tượng có thể thực hiện bất kỳ hành động nào họ muốn với thông tin đó.

Một ví dụ kỳ lạ là web. HTTP là một hệ thống chuyển tin nhắn - bạn chuyển một động từ lệnh và 'gói dữ liệu' cho một quy trình máy chủ. (ví dụ: NHẬN http: \ myserver \ url) Cả trình duyệt của bạn cũng như máy chủ web không quan tâm bất cứ điều gì về dữ liệu bạn gửi hoặc nơi bạn gửi nó đến. Máy chủ sẽ chuyển nó vào mã sẽ gói một 'gói' dữ liệu khác và gửi lại cho bạn. Không có thành phần nào trong hệ thống này biết bất cứ điều gì về những thứ khác hoạt động hoặc những gì chúng làm, chúng chỉ biết giao thức được sử dụng để liên lạc tin nhắn.


@gbjbannb, Cần một số giải thích mã giả ....
Pacerier
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.