Mình thường hiểu thế này,
Abstract class để miêu tả 1 khái niệm trừu tượng, mà từ đó , tất cả các khái niệm được mở rộng (trong php thường dùng từ khoá extents) sẽ kế thừa các properties (các biến được khai báo trong Abstract class), hay các phương thức chung.
Class dùng để diễn đạt một khái niệm cụ thể, rõ ràng hơn.
Interface thường dùng để diễn dải ta có thể làm được những việc gì, chú ý ở đây chỉ là diễn tả ta làm được gì thôi, chứ làm thế nào thì mỗi class phải tự implements riêng.
Một ví dụ cụ thể là
// Mình có 1 khái niệm khá trừu tượng là Loài chim
abstract class LoaiChim{
// Đã là loài chim thì phải có cánh, nên mình có thuộc tính là cánh
protected $canh;
// Thuộc loài chim thì phải có chân
protected $chân;
// Chi thì phải có mỏ
protected $mỏ;
}
// Bên cạnh đó mình có Interface thể hiện các hành động mà 1 class có thể làm được, chính vì vậy không thể khai báo properties, và viết function trong interface được.
// Khả năng bay
interface iBay{
public function bay();
}
// Khả năng đi bộ
interface iĐibộ{
public function đibộ();
}
// Khả năng bơi
interface iBơi{
public function bơi();
}
// Từ những khái niệm chung chung như trên ta tiến tới cụ thể hoá
// Mình sẽ có 1 class là ChimCánhCụt
class ChimCánhCụt extents LoàiChim implements iBơi, iĐiBộ{
public function bơi(){
$this->chân+=5; (quẫy chân đạp nước với 5 bước tiến 1 lần bơi)
}
public function đibộ{
$this->chân ++ ;( mỗi lần đi bộ đi được có 1 bước, chim cánh cụt chân ngắn nên đi bộ chậm mà)
}
}
// Mình sẽ có 1 class là Đàđiểu
class Đàđiểu extents LoàiChim implements iĐiBộ{
public function đibộ{
$this->chân +=5 ;( mỗi lần đi bộ đi được 5 bước, đà điều thì chạy rất nhanh)
}
}
// Mình có 1 class ChimBóiCá
class ChimBóiCá extents LoàiChim implements iĐiBộ, iBơi, iBay{
public function đibộ{
$this->chân +=1 ;
}
public function bơi(){
$this->chân+=3; (Chim bói cá biết bơi, tất nhiên bơi thì chậm hơn bay)
}
public function bay(){
$this->cánh+=6; (Chim bói cá biết bơi, tất nhiên bơi thì chậm hơn bay)
}
}
Với cách viết trên, ta sẽ thấy code của mình trở nên rõ ràng hơn rất nhiều, và đọc nhanh và dễ hơn, nhìn vào dòng lệnh
class ChimBóiCá extents LoàiChim implements iĐiBộ, iBơi, iBay
sẽ dễ dàng biết được class ChimBoiCa có khả năng ( method) đi bộ, bay, và bơi
class Đàđiểu extents LoàiChim implements iĐiBộ
Đà điểu thì chỉ có thể đi bộ được thôi, ít code thừa hơn. Và khi sử dụng ta có thể làm như sau
class CuocThiBoi{
$danh_sách_ứng_viên= array()
// chỉ chấp nhận các ứng viên nào biết bơi
public funcition thêmUngVien(IBoi $ứng_viên){
$this->danh_sách_ứng_viên[] = $ứng_viên;
}
public function thiđấu(){
foreach($this->danh_sach_ưng_viên as $ứng_viên){
$ứng_viên->bơi();
}
}
}
Khi ta xử dụng sẽ là
$cuoc_thi_bơi = new CuocThiBoi();
$cuọc_thi_boi->thêmUngVien(Chim bói cá);
$cuọc_thi_boi->thêmUngVien(Chim cánh cụt);
$cuọc_thi_boi->thêmUngVien(Đà điểu); // quăng exception ngay từ vòng gửi xe
Một số sai lầm phổ biến của các bạn khi mới code thường gặp phải như
abstract class LoaiChim{
// Đã là loài chim thì phải có cánh, nên mình có thuộc tính là cánh
protected $canh;
// Thuộc loài chim thì phải có chân
protected $chân;
// Chi thì phải có mỏ
protected $mỏ;
public function đibộ{
$this->chân +=1 ;
}
public function bơi(){
$this->chân+=1;
}
public function bay(){
$this->cánh+=1;
}
}
// Mình sẽ có 1 class là ChimCánhCụt
class ChimCánhCụt extents LoàiChim{
// không cần phải override lại vì dùng lại được
//public function đibộ
// không biết bay nhưng vẫn phải bay vì bay được 0 bước
public function bay(){
$this->cánh+=0;
}
public function bơi(){
$this->chân+=5; (quẫy chân đạp nước với 5 bước tiến 1 lần bơi)
}
}
// Mình sẽ có 1 class là Đàđiểu
class Đàđiểu extents LoàiChim implements iĐiBộ{
public function đibộ{
$this->chân +=5 ;( mỗi lần đi bộ đi được 5 bước, đà điều thì chạy rất nhanh)
}
// không biết bay nhưng vẫn phải bay vì bay được 0 bước
public function bay(){
$this->cánh+=0;
}
public function bơi(){
$this->chân+=0;
}
}
// Mình có 1 class ChimBóiCá
class ChimBóiCá extents LoàiChim implements iĐiBộ, iBơi, iBay{
// không cần ovrried lại
// public function đibộ
public function bơi(){
$this->chân+=3; (Chim bói cá biết bơi, tất nhiên bơi thì chậm hơn bay)
}
public function bay(){
$this->cánh+=6; (Chim bói cá biết bơi, tất nhiên bơi thì chậm hơn bay)
}
}
Cách viết này thường được các bạn viết, vì nó có 1 ưu điểm là mình sẽ implement được 1 số các method thường dùng để khi extends ra cụ thể thì không tốn công viết lại nữa, ở trong trường hợp này là method đi bộ được tiết kiệm 2 lần, không cần viết lại. Nhưng thật chất nó cho ta một cấu trúc không rõ ràng. Rõ ràng chim cánh cụt không biết bay nhưng vẫn phải có method bay, hay Đà điểu thì làm gì biết bơi với bay, những vẫn phải ôm đồm 2 việc này.
Và khi các bạn cần sử dụng thì càng khó khăn hơn. Giống như trên tôi muốn tổ chức 1 cuộc thi bơi, giữa muôn thú thì tôi phải làm sao
class CuocThiBoi{
$danh_sách_ứng_viên= array()
public funcition thêmUngVien($ứng_viên){
$this->danh_sách_ứng_viên[] = $ứng_viên;
}
public function thiđấu(){
foreach($this->danh_sach_ưng_viên as $ứng_viên){
$ứng_viên->bơi();
}
}
}
Khi ta xử dụng sẽ là
$cuoc_thi_bơi = new CuocThiBoi();
$cuọc_thi_boi->thêmUngVien(Chim bói cá);
$cuọc_thi_boi->thêmUngVien(Chim cánh cụt);
$cuọc_thi_boi->thêmUngVien(Đà điểu);
$cuộc_thi_bơi->thi đấu(); ====> đà điểu thiệt mạng
Bạn thấy rõ code rất mập mời