+1

Những dấu chân của nhân loại trên con đường đến với lập trình hướng đối tượng (phần cuối)

C-Programming.jpg


Những yếu tố làm nên lập trình hướng đối tượng


Và như vậy, ta có thể thấy những chức năng cần thiết cho việc hướng đối tượng là : - Kiểu dữ liệu trừu tượng : dữ liệu và xử lí liên quan đến nhau - Kiểu dữ liệu trừu tượng : dữ liệu có thể được ẩn đi - Đối tượng : bản thân dữ liệu tự hiểu được vai trò của mình - Đa trạng thái động : tự động tìm kiếm dữ liệu và xử lí của đối tượng - Thiết lập phạm vi tìm kiếm : kế thừa và ủy thác

Sự hướng đối tượng trên Smalltalk và Objective - C


Alan Kay - một trong những người sáng lập Smalltalk đã định nghĩa về sự “hướng đối tượng” là “sự thể hiện tất cả mọi thứ liên quan đến personal computing bằng đối tượng và sự truyền thông điệp giữa các đối tượng đó với nhau”. Quan điểm của ngôn ngữ C++ về khái niệm đó thì lại khác – điều này tôi nghĩ các bạn cũng hiểu.

Đối tượng – chiếc máy tính giả tưởng


Lối tư duy cơ bản trong thế giới quan của Alan Kay là nếu ta muốn trừu tượng hóa memory, CPU và các câu lệnh liên quan đến chúng một cách triệt để thì không còn gì đúng đắn hơn việc ta trừu tượng hóa chúng bằng một chiếc máy tính giả tưởng có mang mọi dữ liệu, xử lí, câu lệnh y hệt như vậy và ở dưới dạng một tập hợp.

Điều này giống với điều Djikstra đã nói trong thuyết Lập trình cấu trúc : trừu tượng hóa theo các tầng dưới dạng một máy tính giả tưởng.

2 con người đó tuy ở các bối cảnh khác nhau, nhưng điều họ nói là giống nhau.

Họ xem như “đối tượng” là một chiếc máy độc lập, cần truyền thông điệp cho chúng và ta khiến chúng tự chịu trách nhiệm đối với dữ liệu chúng mang theo.

Chính môi trường lập trình của Smalltalk cũng được tạo ra dưới dạng một chiếc máy giả tưởng.

Truyền thông điệp


Trong Smalltalk sự truyền thông điệp được thể hiện bằng ``receiver message``.

Trong Objective-C, cơ chế truyền thông điệp lại được kích hoạt bằng đoạn code có dạng như sau :

[receiver message]

[receiver methodName:args1 with:args]

Một cách khắt khe mà nói, truyền thông điệp và gọi hàm số là 2 việc khác nhau. Mặc dù trên Objective-C, về mặt implement mà nói thì chúng giống nhau.

Thông điệp là một dạng thông tin. Chúng ta có thể liên tưởng đến việc gửi mail - chỉ cần biết địa chỉ người nhận là gửi được và người nhận chỉ cần nhận mail và lí giải mail đó theo ý họ.

Tôi sẽ giới thiệu vài đặc tính liên quan đến việc truyền thông điệp.

Truyền tin động


Thông điệp cũng là đối tượng không hơn nên ta có thể tạo và gửi nó một cách động.

Chẳng hạn, Object#send trong Ruby là một ví dụ thể hiện điều đó.

class A
  def hello
    p "hello"
  end
end

a = A.new
//Tạo method một cách động
method = "he" + "ll" + "o"
///Gửi nó đi
a.send(method)

Trong các ngôn ngữ dạng LL (lightweight language), tính chất động như vậy là rất thông thường. Ngay cả trong Objective-C, ta cũng có thể gửi thông điệp động bằng cách sử dụng NSInvocation hoặc performSelector (đi kèm với selector type).

Chức năng cascade


Trong Smalltalk có một chức năng gọi là cascade, cho phép ta gộp nhiều thông điệp lại để gửi cùng một lúc. Chức năng này, ta có thể nói rằng chẳng qua chỉ là một dạng của truyền thông điệp.
//Gửi liên tục 5 thông điệp add
| collection |

collection := OrderedCollection
    new
    add: 0;
    add: 1;
    add: 2;
    add: 3;
    add: 4;
    yourself.

Đồng thời, nó cũng gần giống với Bulk Request của JSON RPC.

Chuyển tiếp thông điệp (forward message)


Sau khi nhận thông điệp, ta có thể tự do tùy ý xử lí nó cho dù không có method nào được khai báo đi chăng nữa. https://en.wikipedia.org/wiki/Forwarding_(object-oriented_programming)

Method_missing trong Ruby hay forwardInvocation trong Objective-C chính là những chức năng tương ứng với khái niệm này. Các ngôn ngữ theo kiểu động ngày nay đều được trang bị chức năng kiểu này, chẳng hạn như Perl với chức năng AUTOLOAD.

