Giao diện hoặc một lớp trừu tượng: sử dụng cái nào?


327

Vui lòng giải thích khi nào tôi nên sử dụng PHP interfacevà khi nào tôi nên sử dụngabstract class ?

Làm thế nào tôi có thể thay đổi abstract classtrong một interface?

Câu trả lời:


458

Sử dụng giao diện khi bạn muốn buộc các nhà phát triển làm việc trong hệ thống của mình (bao gồm chính bạn) thực hiện một số phương thức đã đặt trên các lớp mà họ sẽ xây dựng.

Sử dụng một lớp trừu tượng khi bạn muốn buộc các nhà phát triển làm việc trong hệ thống của bạn (bao gồm chính bạn) thực hiện một số phương thức đã đặt bạn muốn cung cấp một số phương thức cơ bản sẽ giúp họ phát triển các lớp con của họ.

Một lưu ý khác là các lớp máy khách chỉ có thể mở rộng một lớp trừu tượng, trong khi chúng có thể thực hiện nhiều giao diện. Vì vậy, nếu bạn đang xác định hợp đồng hành vi của mình trong các lớp trừu tượng, điều đó có nghĩa là mỗi lớp con chỉ có thể tuân thủ một hợp đồng duy nhất. Đôi khi điều này là một điều tốt, khi bạn muốn buộc các lập trình viên của mình đi theo một con đường cụ thể. Lần khác nó sẽ là xấu. Hãy tưởng tượng nếu các giao diện Countable và Iterator của PHP là các lớp trừu tượng thay vì các giao diện.

Một cách tiếp cận phổ biến khi bạn không chắc chắn nên đi theo hướng nào (như được đề cập bởi cletus bên dưới ) là tạo một giao diện, và sau đó lớp trừu tượng của bạn thực hiện giao diện đó.


12
Tôi đã cố gắng cả ngày để hiểu các tập quán abstractinterfacecác lớp học, bài viết của bạn đã làm cho tất cả rõ ràng. Cảm ơn rất nhiều Alan
afarazit

4
Một ưu điểm khác của các lớp trừu tượng là khả năng định nghĩa các phương thức được bảo vệ trừu tượng . Không phải lúc nào cũng hữu ích, nhưng có thể có ích trong một số kiến ​​trúc.
netcoder

Vì vậy, trong nhiều trường hợp, chúng ta nên sử dụng lớp trừu tượng vì tính linh hoạt - đó là kết luận của tôi :)
ymakux

3
@volocuga: không nhất thiết, như Alan chỉ ra, chỉ một bản tóm tắt duy nhất có thể được mở rộng. Cá nhân tôi không thích bản tóm tắt thực hiện một ý tưởng giao diện vì nó góp phần vào việc mã hóa và ít trực tiếp hơn, IMO.
Tiền tố

171

Sự khác biệt giữa một Abstract Classvà một Interface:

Lớp học trừu tượng

Một lớp trừu tượng có thể cung cấp một số chức năngđể lại phần còn lại cho lớp dẫn xuất .

  • Lớp dẫn xuất có thể hoặc không thể ghi đè các hàm cụ thể được định nghĩa trong lớp cơ sở.

  • Một lớp con được mở rộng từ một lớp trừu tượng nên có liên quan về mặt logic.

Giao diện

Một giao diện không thể chứa bất kỳ chức năng . Nó chỉ chứa định nghĩa của các phương thức.

  • Lớp dẫn xuất PHẢI cung cấp mã cho tất cả các phương thức được định nghĩa trong giao diện .

  • Các lớp hoàn toàn khác nhau và không liên quan có thể được nhóm lại với nhau bằng cách sử dụng một giao diện.


1
Bạn có thể cung cấp một ví dụ thực tế để chứng minh điều đó?
RN Kushwaha

1
Sự khác biệt giữa abstract class X implements Yvà là class X implements Ygì?
Webinan

