Bài 14: Thuộc tính và phương thức tĩnh trong lập trình hướng đối tượng ph - Học lập trình PHP nâng cao
Như ta biết mỗi đối tượng luôn có các thuộc tính, phương thức và muốn sử dụng được nó ta phải khởi tạo đối tượng đó gán vào một biến sau đó gọi đến. Nhưng giả sử chúng ta tạo hai hai biến cùng một đối tượng đó thì các thao tác xử lý trên hai biến đó (thực chất là 2 object) không ảnh hưởng lẫn nhau. ...
Như ta biết mỗi đối tượng luôn có các thuộc tính, phương thức và muốn sử dụng được nó ta phải khởi tạo đối tượng đó gán vào một biến sau đó gọi đến. Nhưng giả sử chúng ta tạo hai hai biến cùng một đối tượng đó thì các thao tác xử lý trên hai biến đó (thực chất là 2 object) không ảnh hưởng lẫn nhau. Và nếu bạn đã đọc qua bài hàm và khai báo hàm trong php thì sẽ có khái niệm biến cục bộ và biến toàn cục, với ví dụ trên thì ta có thể hiểu mỗi thao tác chỉ ảnh hưởng trên chính biến đó chứ không ảnh hưởng qua các biến khác nên có thể gọi là cục bộ. Nhưng trong thực tế đôi lúc ta muốn bất kỳ thao tác nào đều được lưu lại trên đối tượng đó (dạng toàn cục) thì kiểu dữ liệu tĩnh chính là mấu chốt của vấn đề này.
1. Khái niệm dữ liệu tĩnh
Trong lập trình hướng đối tượng ta có thể hiểu dữ liệu tĩnh là loại dữ liệu được sử dụng ở dạng toàn cục, dù nó được xử lý ở bất kỳ file nào trong cùng một chương trình đều được lưu lại trong lớp, ta có thể gọi chúng là thành viên tĩnh. Mỗi thành viên đều có các mức truy cập private, public và protected bình thường.
Ví dụ 1: chương trình ở phần mở đầu.
// Lớp động vật class Animal { protected $_name = 'Chưa có tên'; function setName($name){ $this->_name = $name; } function getName(){ return $this->_name; } } // Phần 1: Con Vịt $con_vit = new Animal(); $con_vit->setName('Con Vịt'); echo $con_vit->getName(); // Kết quả: Con Vịt // Phần 2: Con Heo $con_heo = new Animal(); echo $con_heo->getName(); // Kết quả: Chưa có tên
Ta thấy ở Phần 1 tôi đã tạo object $con_vit
và thiết lập tên cho nó là 'Con Vịt'. Tiếp theo tôi tạo một object $con_heo
và tôi không có thiết lập tên cho nó, khi xuất tên ra màn hình thì Phần 1 xuất hiện chữ 'Con Vịt' còn Phần 2 thì xuất hiện chữ 'Chưa có tên'. Như vậy rõ ràng các thao tác trên biến $con_vit
không ảnh hưởng qua biến $con_heo
.
Ví dụ 2: chương trình dùng dạng thành viên tĩnh
// Lớp động vật class Animal { protected static $_name = 'Chưa có tên'; public static function setName($name){ Animal::$_name = $name; } public static function getName(){ return Animal::$_name; } } // Phần 1: Con Vịt $con_vit = new Animal(); $con_vit->setName('Con Vịt'); echo $con_vit->getName(); // Kết quả: Con Vịt // Phần 2: Con Heo $con_heo = new Animal(); echo $con_heo->getName(); // Kết quả: Con Vịt
Trong ví dụ này khác ở ví dụ 1 là các phương thức và thuộc tính tôi khai báo dạng tĩnh (có từ khóa static) và kết quả hoàn toàn khác với ví dụ 1. Ở cả Phần 1 và Phần 2 đều xuất ra màn hình là 'Con Vịt', lý do là tôi sử dụng dạng tĩnh và khi có thao tác thay đổi dữ liệu thì nó đều lưu vào trong class Animal nên khi khởi tạo thêm biến nó đều bị ảnh hưởng theo.
Qua hai ví dụ này có lẽ các bạn cũng đã hình dung được phần nào rồi, ta sẽ tiếp tục tìm hiểu sâu hơn nữa nhé.
3. Các vấn đề thông dụng khi sử dụng thành viên tĩnh
Như đã trình bày ở trên các thành viên tĩnh đều có các mực truy cập bình thường. Và để khai báo các mức truy cập thì ta sử dụng cú pháp sau:
Cú pháp:
- Thuộc tính:
[private|public|protected] static $name
, ví dụ public static $name - Phương thức:
[private|public|protected] static function functionname(){}
, ví dụ public static function setName()
Bây giờ ta sẽ thảo luận đến một số vấn đề khi sử dụng thành viên tĩnh.
Truy xuất trực tiếp không cần khởi tạo object
// Lớp động vật class Animal { protected static $_name = 'Chưa có tên'; public static function setName($name){ Animal::$_name = $name; } public static function getName(){ return Animal::$_name; } } Animal::setName('Con Vịt'); echo Animal::getName(); // Kết quả: Con Vịt
Gọi các hàm tĩnh trong nội bộ của class
// Lớp động vật class Animal { protected static $_name = 'Chưa có tên'; public static function setName($name){ Animal::$_name = $name; } public static function getName(){ return Animal::$_name; } public static function all($name){ Animal::setName($name); echo Animal::getName(); } } Animal::all('Con Vịt'); // Kết quả: Con Vịt
Các bạn để ý trong ví dụ này tôi đã gọi các thành viên tĩnh nội bộ lẫn nhau trong chính đối tượng đó thông qua cú pháp hai dấu chấm (::). Tương tự khi sử dụng ở ngoài lớp ta vẫn dùng hai dấu chấm thay vì dấu mũi tên. Có một lưu ý rằng khi gọi đến thuộc tính tĩnh thì thông thường ta dùng $this->name
, nhưng trong thành viên tĩnh thì khác ta dùng thêm dấu $ nữa, ví dụ Anmal::$name
Không sử dụng từ khóa $this
Vì các thuộc tính và phương thức tĩnh ở dạng toàn cục, được gọi mà không cần khởi tạo nên nếu bạn dùng từ khóa $this
để gọi đến một hàm nào đó trong chính lớp đó thì sẽ bị báo sai. Từ đây ta rút ra kết luận trong phương thức tĩnh chỉ gọi được những thuộc tính và phương thức cùng class ở dạng tĩnh. Ngoài cách gọi trực tiếp tên class ta có thể dùng từ khóa self để thay thế, ví dụ self::$_name
// Lớp động vật class Animal { protected $_age = ''; protected static $_name = 'Chưa có tên'; public static function setInfo($name, $age) { // Đúng Animal::$_name = $name; // Sai vì $this không tồn tại $this->_name = $name; // Sai vì $this không tồn tại $this->_age = $age; // Sai vì thuộc tính $_age không phải là tĩnh Animal::$_age = $age; } }
Kế thừa khi sử dụng static
Về tính kế thừa thì nó hoàn toàn bình thường không có gì đặc biệt, chỉ khác một điều là dùng hai dấu chấm (::) để truy xuất đến hàm tĩnh của lớp cha.
// Lớp động vật class Animal { protected static $_name = ''; public static function setName($name) { Animal::$_name = $name; } public static function getName(){ return Animal::$_name; } } class ConHeo extends Animal { public static function setName($name) { parent::setName($name); } } ConHeo::setName('Con Heo'); echo ConHeo::getName(); // Kết quả: COn Heo
3. Lợi hại khi sử dụng thành viên tĩnh static
Việc sử dụng các hàm và thuộc tính ở dạng tĩnh có ưu điểm là ta có thể thay đổi dữ liệu toàn cục cho đối tượng đó, không cần khởi tạo đối tượng mới vẫn sử dụng được. Tuy nhiên nó có khuyết điểm là nếu ta khai báo tĩnh thì chương trình sẽ xử lý lưu trữ toàn cục nên sẽ tốn bộ nhớ hơn.
4. Lời kết
Trong thực tế các thuộc tính phương thức tĩnh được sử dụng rất nhiều trong lập trình hướng đối tượng, nó là một phần không thể thiếu trong OOP nên chắc chắn ban phải nắm vững rồi :D