01/10/2018, 15:34

Bài 18: PHP –  Lập Trình Hướng Đối Tượng

Chúng ta có thể tưởng tượng vũ trụ của chúng ta được tạo từ nhiều đối tượng như mặt trời, trái đất, mặt trăng … Cũng giống như chúng ta có thể tưởng tượng chiếc xe hơi của chúng ta được tạo từ các đối tượng khác nhau như bánh xe, vô lăn, hộp số v.v… Cùng cách như vậy có các khái ...

Chúng ta có thể tưởng tượng vũ trụ của chúng ta được tạo từ nhiều đối tượng  như mặt trời, trái đất, mặt trăng … Cũng giống như chúng ta có thể tưởng tượng chiếc xe hơi của chúng ta được tạo từ các đối tượng khác nhau như bánh xe, vô lăn, hộp số v.v… Cùng cách như vậy có các khái niệm về chương trình hướng đối tượng mà giả định mọi thứ như một đối tượng và thực hiện một phần mềm sử dụng các đối tượng khác nhau.

Các Khái Niệm Hướng Đối Tượng

Trước khi chúng ta đi vào chi tiết, hãy xem qua định nghĩa các thuật ngữ quan trọng liên quan đến đối tượng

  • Class: đây là kiểu dữ liệu được định nghĩa bởi programmer (programmer khác với developer nha các bạn, về lý thuyết programmer là các đại ca tạo ra ngôn ngữ lập trình, hoặc là những người viết ra core, nói chung cao cấp hơn dev, dev thì sử dụng cái programmer tạo ra để phát triển, ứng dụng tao ra sản phẩm), mà bao gồm các hàm cũng như dữ liệu cục bộ. Bạn có thể nghĩ một class như một template cho khởi tạo nhiều đối tượng tương tự nhau. Như ta tạo ra class bánh xe, thì nếu tôi sản xuất xe đạp, xe máy(người việt mình hay quen gọi xe Honda), xe hơi, thì tôi vẩn có thể gọi class này để tạo ra đối tượng bánh xe, mặc dù tôi chỉ sản xuất xe đạp, còn bạn sản xuất xe hơi, nhưng đối tượng bánh xe là tương tự nhau nên sài chung class bánh xe được.
  • Object: Một khởi tạo cấu trúc dữ liệu cá nhân được định nghĩa bởi một class. Một khi bạn định nghĩa một class và sau đó tạo nhiều đối tượng thuộc về class. Các Object cũng được biết như instance (thể hiện của class).
  • Member Variable: Có các biến định nghĩa bên trong một class. Dữ liệu này sẽ bị ẩn bên ngoài của class và có thể truy cập bằng các hàm thành viên(member function) của class. Các biến này được gọi là thuộc tính của đối tượng một khi một đối tượng được tạo.
  • Member function: Các hàm được định nghĩa bên trong một class và được sử dụng để truy cập dữ liệu đối tượng.
  • Inheritance: Khi một class được định nghĩa bằng việc kế thừa từ class cha(parent class) thì nó được gọi là inheritance. Ở đây class con sẽ kế thừa tất cả hoặc một vài hàm của class cha và các biến của class cha.
  • Parent class: Một class mà được kế thừa từ một class khác. Đây được gọi là class cơ sở (base class) hoặc super class.
  • Child class: Một class mà kế thừa từ class khác. Đây cũng được gọi là subclass hoặc derived class (lớp dẩn xuất).
  • Polymorphism: Đây là khái niệm một đối tượng nơi mà cùng một hàm có thể được sử dụng cho các mục đích khác nhau. Lấy ví dụ tên của hàm sẽ vẩn giống nhau nhưng số đối số khác nhau và có thể làm các nhiệm vụ khác nhau. Dân giang hồ hay gọi là “đa hình”, thuật ngữ chuyên ngành.
  • Overloading: Một kiểu polymorphism trong một số hoặc tất cả các toán tử có các thực thi khác nhau phù thuộc vào kiểu của đối số của chúng. Các hàm giống nhau cũng có thể được nạp vào và thực hiện khách nhau. Nếu pholymorphism cùng tên hàm nhưng khác về số lượng đối số thì sẽ thực hiện khác nhau nhiệm vụ, ở Overloading thì có thể không cần khác số lượng đối số, nhưng đối số truyền vào là đối tượng khác thì sẽ làm nhiệm vụ khác. Dân trong nghề gọi nó là “nạp chồng”.
  • Data Abstraction: Bất kỳ đại diện dữ liệu mà chi tiết thực thi được ẩn. Hay gọi là dữ liệu trừu tượng. Giống như một dàn ý mà bạn đem ra để dựa vào thuyết trình, tuy nhiên dữ liệu này chỉ bạn biết mà không có nói ra bên ngoài, cái người khác nghe được là bài thuyết trình đầy đủ của bạn, chứ không phải các mục gợi ý. Nghĩa tiếng anh cố dịch sang tiếng việt là “trừu tượng hóa dữ liệu”.
  • Encapsulation: đề cập đến một khái niệm nơi mà chúng ta đóng gói tất cả dữ liệu và các hàm thành viên lại với nhau để tạo một đối tượng.
  • Constructor: liên quan đến một kiểu đặc biệt của hàm mà sẽ được gọi tự động bất cứ khi nào có một đối tượng được hình thành từ một class.
  • Destructor: liên quan đến một loại đặc biệt mà sẽ được gọi tự động bất kỳ lúc nào một đối tượng bị xóa hoặc xa tầm tay, xa phạm vi hoạt động.