3
@Webinan Trong abstract class X implements Ybạn tuyên bố rằng chức năng hàng loạt của X nên được triển khai trong lớp dẫn xuất và cả lớp trừu tượng và lớp dẫn xuất phải chứa các hàm được định nghĩa trong Y, trong khi class X implements Ychỉ ngụ ý rằng lớp X phải chứa các hàm được định nghĩa trong Y. Nếu giao diện của bạn Y không được dự định thực hiện bởi bất kỳ lớp nào khác ngoài XI thực sự sẽ bỏ qua việc định nghĩa Y là giao diện và chỉ triển khai các hàm trong Y là hàm trừu tượng công khai / được bảo vệ / riêng tư để đảm bảo chúng được triển khai trong lớp dẫn xuất.
Calle Bergström

1
Các giao diện không chỉ có thể chứa định nghĩa về các phương thức, chúng còn có thể chứa các hằng số
Thielicy

Thích so sánh của bạn. Vì vậy, muốn thêm một cái gì đó. Các giao diện có thể có các hằng số lớp ngoài hộp trong khi lớp trừu tượng thì không thể.
Noman Ibrahim

128

Tại sao phải sử dụng các lớp trừu tượng? Sau đây là một ví dụ đơn giản. Hãy nói rằng chúng tôi có mã sau đây:

<?php 

class Fruit {
    private $color;

    public function eat() {
        // chew
    }

    public function setColor($c) {
        $this->color = $c;
    }
}

class Apple extends Fruit {
    public function eat() {
        // chew until core
    }
}

class Orange extends Fruit {
    public function eat() {
        // peeling
        // chew
    }
}

Bây giờ tôi cho bạn một quả táo và bạn ăn nó. Vị nó như thế nào? Nó có vị như một quả táo.

<?php 
$apple = new Apple();
$apple->eat();

// Now I give you a fruit.
$fruit = new Fruit();
$fruit->eat();

Hương vị đó như thế nào? Chà, nó không có ý nghĩa nhiều lắm, vì vậy bạn không nên làm điều đó. Điều này được thực hiện bằng cách làm cho lớp Fruit trở nên trừu tượng cũng như phương thức ăn bên trong nó.

<?php 
abstract class Fruit {
    private $color;

    abstract public function eat(){}

    public function setColor($c) {
        $this->color = $c;
    }
}
?>

Một lớp trừu tượng giống như một giao diện, nhưng bạn có thể định nghĩa các phương thức trong một lớp trừu tượng trong khi trong một giao diện chúng đều trừu tượng. Các lớp trừu tượng có thể có cả phương thức rỗng và phương pháp làm việc / cụ thể. Trong các giao diện, các chức năng được xác định không thể có một cơ thể. Trong các lớp trừu tượng, họ có thể.

Một ví dụ thực tế:

<?php 
abstract class person {

    public $LastName;
    public $FirstName;
    public $BirthDate;

    abstract protected function write_info();
}

final class employee extends person{

    public $EmployeeNumber;
    public $DateHired;

    public function write_info(){
        //sql codes here
        echo "Writing ". $this->LastName . "'s info to emloyee dbase table <br>";   
    }
}

final class student extends person{

    public $StudentNumber;
    public $CourseName;

    public function write_info(){
        //sql codes here
        echo "Writing ". $this->LastName . "'s info to student dbase table <br>";
    }
}

///----------
$personA = new employee;
$personB = new student;

$personA->FirstName="Joe";
$personA->LastName="Sbody";

$personB->FirstName="Ben";
$personB->LastName="Dover";

$personA->write_info();
// Writing Sbody's info to emloyee dbase table
$personB->write_info();
// Writing Dover's info to student dbase table 