Trong Smalltalk, method doesNotUnderstand sẽ được gọi và sau đó bạn làm gì với nó thì tùy ý.

proxy.rb
class Proxy
  def method_missing(name, *args, &block)
    target.send(name, *args, &block)
  end

  def target
    @target ||= []
  end
end

Chẳng hạn, ta có thể định nghĩa một proxy class như đoạn code trên và cứ thế chuyển tiếp mọi thông điệp đến object target.

Đây cũng “chẳng qua chỉ là một dạng của truyền thông điệp”.

Truyền tin không đồng thời


Hầu như mọi ngôn ngữ đều nhận thông điệp một cách đồng thời nhưng vì mọi kiểu truyền thông điệp đều “chẳng qua chỉ là một dạng của truyền thông điệp” (tôi biết là hơi khó hiểu) nên chúng ta vốn không cần thiết phải đợi chúng đến một cách đồng thời.

object foo //Gọi đồng thời

future = object @foo //Gọi không đồng thời

Sự model hóa trao đổi giữa các đối tượng, lấy message parsing một cách không đồng thời làm trung tâm đem lại cho ta khái niệm actor model.

Dưới đây là ví dụ sử dụng actor trong Scala.

actor.scala
class HelloActor extends Actor {
    def receive = {
        case "Hello" => println("helloworld")
        case _ => println("errror")
    }
}

Bằng cách sử dụng một trong những kiểu message parsing như trên, ta có thể đưa thêm nhiều tính chất vào khái niệm hướng đối tượng.

Tuy nhiên, nếu người ta chỉ hiểu khái niệm “hướng đối tượng” theo hướng nó định nghĩa lại ngôn ngữ C++ bằng sự hướng đối tượng thì vai trò của message parsing sẽ trở nên rất mờ nhạt. Do đó, chính Alan Kay đã cho rằng cái tên “hướng đối tượng” mà ông nghĩ ra là không phù hợp.

https://www.infoq.com/news/2010/07/objects-smalltalk-erlang

Sau khi chúng ta bàn luận đến đây rồi, tôi nghĩ các bạn đã có thể hiểu nội dung bài viết trong link trên một cách dễ dàng hơn trước.

Nhất là đoạn sau :

"Tôi đã bắt đầu hoài nghi về thứ gọi là lập trình hướng đối tượng. Tôi luôn nghĩ Erlang là ngôn ngữ kiểu hàm số, không phải là kiểu hướng đối tượng. Thế nhưng, thầy hướng dẫn tôi làm luận văn đã nói rằng “Anh sai, Erlang là ngôn ngữ hướng đối tượng một cách triệt để”. Đồng thời ông cũng nói rằng các ngôn ngữ hướng đối tượng mà người ta vẫn thường nói thực ra không phải là hướng đối tượng. Tôi bán tín bán nghi, nhưng sự thật là sau đó dần dần tôi đã nghĩ rằng có lẽ Erlang là ngôn ngữ hướng đối tượng duy nhất trên thế giới. Lập trình hướng đối tượng có 3 chủ trương đó là : phân li các đối tượng, lấy truyền thông điệp làm nền tảng và mang cấu trúc đa hình."

Thêm vào đó, việc một chuyên gia về chính ngôn ngữ Erlang – Joe Armstrong viết bài luận với chủ đề “Object oriented programming sucks!” đã làm sự việc càng trở nên thú vị.

Tại sao sự hướng đối tượng là đồ vứt đi

Kết luận


Lập trình hướng đối tượng hay lập trình cấu trúc đều đặt ra vấn đề giống nhau là trừu tượng hóa. C++ đã sử dụng lại những tính chất tốt của Simula là module hóa, kiểu dữ liệu trừu tượng và đa trạng thái động.

Mặt khác, Smalltalk đã tổng hợp những ý tưởng của Simula thành 2 khái niệm : thông điệp và đối tượng.

Thông qua đó, nhiều tính chất động khác đã phát triển thành ngôn ngữ lập trình ngày nay. Ngoài ra, khái niệm message parsing có bản chất giống với những mẫu hình đồng thời như Actor hay CSP – đang rất được chú ý hiện nay, cũng là một điều khiến tôi thích thú.

Lời cuối


Tôi rất mong thông qua bài viết này, các bạn có thể hiểu được background của lập trình hướng đối tượng và từ đó viết được những đoạn code tốt hơn, cũng như tạo ra được những thiết kế hệ thống hợp lí hơn.

Bài viết của tôi không phải là tư tưởng chính thống của lập trình hướng đối tượng mà chỉ là tư tưởng tự giác ngộ.

Trong bài tôi có sử dụng những ví dụ thực tế, những chú giải liên quan đến kiến thức về kĩ thuật nên có lẽ không thực sự dành cho người mới làm quen với lập trình. Tuy nhiên, thông qua việc lí giải những điều đó, tôi hi vọng các bạn sẽ suy ra được bản chất của những chức năng khác nhau trong từng ngôn ngữ, thực ra đều có một mối quan hệ hữu hình với nhau.


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.