Định Nghĩa Class PHP

<?php

    class phpClass {

        var $var1;

        var $var2 = “constant string”;

        function myfunc ($arg1, $arg2) {

            //do something

        }

        //something do

    }

?>

Mô tả:

  • Khai báo class bằng từ khóa class và theo sau là tên của class mà bạn muốn đặt.
  • Các biến, hàm bên trong class đều nằm trong cặp ngoặc {}
  • Khai báo biến bắt đầu với var, và theo sau ký hiệu $. Và có thể khởi tạo giá trị hằng số cho biến. Với việc khai báo biến sử dụng keyword var thì trong php 4, còn php 5 trở lên ta dùng public, protected, private. Cho nên nếu các bạn có đọc tài liệu hoặc các source code mà họ viết sử dụng var thì cũng hiểu lý do. Tuy nhiên bây giờ, và ngay lúc các bạn đang học serial này thì php đã đi khá xa rồi, đã có PHP 7 rồi, do vậy ta sẽ sử dụng cách khai báo biến bằng public, protected và private, và cũng để dể diễn giải trong chương học hướng đối tượng.

Ví dụ tạo một class sách:

<?php

   class Books {

      /* Member variables */

      public $price;

      public $title;

      /* Member functions */

      function setPrice($par){

         $this->price = $par;

      }

      function getPrice(){

         echo $this->price .”<br/>”;

      }

      function setTitle($par){

         $this->title = $par;

      }

      function getTitle(){

         echo $this->title .” <br/>”;

      }

   }

?>

Biến $this là một biến đặc biệt và nó sẽ tham chiếu tới cùng đối tượng, còn hiểu là chính nó.

Tạo Đối Tượng Trong PHP

Một khi bạn đã định nghĩa class của bạn, thì bạn có thể tạo nhiều đối  tượng bạn thích. Như ta đã định nghĩa class Books, bây giờ ta sẽ tạo ra hàng loạt đối tượng cho nó như sau:

$physics = new Books; //sách vật lý

$maths = new Books;

$chemistry = new Books;

Ở đây chúng ta đã tạo 3 đối tượng và các đối tượng này độc lập với nhau và chúng sẽ tồn tại riêng biệt. Kế tiếp chúng ta sẽ xem cách để truy cập hàm bên trong class và các biến thành viên như thế nào.

Gọi Các Hàm Bên Trong Class

Sau khi việc tạo đối tượng, bạn sẽ có thể gọi các hàm thành viên liên qua đến đối tượng đó. Một hàm thành viên sẽ có thể xử lý biến thành viên chỉ liên quan đến đối tượng.

Ví dụ chúng ta set title và price cho 3 quyển sách bằng cách gọi các hàm:

$physics->setTitle( “Vat Ly Lop 12” );

$chemistry->setTitle( “Hoa Hoc Nang Cao” );

$maths->setTitle( “Tich Phan 12” );

………………………………………………………………………………..

$physics->setPrice( 10 );

$chemistry->setPrice( 15 );

$maths->setPrice( 7 );

Bây giờ bạn gọi các hàm thành viên khác để lấy giá trị được set ở trên

$physics->getTitle();

$chemistry->getTitle();

$maths->getTitle();

…………………………………………………………………………….

$physics->getPrice();