2
Xin chào, câu trả lời này có thể có downvote do cách nó được định dạng. Sẽ tốt hơn nếu đó không phải là một khối mã lớn (bốn không gian biến một thứ gì đó thành một khối mã, bỏ qua văn bản để đưa nó ra khỏi khối) và nếu điều này được sao chép từ một nơi nào đó (có vẻ như vậy) nó sẽ là lịch sự để tín dụng họ.
Camilo Martin

9
Tôi yêu bạn cho người đàn ông ví dụ trái cây! Kể từ khi tôi bắt đầu học php, bản phác thảo này giúp tôi rõ ràng cảm ơn rất nhiều
Raheel

23
+1 What does that taste like? Well, it doesn't make much sense, so you shouldn't be able to do that.Bây giờ tôi đã biết trừu tượng!
Webinan

Không những gì finaltừ khóa làm gì? Bài đăng tuyệt vời, cảm ơn.
Gus

1
@VineeshKalarickal Điều tôi không hiểu trong ví dụ Person là sự khác biệt giữa: 1) khi sử dụng lớp trừu tượng Person (như trong ví dụ); 2) viết Person như một lớp tiêu chuẩn và làm cho Nhân viên và Sinh viên ghi đè lên phương thức write_info ().
Ferex

66

Thực tiễn tốt nhất là sử dụng một giao diện để xác định hợp đồng và một lớp trừu tượng chỉ là một cách thực hiện. Lớp trừu tượng đó có thể điền vào rất nhiều mẫu soạn sẵn để bạn có thể tạo một triển khai bằng cách chỉ ghi đè những gì bạn cần hoặc muốn mà không buộc bạn phải sử dụng một triển khai cụ thể.


37

Chỉ để ném cái này vào hỗn hợp, nhưng như Cletus đã đề cập bằng cách sử dụng một giao diện kết hợp với một lớp trừu tượng, tôi thường sử dụng giao diện để làm rõ suy nghĩ thiết kế của mình.

Ví dụ:

<?php
class parser implements parserDecoratorPattern {
    //...
}

Bằng cách đó, bất kỳ ai đọc mã của tôi (và ai biết Mẫu trang trí là gì) sẽ biết ngay a) cách tôi xây dựng trình phân tích cú pháp và b) có thể xem phương thức nào được sử dụng để triển khai mẫu trang trí.

Ngoài ra, và tôi có thể không ở đây không phải là một lập trình viên Java / C ++ / etc, nhưng các kiểu dữ liệu có thể xuất hiện ở đây. Các đối tượng của bạn thuộc loại và khi bạn chuyển chúng xung quanh loại đó sẽ lập trình. Di chuyển các mục có thể hợp đồng của bạn vào giao diện chỉ ra lệnh các loại mà các phương thức trả về, chứ không phải loại cơ sở của lớp thực hiện nó.

Đã muộn và tôi không thể nghĩ ra một ví dụ mã psudo tốt hơn, nhưng rồi đây:

<?php
interface TelevisionControls {};
class Remote implements TelevisionControls {};
class Spouse implements TelevisionControls {};
Spouse spouse = new Spouse();
Remote remote = new Remote();
isSameType = (bool)(remote == spouse)

1
Thật là một ví dụ tuyệt vời! ;)
Joel Murphy

@ matt2000 Không phân biệt giới tính VÀ cũng hoạt động cho các cuộc hôn nhân đồng giới. Chỉnh sửa tuyệt vời. :)
Austen Hoogen

4
Đây là một ví dụ tuyệt vời! Tuy nhiên, tôi không nghĩ bạn có thể khởi tạo một lớp trừu tượng mà là một lớp kéo dài từ một lớp trừu tượng
Arielle Nguyễn

2
sẽ không giết để tạo mã sudo ít nhất trông giống như ngôn ngữ được đề cập. ai đó nên đặt một số $ trong đó.
Tôi vật lộn một con gấu một lần.

2
Mặc dù ví dụ này rất buồn cười, chúng ta không thể khởi tạo trực tiếp một lớp trừu tượng, vui lòng thực hiện các thay đổi trong ví dụ để mọi người có thể không coi đó là cách sử dụng hợp lệ trong PHP.
saji89

