[JavaScript] Bài 28 – Polymorphism in OOP

Oh.. đây là một đặc trưng được hỗ trợ bởi các ngôn ngữ lập trình với những cấp độ khác nhau. Đối với những ngôn ngữ lập trình kiểu tĩnh static-typing như C, Ada, Haskell, Java, và C# thì việc biểu thị logic Đa Hình Polymorphism trong code sẽ phụ thuộc khá nhiều vào các

Oh.. đây là một đặc trưng được hỗ trợ bởi các ngôn ngữ lập trình với những cấp độ khác nhau. Đối với những ngôn ngữ lập trình kiểu tĩnh static-typing như C, Ada, Haskell, Java, và C# thì việc biểu thị logic Đa Hình Polymorphism trong code sẽ phụ thuộc khá nhiều vào các công cụ mà mỗi ngôn ngữ cung cấp; Còn đối với những ngôn ngữ quản lý kiểu dữ liệu linh động dynamic-typing như JavaScript, Python, hay Ruby, thì về cơ bản là không hề có giới hạn để biểu hiện.

Chúng ta sẽ khởi đầu với định nghĩa được tổ hợp bởi Wikipedia

Sự Đa Hình Polymorphism trong lĩnh vực lập trình nói chung là việc sử dụng 1 tên định danh để biểu thị hay đại diện cho nhiều kiểu dữ liệu khác nhau. Khái niệm này được vay mượn từ lĩnh vực nghiên cứu sinh học – nơi mà người ta sử dụng một tên định danh để mô tả một nhóm các loài khác nhau.
_Wikipedia

Và cũng theo bản ghi này thì những cách thức để biểu thị sự đa hình trong môi trường lập trình có thể được chia thành 3 nhóm:

  • Sub-typing – khi 1 tên định danh được sử dụng để biểu thị một object bất kỳ trong nhiều class khác nhau có chung super class.
  • Ad-hoc – khi code đưa ra định nghĩa một giao diện lập trình chung cho một bộ ngẫu nhiên các kiểu giá trị.
  • Parametric – Khi code không chỉ định kiểu dữ liệu cụ thể và thay vào đó thì các ký hiệu trừu tượng sẽ được sử dụng để thay thế cho khả năng chấp nhận bất kỳ kiểu dữ liệu nào.

Sub-typing Polymorphism

Đây là khía cạnh logic biểu thị Polymorphism căn bản nhất và rất dễ để nhận biết sự tương đồng với định nghĩa được đặt trong khung trích dẫn ở phía trên. Ở đây chúng ta sẽ thực hiện một ví dụ đơn giản với các class có mặt trong đồ hình như sau:

Chúng ta có các class Shape có một phương thức show() được kế thừa bởi các class khác là Circle, Square, và Triangle. Và ở các class kế thừa thì phương thức show() đều được định nghĩa lại để có kết quả hoạt động phù hợp với từng class.

classMain{publicstaticvoidmain(String args[]){Shape form =null;

      form =newCircle();
      form.show ();
     
      form =newSquare();
      form.show ();

      form =newTriangle();
      form.show ();}}//. MainclassShape{publicvoid show (){System.out.println ("-- Shape - - - - - - - - - - -");}}//. ShapeclassCircleextendsShape{publicvoid show (){super.show ();System.out.println ("Type of Shape : Circle n");}}//. CircleclassSquareextendsShape{publicvoid show (){super.show ();System.out.println ("Type of Shape : Square n");}}//. SquareclassTriangleextendsShape{publicvoid show (){super.show ();System.out.println ("Type of Shape : Triangle n");}}//. Triangle

Lúc này ở chương trình main, chúng ta chỉ khai báo một biến form duy nhất được định kiểu là sẽ lưu trữ một object tương thích với class Shape. Ở đây chúng ta có thể hiểu là biến này sẽ chỉ cho phép lưu trữ một object được tạo ra trực tiếp từ class Shape bằng phép thực thi new Shape(), hoặc được tạo ra từ các class khác kế thừa class Shape.

Đây là điều kiện cần trong định nghĩa Polymorphism, đó là khi một tên định danh form có thể đại diện cho object của nhiều class khác nhau. Tuy nhiên, yếu tố còn lại, hay điều kiện đủ để hoàn thiện định nghĩa Sub-typing Polymorphism đó là tính tương thích về giao diện lập trình API được để mở ra code sử dụng bên ngoài của các class.