$chemistry->getPrice();

$maths->getPrice();

Kết quả đây:

Vat Ly Lop 12

Hoa Hoc Nang Cao

Tich Phan 12

10

15

7

Hàm Contructor

Contructor là một kiểu hàm đặc biệt mà được gọi tự động bất kỳ nơi nào đối tượng được tạo. Vì thế chúng ta tận dụng lợi thế của nó mà khởi tạo nhiều thứ trong hàm khởi tạo.

PHP cung cấp một hàm đặc biệt __construct() để định nghĩa một constructor. Bạn có thể đưa vào nhiều đối số bạn thích trong hàm constructor.

Ví dụ tạo một hàm constructor cho class Books và nó sẽ khởi tạo price và title cho sách ở thời điểm việc tạo đối tượng.

function __construct($par1, $par2) {

    $this->title = $par1;

    $this->price = $par2 ;

}

Bây giờ chúng ta không cần gọi hàm set để đặt giá và title. Chúng ta có thể khởi tạo 2 biến thành viên này ở thời điểm tạo đối tượng.

EX :

$physics = new Books(  ‘Vat Ly  Lop 12’, 10) ;

$maths = new Books( ‘Hoa Hoc Nang Cao’, 15);

$chemistry = new Books( ‘Tich Phan’, 7);

/* Lay cac gia tri */

$physics->getTitle();

$chemistry->getTitle();

$maths->getTitle();

…………………………………………………………..

$physics->getPrice();

$chemistry->getPrice();

$maths->getPrice();

Kết quả:

Vat Ly Lop 12

Hoa Hoc Nang Cao

Tich Phan 12

10

15

7

Destructor

Giống như hàm constructor, bạn có thể định nghĩa một hàm destructor bằng cách sử dụng __destructor(). Bạn có thể giải phóng tất cả resource với hàm destructor. Một số ngôn ngữ có thể tự giải phóng các đối tượng không cần thiết, ở một khoảng thời gian không sử dụng đến hoặc theo một cơ chế được định sẳn bên trong nó.

Inheritance

Class trong PHP định nghĩa có thể kế thừa từ class cha bằng việc sử dùng từ khóa extends.

Cú pháp như sau:

class Child extends Parent {

      //định nghĩa thân class Child

}

Hiệu quả của việc kế thừa là lớp con có các đặc điểm sau:

  • Tự động có tất cả các biến thành viên được khai báo trong lớp cha
  • Tự động có tất cả các hàm thành viên ở lớp cha, mà sẽ làm việc cùng cách mà hàm đó làm ở lớp cha.

EX:

class Novel extends Books {

   public $publisher;

      function setPublisher($par){

      $this->publisher = $par;  

    }      

    function getPublisher(){

      echo $this->publisher. “<br />”;  

    }

}

Bây giờ thì lớp Novel vừa có các hàm kế thừa và định nghĩa thêm 2 hàm thành viên của riêng nó.

Overriding

Đó chúng ta định nghĩa hàm trong lớp con trùng tên với hàm trong lớp cha. Múc đích để thay đổi theo yêu cầu của lớp con.

Trong ví dụ dưới đây chúng ta sẽ override 2 hàm getPrice và getTitle trả về một số giá trị

function getPrice() {

   echo $this->price . “<br/>”;

   return $this->price;

}  

function getTitle(){

   echo $this->title . “<br/>”;

   return $this->title;

}

Public Members

Trừ khi bạn chỉ định cái khác, các thuộc tính và phương thức của một class mặc định sẽ là public. Điều đó nói rằng, chúng có thể được truy cập trong 3 tinh trạng sau:

  • Từ bên ngoài class nơi mà nó được khai báo
  • Từ bên trong class
  • Từ bên trong class khác mà kế thừa class nơi nó được khai báo

Trong các ví dụ ở trên tất cả member gồm biến và hàm đều là public. Nếu bạn muốn giới hạn truy cập thành viên của một class thì bạn định nghĩa thành viên trong class là private hoặc protected.

Private Members

Bằng việc thiết kế thành viên là private, bạn giới hạn nó truy cập tới class mà nó được khai báo. Thành phần private không thể truy cập từ class kế thừa và cũng không thể truy cập bên ngoài class nơi nó được khai báo.

Một thành viên của class được tạo là private bằng việc sử dụng từ khóa private phía trước tên thành viên.

Ví dụ:

class MyClass {

   private $car = “skoda”;