16

Sự khác biệt chính là một lớp trừu tượng có thể chứa triển khai mặc định trong khi giao diện không thể.

Một giao diện là một hợp đồng của hành vi mà không cần thực hiện.


16

Ngoài ra, chỉ muốn thêm vào đây rằng chỉ vì bất kỳ ngôn ngữ OO nào khác cũng có một số loại giao diện và trừu tượng hóa không có nghĩa là chúng có cùng ý nghĩa và mục đích như trong PHP. Việc sử dụng trừu tượng / giao diện hơi khác nhau trong khi các giao diện trong PHP thực sự không có chức năng thực sự. Chúng chỉ được sử dụng cho các lý do ngữ nghĩa và liên quan đến chương trình. Vấn đề là phải có một dự án linh hoạt nhất có thể, có thể mở rộng và an toàn cho các tiện ích mở rộng trong tương lai bất kể nhà phát triển sau này có kế hoạch sử dụng hoàn toàn khác hay không.

Nếu tiếng Anh của bạn không phải là tiếng mẹ đẻ, bạn có thể tra cứu Trừu tượng và Giao diện thực sự là gì. Và tìm từ đồng nghĩa quá.

Và điều này có thể giúp bạn như một phép ẩn dụ:

Giao diện

Giả sử, bạn nướng một loại bánh mới với dâu tây và bạn đã tạo ra một công thức mô tả các thành phần và các bước. Chỉ có bạn biết tại sao nó ngon như vậy và khách của bạn thích nó. Sau đó, bạn quyết định xuất bản công thức của bạn để người khác cũng có thể thử bánh đó.

Vấn đề ở đây là

- làm cho đúng
- cẩn thận
- để ngăn chặn những thứ có thể trở nên tồi tệ (như quá nhiều dâu tây hoặc thứ gì đó)
- để giữ cho những người dùng thử dễ dàng
- nói cho bạn biết phải làm trong bao lâu (như bắn )
- để cho biết những việc bạn CÓ THỂ làm nhưng KHÔNG PHẢI

Chính xác NÀY là những gì mô tả giao diện. Đó là một hướng dẫn, một bộ hướng dẫn quan sát nội dung của công thức. Tương tự như khi bạn tạo một dự án bằng PHP và bạn muốn cung cấp mã trên GitHub hoặc với bạn bè của mình hoặc bất cứ điều gì. Giao diện là những gì mọi người có thể làm và những gì bạn không nên làm. Các quy tắc giữ nó - nếu bạn không tuân theo một quy tắc, toàn bộ cấu trúc sẽ bị phá vỡ.


TÓM TẮT

Để tiếp tục với phép ẩn dụ này ở đây ... hãy tưởng tượng, bạn là khách lần này đang ăn chiếc bánh đó. Sau đó, bạn đang thử chiếc bánh đó bằng cách sử dụng công thức bây giờ. Nhưng bạn muốn thêm các thành phần mới hoặc thay đổi / bỏ qua các bước được mô tả trong công thức. Vậy điều gì đến tiếp theo? Lên kế hoạch cho một phiên bản khác của chiếc bánh đó. Lần này với quả mọng đen và không phải quả dâu và nhiều kem vani ... ngon tuyệt.

Đây là những gì bạn có thể xem xét một phần mở rộng của bánh ban đầu. Về cơ bản, bạn thực hiện một sự trừu tượng hóa nó bằng cách tạo ra một công thức mới bởi vì nó khác biệt. Nó có một vài bước mới và các thành phần khác. Tuy nhiên, phiên bản berry đen có một số phần bạn đã đảm nhận từ bản gốc - đây là những bước cơ bản mà mọi loại bánh đó phải có. Giống như các thành phần giống như sữa - Đó là những gì mọi lớp dẫn xuất đều có.