Để hiểu ngắn gọn, khi chúng ta gọi phương thức show() trong bất kỳ trường hợp nào, từ object được tạo ra từ bất kỳ class nào mà code vẫn có thể hoạt động tốt, thì chúng ta có thể nhận định cách sử dụng tên định danh form như trên là một cách biểu thị Sub-typing Polymorphism.

-- Shape - - - - - - - - - - -
Type of Shape : Circle 

-- Shape - - - - - - - - - - -
Type of Shape : Square 

-- Shape - - - - - - - - - - -
Type of Shape : Triangle

Nhân tiện thì trong các ngôn ngữ lập trình hỗ trợ cú pháp OOP kiểu như Java, C#, Ruby, Python, JS, v.v… thì thao tác khi chúng ta định nghĩa lại một yếu tố property hoặc methodclass kế thừa khi mà yếu tố đó đã được định nghĩa ở super class – được gọi là override.

Thao tác override luôn được nhắc đến gắn liền với định nghĩa Sub-typing Polymorphism, nhưng chúng ta cần lưu ý – đó là Sub-typing Polymorphism còn yêu cầu sự tương thích về giao diện lập trình cung cấp bởi các class. Vì vậy nên nếu như chúng ta thực hiện override các yếu tố và thay đổi các nhãn access modifier để ẩn khỏi bề mặt sử dụng đối với code bên ngoài thì đó không còn là Sub-typing Polymorphism nữa.

Việc biểu thị logic Sub-typing Polymorphism như trên trong JavaScript, hiển nhiên không có gì trở ngại và còn thuận lợi hơn nhiều do JS là một ngôn ngữ kiểu động dynamic-typing. Các yếu tố như sử dụng tên của super class để định kiểu cho biến đại diện chỉ có ý nghĩa trong môi trường của các ngôn ngữ định kiểu tĩnh static-typing như Java, C#, v.v…

classShape{show(){
      console.log("-- Shape - - - - - - - - - - -");}}//. ShapeclassCircleextendsShape{show(){super.show();
      console.log("Type of Shape : Circle n");}}//. CircleclassSquareextendsShape{show(){super.show();
      console.log("Type of Shape : Square n");}}//. SquareclassTriangleextendsShape{show(){super.show();
      console.log("Type of Shape : Triangle n");}}//. Triangle// - main - - - - - - - - - var form =null;

form =newCircle();
form.show();

form =newSquare();
form.show();

form =newTriangle();
form.show();

Ad-hoc Polymorphism

Ad-hoc Polymorphism – khi code đưa ra định nghĩa một giao diện lập trình chung cho một bộ ngẫu nhiên các kiểu giá trị.

Định nghĩa này thực ra xuất phát từ các ngôn ngữ chủ điểm ProceduralFunctional, nơi mà mỗi tên định danh của các sub-program có thể được nhìn nhận là một điểm biểu thị hình thái nhất định.

Ý tưởng ở đây là một sub-program có tên là sum có thể được hiểu như một tên đại diện cho các hình thái hoạt động hay mindset khác nhau. Chúng ta có thể sử dụng sum(a,b) để thu được tổng của hai giá trị đầu vào, hoặc có thể sử dụng sum(array) để thu được tổng của tất cả các giá trị trong mảng array, v.v…

Như vậy, tên định danh sum đã được sử dụng để biểu thị cho nhiều bộ logic mindset khác nhau và có thể được sử dụng với những yếu tố dữ liệu đầu vào input khác nhau; Và đó có thể được xem là một biểu thị Đa Hình Polymorphism.

Bây giờ chúng ta sẽ xem xét một ví dụ trong môi trường OOP. Ở đây chúng ta sẽ tạo ra một class có chứa một tên định danh của method được định nghĩa lại nhiều lần.

classMain{publicstaticvoidmain(String args[]){Utility intel =newUtility();int total = intel.add (1,2);System.out.println ("add (int, int) = "+ total);String message = intel.add("String","Concatenation");System.out.println ("add (String, String) = "+ message);}}//. MainclassUtility{publicint add (int a,int b){return a + b;}publicString add (String a,String b){return a +" "+ b;}}//. Shape
int add (int, int) = 3
String add (String, String) = String Concatenation

Trong môi trường của các ngôn ngữ định kiểu tĩnh stating-typing như Java, C#, v.v… mỗi một bộ định danh bao gồm tên của sub-program và các yếu tố định kiểu các giá trị đầu vào input/parameters và giá trị trả về output/return như mô tả trong kết quả ở console – được gọi là một chữ ký signature.