   $driver = “SRK”;

   function __construct($par) {

      // Statements here run every time

      // an instance of the class

      // is created.

   }

   function myPublicFunction() {

      return(“I’m visible!”);

   }

   private function myPrivateFunction() {

      return(“I’m  not visible outside!”);

   }

}

Khi class MyClass được kế thừa từ một class khác bằng keyword extends, thì myPublicFuntion() sẽ cho sử dụng được từ class con, cũng như biến $driver. Class kế thừa sẽ không có khả năng truy cập myPrivateFunction và biến $car, bởi vì chúng được khai báo private.

Protected Members

Một thuộc tính hay phương thức là protected sẽ được truy cập bên trong class mà nó được khai báo, cũng như các class kế thừa class đó. Các thành viên proctected không có giá trị bên ngoài của 2 loại class đó.

Một thành viên class được tạo là protected bằng cách sử dụng từ khóa protected ở trước tên của thành viên đó.

class MyClass {

   protected $car = “skoda”;

   $driver = “SRK”;

    function __construct($par) {

      // Statements here run every time

      // an instance of the class

      // is created.

   }      

    function myPublicFunction() {

         return(“I’m visible!”);  

    }      

    protected function myPrivateFunction() {

         return(“I’m  visible in child class!”);  

   }

}

Interfaces

Interfaces được định nghĩa để cung cấp một số tên hàm tổng quát để thức thi. Bên trong interface là các phương thức không có thân, chúng chỉ định nghĩa tên phương thức và phạm vi của phương thức đó. Khi một class kế thừa interface thì phải thực hiện các phương thức đã định nghĩa trong interface kế thừa đó. Có thể nói interface như là một xương sống mà được thực hiện bởi developers.  Tất cả phương thức trong interface là public.

interface Mail {

    public function sendMail();

}

Sau đó , nếu một class khác kế thừa interface này

class Report implements Mail() {

            //sendMail() phải được thực thi ở đây, tức là phải viết cho cái hàm này hoàn chỉnh

}

Constants

Một hằng số là có một cái gì đó giống như một biến, trong đó nó giữ một giá trị, nhưng thực sự nó giống nhiều hơn là một hàm bởi vì một hằng số là bất biến. Một khi bạn khai báo một hằng số thì nó không thể thay đổi.

class MyClass {

   const requiredMargin = 1.7;

      function __construct($incomingValue) {

      // Statements here run every time

      // an instance of the class

      // is created.

   }

}

Trong class này requiredMargin là một constant. Nó được khai báo với từ khóa const, và không có bất kỳ trường hợp nào nó có thể thay đổi tới bất kỳ cái gì khác hơn là 1.7.

Lưu ý rằng tên hằng số không có $ như tên của biến.

Abstract Class

Một abstract class là một class không thể khởi tạo, chỉ được kế thừa. Bạn khai báo một abstract class với từ khóa abstract.

Khi kế thừa từ một abstract class, tất cả phương thức mà được đánh dấu là abstract trong class cha thì phải được định nghĩa trong class con. Và các phương này phải được định nghĩa cùng phạm vi hoặc thấp hơn. Ví dụ bạn đặt phước thức trong abstract class cha là protected, thì lớp con kế thừa, phương thức đó phải được định nghĩa là protected hoặc public, không thể lên cao hơn là private được.

abstract class MyAbstractClass {

   abstract function myAbstractFunction() {

   }

}

Lưu ý rằng hàm định nghĩa bên trong một abstract class phải có keyword abstract. Sẽ không đúng đắn nếu định nghĩa một hàm không phải là abstract bên trong một abstract class. Vì thực tế bên trong một abstract class ta có thể định nghĩa một function mà không abstract, lúc đó function này như là một function bình thường trong một class không phải abstract, nó cũng được định nghĩa và có body. Và class kế thừa thì không cần định nghĩa function không phải là abstract trong abstract class. Tuy nhiên chúng ta nên tránh, đã định nghĩa abstract class thì các phương thức phải abstract hết.

Static Keyword

Việc khai báo một thành phần hoặc phương thức là static là để tạo cho chúng khả năng truy cập bên ngoài mà không cần khởi tạo đối tượng của class. Một thành viên được khai báo là static không thể truy cập với một lớp đối tượng được khởi tạo.

<?php   class Foo {

      public static $my_static = ‘foo’;

            public function staticValue() {

         return self::$my_static;

      }

   }           