Bây giờ bạn muốn trao đổi các thành phần và các bước và những điều này PHẢI được xác định trong phiên bản mới của chiếc bánh đó. Đây là những phương pháp trừu tượng phải được xác định cho chiếc bánh mới, bởi vì nên có một loại trái cây trong chiếc bánh nhưng cái nào? Vì vậy, bạn lấy quả mọng màu đen lần này. Làm xong.

Ở đó bạn đi, bạn đã mở rộng bánh, theo giao diện và các bước trừu tượng và các thành phần từ nó.


1
Đây là so sánh yêu thích của tôi về bất cứ điều gì liên quan đến PHP. Nó thực sự có ý nghĩa. Cảm ơn bạn!
cbloss793

13

Để thêm vào một số câu trả lời đã xuất sắc:

  • Các lớp trừu tượng cho phép bạn cung cấp một số mức độ thực hiện, giao diện là các mẫu thuần túy. Một giao diện chỉ có thể xác định chức năng , nó không bao giờ có thể thực hiện nó.

  • Bất kỳ lớp nào thực hiện giao diện đều cam kết thực hiện tất cả các phương thức mà nó định nghĩa hoặc nó phải được khai báo là trừu tượng.

  • Các giao diện có thể giúp quản lý thực tế rằng, giống như Java, PHP không hỗ trợ nhiều kế thừa. Một lớp PHP chỉ có thể mở rộng một cha mẹ duy nhất. Tuy nhiên, bạn có thể thực hiện một lời hứa lớp để thực hiện nhiều giao diện như bạn muốn.

  • loại: đối với mỗi giao diện mà nó thực hiện, lớp sẽ nhận loại tương ứng. Bởi vì bất kỳ lớp nào cũng có thể thực hiện một giao diện (hoặc nhiều giao diện), các giao diện tham gia hiệu quả các loại không liên quan.

  • một lớp có thể mở rộng một siêu lớp và thực hiện bất kỳ số lượng giao diện nào:

    class SubClass extends ParentClass implements Interface1, Interface2 {
        // ...
    }

Hãy giải thích khi nào tôi nên sử dụng một giao diện và khi nào tôi nên sử dụng lớp trừu tượng?

Sử dụng một giao diện khi bạn chỉ cần cung cấp một mẫu mà không cần triển khai và bạn muốn đảm bảo bất kỳ lớp nào thực hiện giao diện đó sẽ có cùng phương thức như bất kỳ lớp nào khác thực hiện nó (ít nhất).

Sử dụng một lớp trừu tượng khi bạn muốn tạo một nền tảng cho các đối tượng khác (một lớp được xây dựng một phần). Lớp mở rộng lớp trừu tượng của bạn sẽ sử dụng một số thuộc tính hoặc phương thức được xác định / triển khai:

<?php
// interface
class X implements Y { } // this is saying that "X" agrees to speak language "Y" with your code.

// abstract class
class X extends Y { } // this is saying that "X" is going to complete the partial class "Y".
?>

Làm thế nào tôi có thể thay đổi lớp trừu tượng của mình thành một giao diện?

Dưới đây là một trường hợp / ví dụ đơn giản hóa. Đưa ra bất kỳ chi tiết thực hiện ra. Ví dụ: thay đổi lớp trừu tượng của bạn từ:

abstract class ClassToBuildUpon {
    public function doSomething() {
          echo 'Did something.';
    }
}

đến:

interface ClassToBuildUpon {
    public function doSomething();
}

12

Từ quan điểm phylosophic:

  • Một lớp trừu tượng đại diện cho một mối quan hệ "là một". Hãy nói rằng tôi có trái cây, tôi sẽ có một lớp trừu tượng Fruit chia sẻ khả năng đáp ứng và hành vi chung.

  • Một giao diện đại diện cho một mối quan hệ "nên làm". Theo tôi, một giao diện (theo ý kiến ​​của một nhà phát triển cơ sở), nên được đặt tên bằng một hành động hoặc một cái gì đó gần với một hành động, (Xin lỗi, không thể tìm thấy từ này, tôi không phải là người bản ngữ tiếng Anh) Hãy nói rằng IEizable. Bạn biết nó có thể ăn được, nhưng bạn không biết bạn ăn gì.