Các signature sẽ được các trình biên dịch của mỗi ngôn ngữ lưu trữ lại để phân biệt giữa các phiên bản định nghĩa khác nhau, và chọn được định nghĩa phù hợp ở vị trí mà sub-program đó được sử dụng. Thêm vào đó thì thao tác định nghĩa lại nhiều lần một sub-program như trên được gọi là overload.

Như vậy là chúng ta đã thấy intel sử dụng các bộ logic mindset khác nhau khi thực hiện thao tác add trên các đối tượng dữ liệu khác nhau: Khi add các số nguyên int thì đó là người làm toán thực hiện phép tính tổng; Còn khi add các chuỗi String thì đó là người làm văn thực hiện công việc gắn kết các câu chữ. Đó là những hình thái khác nhau được thể hiện bởi cùng một tên định danh add – là biểu hiện của Sự Đa Hình Polymorphism theo kiểu Ad-hoc.

Trong các ngôn ngữ kiểu động dynamic-typing như JavaScript, Python, Ruby, v.v… thì cách biểu thị này được thực hiện bằng cách người viết code sẽ phải tự tạo ra logic kiểm tra số lượng tham số đầu vào, kiểu dữ liệu của từng tham số, và thứ tự sắp xếp của các tham số được truyền vào sub-program, để tạo ra các logic rẽ nhánh tới các sub-program khác.

consttyped=(value,type)=>(typeof value)== type

constaddNumber=(a,b)=> a + b
constaddString=(a,b)=> a +' '+ b

constadd=(...parameters)=>{var[a,b]= parameters;if(typed(a,"number")&&typed(b,"number"))returnaddNumber(a,b)if(typed(a,"string")&&typed(b,"string"))returnaddString(a,b)if("any-other-case")thrownewError("Unsupported Signature")}// - main - - - - - - - - -var total =add(1,2)
console.log("add : (number,number) -> number = "+ total)var message =add("String","Concatenation")
console.log("add : (string,string) -> string = "+ message)

Đối với trường hợp overload thì việc sử dụng các ngôn ngữ định kiểu tĩnh static-typing có phần thuận lợi hơn nhiều vì thao tác kiểm tra kiểu dữ liệu của các tham số đầu vào đã được trình biên dịch hỗ trợ. Tuy nhiên thì như chúng ta cũng đã thấy, sự linh động của dynamic-typing luôn khiến mọi thứ đều trở thành có thể. 😄

Parametric Polymorphism

Parametric – Khi code không chỉ định kiểu dữ liệu cụ thể và thay vào đó thì các ký hiệu trừu tượng sẽ được sử dụng để thay thế cho khả năng chấp nhận bất kỳ kiểu dữ liệu nào.

Đọc lướt cái định nghĩa thì bạn thấy rõ rồi đấy, Parametric Polymorphism là một tính năng cần phải biểu thị bởi người viết code trong các ngôn ngữ định kiểu tĩnh static-typing như Java, C#, v.v… Còn đối với các ngôn ngữ kiểu động dynamic-typing như JavaScript, Python, Ruby, v.v… thì mặc định yếu tố đó đã là đặc trưng tự nhiên rồi. Vì vậy nên chúng ta không cần phải quan tâm tới đâu.

Trong trường hợp bạn muốn tìm hiểu ví dụ về Parametric Polymorphism trong các ngôn ngữ định kiểu tĩnh thì có thể hỏi Google với từ khóa Generic Programming. Còn ở đây thì chúng ta sẽ để dành sự quan tâm cho đặc tính tiếp theo Abstraction.

(chưa đăng tải) [JavaScript] Bài 29 – Abstraction in OOP

Nguồn: viblo.asia

Bài viết liên quan

WebP là gì? Hướng dẫn cách để chuyển hình ảnh jpg, png qua webp

WebP là gì? WebP là một định dạng ảnh hiện đại, được phát triển bởi Google

Điểm khác biệt giữa IPv4 và IPv6 là gì?

IPv4 và IPv6 là hai phiên bản của hệ thống địa chỉ Giao thức Internet (IP). IP l

Check nameservers của tên miền xem website trỏ đúng chưa

Tìm hiểu cách check nameservers của tên miền để xác định tên miền đó đang dùn

Mình đang dùng Google Domains để check tên miền hàng ngày

Từ khi thông báo dịch vụ Google Domains bỏ mác Beta, mình mới để ý và bắt đầ