print Foo::$my_static . “ ”;

   $foo = new Foo();

   print $foo->staticValue() . “ ”;

?>

Như bạn thấy bên ngoài ta gọi Foo::$my_static là nó cho giá trị mà không cần khởi tạo đối tượng.

Dòng kế tiếp ta cố khởi tạo đối tượng new Foo();

Rồi sau đó truy cập function gián tiếp gọi $my_static , $foo->staticValue();

Giờ các bạn thử $foo->$my_static coi chuyện gì xãy ra

Bên trong class, để truy cập biến static ta dùng từ khóa self thay vì $this cho các biến khác như đã thấy ở các ví dụ trên. Và bên ngoài ta dùng :: để truy cập Foo::$my_static.

Final Keyword

PHP 5 giới thiệu keyword final, mà ngăn chặn lớp con overriding một phương thức bằng cách đưa từ khóa final vào đầu phương thức. Nếu class mà chính nó được khai báo là final thì nó cũng không thể được kế thừa.

<?php

   class BaseClass {

      public function test() {

         echo “BaseClass::test() called<br>”;

      }

      final public function moreTesting() {

         echo “BaseClass::moreTesting() called<br>”;

      }

   }

   class ChildClass extends BaseClass {

      public function moreTesting() {

         echo “ChildClass::moreTesting() called<br>”;

      }

   }

?>

Chạy sẽ gặp lổi ?

Gọi Constructor Parent

Thay vì viết toàn bộ một constructor mới cho class con, chúng ta sẽ gọi một constructor ở lớp cha (nếu có) và sau đó bất kỳ cái gì cần thiết thêm.

class Name {

   var $_firstName;

   var $_lastName;

      function Name($first_name, $last_name) {

      $this->_firstName = $first_name;

      $this->_lastName = $last_name;

      }      

    function toString() {

        return($this->_lastName .”, ” .$this->_firstName);

    }

}

class NameSub1 extends Name {

   var $_middleInitial;

      function NameSub1($first_name, $middle_initial, $last_name) {

          Name::Name($first_name, $last_name);

          $this->_middleInitial = $middle_initial;

   }      

    function toString() {

         return(Name::toString() . ” ” . $this->_middleInitial);  

    }

}

Trong ví dụ này, chúng ta có một class cha là Name, mà có 2 đối số constructor, và một class con (NameSub1), mà class con này có 3 đối số constructor. Constructor của hàm NameSub1 bằng cách gọi constructor của cha nó đang tồn tại, sử dụng cú pháp :: và truyền vào 2 đối số. Và sau đó cài đặt thêm 1 field.

Tương tự NameSub1 đã override hàm toString mà lớp cha nó đã định nghĩa, gọi sử dụng lại và thêm thành phần cần thiết.

Như ở trên bạn đã thấy hàm __construct() dùng để tự khởi tạo giá trị mổi khi đối tượng được khởi tạo, và đến đây các bạn lại thấy một hình thức khác để khởi tạo đối tượng tự động nữa đó là đặt tên phương thước trùng với tên class.

Đến đây là toàn bộ kiến thức của lập trình hướng đối tượng xem như gần đầy đủ nhất rồi.

Thêm một diễn giải các thuật ngữ sử dụng khi các bạn tiếp cận OOP. Các bạn có thấy khi thì tôi dùng thuộc tính, lúc thì biến, khi thì hàm và lúc thì phương thức. Nó như thế này các bạn: khi bạn khai báo biến trong class thì nó được gọi là properties (thuộc tính), còn hàm trong class thì gọi là phương thức (method) nên có lúc tôi gọi như thế thì cũng hiểu là một mà thôi. Class có thể chứa cả thuộc tính và phương thức và đóng lại thành một gói (package) được gọi là đối tượng Object. Có thể nói class là cái bản vẽ ngôi nhà, còn đối tượng là khi ngôi nhà đã xây xong. Ví dụ tôi có class house và khi muốn cái bản vẽ này thành ngôi nhà thực sự để sử dụng thì tôi phải làm cho nó thành đối tượng, tức là chúng ta khởi tạo $house_obj=new house(); và $house_obj chính là đối tượng của bản vẽ và đem đi sử dụng được. 

Bài hơi dài, các bạn cứ thong thả mà ngâm từ từ nhé. Và xem như các bạn đã có thể xây dựng cho mình một dự án to lớn bằng việc viết các chương trình hướng đối tượng.

0