Từ quan điểm mã hóa:

  • Nếu các đối tượng của bạn có mã trùng lặp, đó là dấu hiệu cho thấy chúng có hành vi chung, có nghĩa là bạn có thể cần một lớp trừu tượng để sử dụng lại mã, điều mà bạn không thể làm với giao diện.

  • Một điểm khác biệt nữa là một đối tượng có thể thực hiện nhiều giao diện mà bạn cần, nhưng bạn chỉ có thể có một lớp trừu tượng vì "vấn đề kim cương" (xem tại đây để biết tại sao! Http://en.wikipedia.org/wiki/ Nhiều_inherribution # The_diamond_propet )

Tôi có thể quên một số điểm, nhưng tôi hy vọng nó có thể làm rõ mọi thứ.

Tái bút: "là một" / "nên làm" được đưa ra bởi câu trả lời của Vivek Vermani, tôi không có ý đánh cắp câu trả lời của anh ta, chỉ để sử dụng lại các thuật ngữ vì tôi thích chúng!


2
Từ bạn đang tìm kiếm có thể ăn được, tôi tin thế.
Travis Weston

1
Trên thực tế, tôi tin rằng đó là "Động từ", "từ đang làm"
Grizly

7

Sự khác biệt kỹ thuật giữa một lớp trừu tượng và một giao diện đã được liệt kê chính xác trong các câu trả lời khác. Tôi muốn thêm một lời giải thích để lựa chọn giữa một lớp và một giao diện trong khi viết mã vì mục đích lập trình hướng đối tượng.

Một lớp nên đại diện cho một thực thể trong khi một giao diện sẽ đại diện cho hành vi.

Hãy lấy một ví dụ. Một màn hình máy tính là một thực thể và nên được biểu diễn dưới dạng một lớp.

class Monitor{
    private int monitorNo;
}

Nó được thiết kế để cung cấp giao diện hiển thị cho bạn, vì vậy chức năng nên được xác định bởi giao diện.

interface Display{
    void display();
}

Có nhiều điều khác để xem xét như được giải thích trong các câu trả lời khác, nhưng đây là điều cơ bản nhất mà hầu hết mọi người bỏ qua trong khi mã hóa.


2
PHP không định nghĩa các kiểu trả về và OP đã gắn thẻ câu hỏi này vớiPHP
Purefan

1

Chỉ muốn thêm một ví dụ về thời điểm bạn có thể cần sử dụng cả hai. Tôi hiện đang viết một trình xử lý tệp ràng buộc với mô hình cơ sở dữ liệu trong một giải pháp ERP cho mục đích chung.

  • Tôi có nhiều lớp trừu tượng xử lý tiêu chuẩn và một số chức năng đặc biệt như chuyển đổi và phát trực tuyến cho các loại tệp khác nhau.
  • Giao diện truy cập tệp xác định một tập hợp các phương thức phổ biến cần thiết để nhận, lưu trữ và xóa tệp.

Bằng cách này, tôi có được nhiều mẫu cho các tệp khác nhau và một bộ phương thức giao diện chung với sự phân biệt rõ ràng. Giao diện cung cấp sự tương tự chính xác cho các phương thức truy cập hơn là những gì đã có với một lớp trừu tượng cơ sở.

Tiếp tục đi xuống khi tôi sẽ tạo bộ điều hợp cho các dịch vụ lưu trữ tệp khác nhau, việc triển khai này sẽ cho phép giao diện được sử dụng ở nơi khác trong các ngữ cảnh hoàn toàn khác nhau.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.