Khi nào nên sử dụng một giao diện thay vì một lớp trừu tượng và ngược lại?


426

Đây có thể là một câu hỏi OOP chung. Tôi muốn làm một so sánh chung giữa một giao diện và một lớp trừu tượng trên cơ sở sử dụng của chúng.

Khi nào thì người ta muốn sử dụng một giao diện và khi nào thì người ta muốn sử dụng một lớp trừu tượng ?


1
Điều này đã được hỏi rất nhiều: stackoverflow.com/questions/56867/interface-vs-base-
class

1
Ngoài các câu trả lời bên dưới, đây là một danh sách ngắn về nơi bạn có thể muốn giao diện và nơi bạn có thể không: Khi nào nên sử dụng Giao diện: msdn.microsoft.com/en-us/l Library / 3b5b8ezk (v = vs.80) .aspx
Anthony

sử dụng trừu tượng khi bạn không chắc chắn những gì lớp sẽ làm. sử dụng giao diện nếu bạn là.
Uur Gümüşhan

Tôi quan tâm để xem có bao nhiêu nhà phát triển, những người không làm việc tại Microsoft, xác định và sử dụng các giao diện trong quá trình phát triển hàng ngày của họ.
user1451111

Câu trả lời:


429

Tôi đã viết một bài báo về điều đó:

Các lớp và giao diện trừu tượng

Tóm tắt:

Khi chúng ta nói về các lớp trừu tượng, chúng ta đang xác định các đặc điểm của một loại đối tượng; chỉ định một đối tượng là gì .

Khi chúng ta nói về một giao diện và xác định các khả năng mà chúng ta hứa sẽ cung cấp, chúng ta đang nói về việc thiết lập một hợp đồng về những gì đối tượng có thể làm.


114
Điều này rất hữu ích : Interfaces do not express something like "a Doberman is a type of dog and every dog can walk" but more like "this thing can walk". Cảm ơn bạn
aexl

2
Có vẻ như liên kết của bạn đã chết.
Kim Ahlstrøm Meyn Mathiassen

Giải thích của Alex dưới đây, lại: sự khác biệt giữa chỉ các chức năng mô tả được thực hiện so với việc mô tả trạng thái được lưu trữ, có vẻ như là một câu trả lời tốt hơn cho câu hỏi này, bởi vì sự khác biệt không chỉ là triết học.
Duncan Malashock 11/03/2016

1
Duncan Malashock, không thực sự. Câu trả lời của Jorge là câu trả lời hay hơn. Câu trả lời của Alex tập trung vào cơ học, trong khi câu trả lời của Jorge về ngữ nghĩa.
Nazar Merza

8
Tôi thích câu nói trước câu trả lời được chỉ định của bạn:Use abstract classes and inheritance if you can make the statement “A is a B”. Use interfaces if you can make the statement “A is capable of [doing] as”
S1r-Lanzelot

433

Một lớp trừu tượng có thể có trạng thái chia sẻ hoặc chức năng. Một giao diện chỉ là một lời hứa để cung cấp trạng thái hoặc chức năng. Một lớp trừu tượng tốt sẽ làm giảm số lượng mã phải viết lại vì chức năng hoặc trạng thái của nó có thể được chia sẻ. Giao diện không có thông tin xác định sẽ được chia sẻ


65
Điều này, với tôi, là câu trả lời tốt nhất ở đây và thật xấu hổ vì nó đã không được bình chọn cao hơn. Vâng, có sự khác biệt về triết học giữa hai khái niệm, nhưng điểm gốc là các lớp trừu tượng đảm bảo rằng tất cả các hậu duệ chia sẻ chức năng / trạng thái, trong đó một giao diện chỉ đảm bảo một liên kết chung.
drharris

3
Ví dụ, một lớp cơ sở trừu tượng được sử dụng cho mẫu thiết kế phương thức mẫu, trong khi một giao diện được sử dụng cho mẫu thiết kế chiến lược .
Raedwald

1
Tôi nghĩ rằng tóm tắt của Jorge giải thích chính mặc dù đằng sau sự tồn tại của cả hai trong khi câu trả lời của Alex là sự khác biệt về kết quả. Tôi ước tôi có thể đánh dấu cả hai là câu trả lời đúng, nhưng tôi vẫn thích câu trả lời của Jorge.
Chirantan

đâyví dụ với mã.
shaijut

Đối với tôi, "Một lớp trừu tượng tốt này sẽ làm giảm số lượng mã phải viết lại vì chức năng hoặc trạng thái của nó có thể được chia sẻ." tuyên bố là cốt lõi của câu trả lời.
Div Tiwari

82

Cá nhân, tôi gần như không bao giờ có nhu cầu viết các lớp trừu tượng.

Hầu hết tôi thấy các lớp trừu tượng được sử dụng (mis), đó là vì tác giả của lớp trừu tượng đang sử dụng mẫu "Phương thức mẫu".

Vấn đề với "Phương thức mẫu" là gần như luôn luôn được cấp lại - lớp "dẫn xuất" biết về không chỉ phương thức "trừu tượng" của lớp cơ sở mà nó đang triển khai, mà còn về các phương thức công khai của lớp cơ sở , mặc dù hầu hết các lần không cần gọi cho họ.

(Đơn giản hóa quá mức) ví dụ:

abstract class QuickSorter
{
    public void Sort(object[] items)
    {
        // implementation code that somewhere along the way calls:
        bool less = compare(x,y);
        // ... more implementation code
    }
    abstract bool compare(object lhs, object rhs);
}

Vì vậy, ở đây, tác giả của lớp này đã viết một thuật toán chung và dự định cho mọi người sử dụng nó bằng cách "chuyên" hóa nó bằng cách cung cấp "móc" của riêng họ - trong trường hợp này là phương pháp "so sánh".

Vì vậy, mục đích sử dụng là như thế này:

class NameSorter : QuickSorter
{
    public bool compare(object lhs, object rhs)
    {
        // etc.
    }
}

Vấn đề với điều này là bạn đã kết hợp quá mức hai khái niệm:

  1. Một cách để so sánh hai mục (mục nào nên đi trước)
  2. Phương pháp sắp xếp các mục (ví dụ: quicksort vs merge sort, v.v.)

Về mặt lý thuyết, về mặt lý thuyết, tác giả của phương pháp "so sánh" có thể gọi lại một cách ngẫu nhiên vào phương thức "Sắp xếp" của siêu lớp ... mặc dù trong thực tế, họ sẽ không bao giờ muốn hoặc không cần phải làm điều này.

Cái giá bạn phải trả cho khớp nối không cần thiết này là rất khó để thay đổi siêu lớp và trong hầu hết các ngôn ngữ OO, không thể thay đổi nó khi chạy.

Phương pháp thay thế là sử dụng mẫu thiết kế "Chiến lược" thay thế:

interface IComparator
{
    bool compare(object lhs, object rhs);
}

class QuickSorter
{
    private readonly IComparator comparator;
    public QuickSorter(IComparator comparator)
    {
        this.comparator = comparator;
    }

    public void Sort(object[] items)
    {
        // usual code but call comparator.Compare();
    }
}

class NameComparator : IComparator
{
    bool compare(object lhs, object rhs)
    {
        // same code as before;
    }
}

Vì vậy, hãy chú ý ngay bây giờ: Tất cả những gì chúng ta có là các giao diện và triển khai cụ thể các giao diện đó. Trong thực tế, bạn không thực sự cần bất cứ điều gì khác để thực hiện một thiết kế OO cấp cao.

Để "che giấu" thực tế là chúng tôi đã triển khai "sắp xếp tên" bằng cách sử dụng lớp "QuickSort" và "NameComparator", chúng tôi vẫn có thể viết phương thức xuất xưởng ở đâu đó:

ISorter CreateNameSorter()
{
    return new QuickSorter(new NameComparator());
}

Bất cứ khi nào bạn có một lớp trừu tượng, bạn có thể làm điều này ... ngay cả khi có mối quan hệ tái ủy quyền tự nhiên giữa lớp cơ sở và lớp dẫn xuất, nó thường trả tiền để làm cho chúng rõ ràng.

Một suy nghĩ cuối cùng: Tất cả những gì chúng ta đã làm ở trên là "soạn thảo" chức năng "NameSorting" bằng cách sử dụng chức năng "QuickSort" và chức năng "NameComparison" ... trong ngôn ngữ lập trình chức năng, phong cách lập trình này thậm chí còn tự nhiên hơn, với ít mã hơn.


5
Chỉ vì bạn có thể sử dụng các lớp Trừu tượng hoặc mẫu Phương thức mẫu không có nghĩa là bạn cần tránh chúng. Mẫu chiến lược là một mẫu khác nhau cho một tình huống khác như trong ví dụ này, nhưng có rất nhiều ví dụ trong đó mẫu mẫu phù hợp hơn nhiều so với Chiến lược.
Jorge Córdoba

5
Chà, theo kinh nghiệm của tôi, tôi không bao giờ gặp phải chúng (những tình huống mà phương pháp mẫu thích hợp hơn) ... hoặc hiếm khi nào. Và đó là tất cả "trừu tượng" là - hỗ trợ ngôn ngữ cho mẫu thiết kế "phương thức mẫu".
Paul Hollingsworth

Ok, tôi đã sử dụng nó một lần cho một hệ thống chuyên gia trong đó quy trình là như thế, lấy 1. FillTheParameter, 2. Tạo sản phẩm Vector giữa chúng, 3. Với mỗi kết quả tính toán, 4. Tham gia kết quả, trong đó bước 1 và 3 trong đó ủy nhiệm và 2 và 4 thực hiện trong lớp cơ sở.
Jorge Córdoba

10
Tôi thấy hầu như bất kỳ việc sử dụng các lớp trừu tượng khó hiểu hơn. Suy nghĩ về các hộp giao tiếp với nhau thay vì mối quan hệ thừa kế sẽ dễ dàng hơn (đối với tôi) ... Nhưng tôi cũng đồng ý rằng các ngôn ngữ OO hiện tại buộc quá nhiều nồi hơi ... Chức năng sẽ là cách để vượt qua OO
Paul Hollingsworth

4
Ví dụ về việc sử dụng sai là khá tầm thường. Nó hiếm khi sôi xuống với chức năng tước đẹp như so sánh. Phổ biến hơn nhiều là các tình huống trong đó có một số chức năng mặc định mà các lớp dẫn xuất thay thế hoặc mở rộng (và trong trường hợp sau, nó hoàn toàn hợp lệ để gọi hàm lớp cơ sở). Trong ví dụ của bạn không có chức năng mặc định, vì vậy việc sử dụng lớp trừu tượng không có lý do chính đáng.
SomeWittyUsername

41

Nếu bạn đang xem java là ngôn ngữ OOP,

" Giao diện không cung cấp phương thức triển khai " không còn hợp lệ với khởi chạy Java 8. Bây giờ java cung cấp thực hiện trong giao diện cho các phương thức mặc định.

Nói một cách đơn giản, tôi muốn sử dụng

giao diện: Để thực hiện hợp đồng bởi nhiều đối tượng không liên quan. Nó cung cấp " ĐÃ A khả năng ".

lớp trừu tượng: Để thực hiện cùng một hành vi hoặc khác nhau giữa nhiều đối tượng liên quan. Nó thành lập " mối quan hệ IS A ".

Trang web của Oracle cung cấp sự khác biệt chính giữa interfaceabstractlớp.

Cân nhắc sử dụng các lớp trừu tượng nếu:

  1. Bạn muốn chia sẻ mã giữa một số lớp liên quan chặt chẽ.
  2. Bạn mong đợi rằng các lớp mở rộng lớp trừu tượng của bạn có nhiều phương thức hoặc trường phổ biến hoặc yêu cầu các sửa đổi truy cập khác ngoài công khai (như được bảo vệ và riêng tư).
  3. Bạn muốn khai báo các trường không tĩnh hoặc không cuối cùng.

Cân nhắc sử dụng các giao diện nếu:

  1. Bạn mong đợi rằng các lớp không liên quan sẽ thực hiện giao diện của bạn. Ví dụ, nhiều đối tượng không liên quan có thể thực hiện Serializablegiao diện.
  2. Bạn muốn chỉ định hành vi của một loại dữ liệu cụ thể, nhưng không quan tâm đến việc ai thực hiện hành vi của nó.
  3. Bạn muốn tận dụng lợi thế của nhiều kiểu thừa kế.

Thí dụ:

Lớp trừu tượng ( quan hệ IS )

Reader là một lớp trừu tượng.

BufferedReader là mộtReader

FileReader là mộtReader

FileReaderBufferedReaderđược sử dụng cho mục đích chung: Đọc dữ liệu và chúng có liên quan thông qua Readerlớp học.

Interface ( HAS Một khả năng)

Nối tiếp là một giao diện.

Giả sử rằng bạn có hai lớp trong ứng dụng của mình, đang triển khai Serializablegiao diện

Employee implements Serializable

Game implements Serializable

Ở đây bạn không thể thiết lập bất kỳ mối quan hệ nào thông qua Serializablegiao diện giữa EmployeeGame, có nghĩa là cho các mục đích khác nhau. Cả hai đều có khả năng nối tiếp trạng thái và sự so sánh kết thúc ở đó.

Hãy xem những bài viết này:

Làm thế nào tôi có thể giải thích sự khác biệt giữa một lớp Giao diện và lớp Trừu tượng?


40

Được rồi, tôi đã tự "mò mẫm" điều này - đây là điều khoản của giáo dân (cứ tự nhiên sửa tôi nếu tôi sai) - Tôi biết chủ đề này là oooooold, nhưng một ngày nào đó người khác có thể vấp phải nó ...

Các lớp trừu tượng cho phép bạn tạo một kế hoạch chi tiết và cho phép bạn bổ sung các thuộc tính và phương thức XÂY DỰNG (thực hiện) mà bạn muốn TẤT CẢ con cháu của nó sở hữu.

Mặt khác, một giao diện chỉ cho phép bạn khai báo rằng bạn muốn các thuộc tính và / hoặc các phương thức có tên đã tồn tại trong tất cả các lớp thực hiện nó - nhưng không chỉ định cách bạn nên triển khai nó. Ngoài ra, một lớp có thể thực hiện NHIỀU giao diện, nhưng chỉ có thể mở rộng MỘT lớp Trừu tượng. Giao diện là một công cụ kiến ​​trúc cấp cao (sẽ trở nên rõ ràng hơn nếu bạn bắt đầu nắm bắt các mẫu thiết kế) - một Tóm tắt có một chân trong cả hai trại và cũng có thể thực hiện một số công việc bẩn thỉu.

Tại sao sử dụng cái này hơn cái kia? Cái trước cho phép định nghĩa cụ thể hơn về con cháu - cái sau cho phép đa hình lớn hơn . Điểm cuối cùng này rất quan trọng đối với người dùng cuối / người viết mã, người có thể sử dụng thông tin này để triển khai AP I (nterface) trong nhiều kết hợp / hình dạng phù hợp với nhu cầu của họ.

Tôi nghĩ rằng đây là khoảnh khắc "bóng đèn" đối với tôi - nghĩ về các giao diện ít hơn từ nhận thức của tác giả và nhiều hơn từ bất kỳ lập trình viên nào đến sau trong chuỗi, người đang thêm triển khai vào dự án hoặc mở rộng API.


để xây dựng dựa trên điều này: Một đối tượng thực hiện giao diện sẽ sử dụng LOẠI. Điều này là rất quan trọng. Vì vậy, bạn có thể chuyển các biến thể khác nhau của giao diện cho một lớp, nhưng hãy tham khảo chúng (và các phương thức của chúng) VỚI TÊN LOẠI GIAO DIỆN. Vì vậy, bạn loại bỏ sự cần thiết của một chuyển đổi hoặc vòng lặp if / khác. Hãy thử hướng dẫn này về chủ đề - nó cho thấy việc sử dụng giao diện thông qua Mẫu chiến lược. phpfreaks.com/tutorial/design-potypes---strargety-and-bridge/iêu
sunwukung

Tôi hoàn toàn đồng ý về khoảnh khắc bóng đèn của bạn: "API (nterface) trong nhiều kết hợp / hình dạng phù hợp với nhu cầu của họ"! Điểm rất rất tốt để thực hiện.
Trevor Boyd Smith

38

Theo quan điểm của tôi:

Một giao diện về cơ bản xác định một hợp đồng, mà bất kỳ lớp thực hiện nào cũng phải tuân thủ (thực hiện các thành viên giao diện). Nó không chứa bất kỳ mã nào.

Mặt khác, một lớp trừu tượng có thể chứa mã và có thể có một số phương thức được đánh dấu là trừu tượng mà một lớp kế thừa phải thực hiện.

Các tình huống hiếm gặp mà tôi đã sử dụng các lớp trừu tượng là khi tôi có một số chức năng mặc định mà lớp kế thừa có thể không thú vị khi ghi đè, nói một lớp cơ sở trừu tượng, mà một số lớp chuyên biệt kế thừa.

Ví dụ (một trong rất thô sơ!): Xem xét một lớp cơ sở gọi là khách hàng trong đó có phương pháp trừu tượng như CalculatePayment(), CalculateRewardPoints()và một số phương pháp phi trừu tượng như GetName(), SavePaymentDetails().

Các lớp chuyên biệt thích RegularCustomerGoldCustomersẽ kế thừa từ Customerlớp cơ sở và thực hiện logic riêng CalculatePayment()CalculateRewardPoints()phương thức của chúng, nhưng sử dụng lại các phương thức GetName()SavePaymentDetails().

Bạn có thể thêm nhiều chức năng hơn vào một lớp trừu tượng (phương thức không trừu tượng) mà không ảnh hưởng đến các lớp con đang sử dụng phiên bản cũ hơn. Trong khi đó việc thêm các phương thức vào một giao diện sẽ ảnh hưởng đến tất cả các lớp thực hiện nó vì bây giờ chúng sẽ cần phải thực hiện các thành viên giao diện mới được thêm vào.

Một lớp trừu tượng với tất cả các thành viên trừu tượng sẽ tương tự như một giao diện.


1
+1 cho "Bạn có thể thêm nhiều chức năng hơn vào một lớp trừu tượng (không phải là các phương thức trừu tượng) mà không ảnh hưởng đến các lớp con đang sử dụng phiên bản cũ hơn. Trong khi đó, việc thêm các phương thức vào một giao diện sẽ ảnh hưởng đến tất cả các lớp triển khai khi chúng cần triển khai các thành viên giao diện mới được thêm vào. "
Div Tiwari

các giao diện có thể có các phương thức "mặc định" vì vậy không có phương thức nào trong các giao diện là ý tưởng sai. Mối quan hệ IS-Cha mẹ với Con cái là một chìa khóa ở đây. Ngoài ra, "Thuộc tính được chia sẻ" so với "Thuộc tính được chia sẻ". vd: Chó iS-A Animal. Nhưng một con chó cũng có thể "Đi bộ"
ha9u63ar

30

Khi nào nên làm những gì là một điều rất đơn giản nếu bạn có khái niệm rõ ràng trong tâm trí của bạn.

Các lớp trừu tượng có thể được tạo ra trong khi Giao diện có thể được triển khai. Có một số khác biệt giữa hai. Khi bạn lấy được một lớp Trừu tượng, mối quan hệ giữa lớp dẫn xuất và lớp cơ sở là 'là một mối quan hệ'. ví dụ: Chó là Động vật, Cừu là Động vật có nghĩa là lớp Derogen đang thừa hưởng một số thuộc tính từ lớp cơ sở.

Trong khi thực hiện các giao diện, mối quan hệ là "có thể". ví dụ, một con chó có thể là một con chó gián điệp. Một con chó có thể là một con chó xiếc. Một con chó có thể là một con chó đua. Điều đó có nghĩa là bạn thực hiện các phương pháp nhất định để có được một cái gì đó.

Tôi hy vọng tôi rõ ràng.


11

1.Nếu bạn đang tạo một cái gì đó cung cấp chức năng chung cho các lớp không liên quan, hãy sử dụng một giao diện.

2.Nếu bạn đang tạo một cái gì đó cho các đối tượng có liên quan chặt chẽ trong hệ thống phân cấp, hãy sử dụng một lớp trừu tượng.


10

Tôi đã viết một bài báo về khi nào nên sử dụng một lớp trừu tượng và khi nào nên sử dụng một giao diện. Có rất nhiều sự khác biệt giữa chúng ngoài "một IS-A ... và một CAN-DO ...". Đối với tôi, đó là những câu trả lời đóng hộp. Tôi đề cập đến một vài lý do khi sử dụng một trong hai. Hy vọng nó giúp.

http://codeofdoom.com/wordpress/2009/02/12/learn-this-when-to-use-an-abauge- class-and-an-interface /


7

Tôi nghĩ rằng cách đặt cô đọng nhất là như sau:

Thuộc tính được chia sẻ => lớp trừu tượng.
Chức năng dùng chung => giao diện.

Và để nó bớt ngắn gọn ...

Ví dụ lớp trừu tượng:

public abstract class BaseAnimal
{
    public int NumberOfLegs { get; set; }

    protected BaseAnimal(int numberOfLegs)
    {
        NumberOfLegs = numberOfLegs;
    }
}

public class Dog : BaseAnimal
{
    public Dog() : base(4) { }
}

public class Human : BaseAnimal 
{
    public Human() : base(2) { }
}

Vì động vật có một tài sản chung - số chân trong trường hợp này - nên tạo ra một lớp trừu tượng có chứa tài sản chung này. Điều này cũng cho phép chúng tôi viết mã phổ biến hoạt động trên tài sản đó. Ví dụ:

public static int CountAllLegs(List<BaseAnimal> animals)
{
    int legCount = 0;
    foreach (BaseAnimal animal in animals)
    {
        legCount += animal.NumberOfLegs;
    }
    return legCount;
}

Ví dụ về giao diện:

public interface IMakeSound
{
    void MakeSound();
}

public class Car : IMakeSound
{
    public void MakeSound() => Console.WriteLine("Vroom!");
}

public class Vuvuzela : IMakeSound
{
    public void MakeSound() => Console.WriteLine("VZZZZZZZZZZZZZ!");        
}

Lưu ý ở đây rằng Vuvuzelas và Ô tô là những thứ hoàn toàn khác nhau, nhưng chúng có chung chức năng: tạo ra âm thanh. Do đó, một giao diện có ý nghĩa ở đây. Hơn nữa, nó sẽ cho phép các lập trình viên nhóm các thứ tạo ra âm thanh với nhau theo một giao diện chung - IMakeSoundtrong trường hợp này. Với thiết kế này, bạn có thể viết mã sau đây:

List<IMakeSound> soundMakers = new List<ImakeSound>();
soundMakers.Add(new Car());
soundMakers.Add(new Vuvuzela());
soundMakers.Add(new Car());
soundMakers.Add(new Vuvuzela());
soundMakers.Add(new Vuvuzela());

foreach (IMakeSound soundMaker in soundMakers)
{
    soundMaker.MakeSound();
}

Bạn có thể cho biết những gì sẽ đầu ra?

Cuối cùng, bạn có thể kết hợp cả hai.

Ví dụ kết hợp:

public interface IMakeSound
{
    void MakeSound();
}

public abstract class BaseAnimal : IMakeSound
{
    public int NumberOfLegs { get; set; }

    protected BaseAnimal(int numberOfLegs)
    {
        NumberOfLegs = numberOfLegs;
    }

    public abstract void MakeSound();
}

public class Cat : BaseAnimal
{
    public Cat() : base(4) { }

    public override void MakeSound() => Console.WriteLine("Meow!");
}

public class Human : BaseAnimal 
{
    public Human() : base(2) { }

    public override void MakeSound() => Console.WriteLine("Hello, world!");
}

Ở đây, chúng tôi yêu cầu tất cả mọi người BaseAnimaltạo ra âm thanh, nhưng chúng tôi chưa biết việc thực hiện nó. Trong trường hợp như vậy, chúng ta có thể trừu tượng hóa việc thực hiện giao diện và ủy thác việc thực hiện nó cho các lớp con của nó.

Một điểm cuối cùng, hãy nhớ làm thế nào trong ví dụ lớp trừu tượng chúng ta có thể hoạt động trên các thuộc tính được chia sẻ của các đối tượng khác nhau và trong ví dụ giao diện, chúng ta có thể gọi chức năng chia sẻ của các đối tượng khác nhau? Trong ví dụ cuối cùng này, chúng ta có thể làm cả hai.


7

Khi nào thích một lớp trừu tượng hơn giao diện?

  1. Nếu một người có kế hoạch cập nhật một lớp cơ sở trong suốt vòng đời của một chương trình / dự án, tốt nhất là cho phép lớp cơ sở đó là một lớp trừu tượng
  2. Nếu một người đang cố gắng xây dựng một xương sống cho các đối tượng có liên quan chặt chẽ trong một hệ thống phân cấp, thì việc sử dụng một lớp trừu tượng sẽ rất có lợi

Khi nào thích một giao diện hơn lớp trừu tượng?

  1. Nếu một người không làm việc với một loại khung phân cấp lớn, giao diện sẽ là một lựa chọn tuyệt vời
  2. Vì nhiều kế thừa không được hỗ trợ với các lớp trừu tượng (vấn đề kim cương), các giao diện có thể tiết kiệm trong ngày

Điều gì khiến bạn nghĩ rằng một câu hỏi gần một thập kỷ cần một câu trả lời thứ 22?
jonrsharpe

3
Kiểu suy nghĩ tương tự khiến tôi tìm kiếm một câu trả lời đơn giản cho câu hỏi.
Satya

1
FWIW, tôi thực sự thích câu trả lời này.
Brent Rittenhouse

6

Các lớp chỉ có thể kế thừa từ một lớp cơ sở, vì vậy nếu bạn muốn sử dụng các lớp trừu tượng để cung cấp đa hình cho một nhóm các lớp, tất cả chúng phải kế thừa từ lớp đó. Các lớp trừu tượng cũng có thể cung cấp các thành viên đã được thực hiện. Do đó, bạn có thể đảm bảo một số lượng chức năng giống hệt nhau với một lớp trừu tượng, nhưng không thể có giao diện.

Dưới đây là một số đề xuất để giúp bạn quyết định nên sử dụng giao diện hay lớp trừu tượng để cung cấp tính đa hình cho các thành phần của bạn.

  • Nếu bạn dự kiến ​​tạo nhiều phiên bản thành phần của mình, hãy tạo một lớp trừu tượng. Các lớp trừu tượng cung cấp một cách đơn giản và dễ dàng để phiên bản các thành phần của bạn. Bằng cách cập nhật lớp cơ sở, tất cả các lớp kế thừa sẽ tự động được cập nhật với thay đổi. Mặt khác, các giao diện không thể được thay đổi một khi được tạo theo cách đó. Nếu cần một phiên bản mới của giao diện, bạn phải tạo một giao diện hoàn toàn mới.
  • Nếu chức năng bạn đang tạo sẽ hữu ích trên một loạt các đối tượng khác nhau, hãy sử dụng giao diện. Các lớp trừu tượng nên được sử dụng chủ yếu cho các đối tượng có liên quan chặt chẽ, trong khi các giao diện phù hợp nhất để cung cấp chức năng chung cho các lớp không liên quan.
  • Nếu bạn đang thiết kế các bit chức năng nhỏ, súc tích, hãy sử dụng các giao diện. Nếu bạn đang thiết kế các đơn vị chức năng lớn, hãy sử dụng một lớp trừu tượng.
  • Nếu bạn muốn cung cấp chức năng chung, được triển khai trong số tất cả các triển khai thành phần của bạn, hãy sử dụng một lớp trừu tượng. Các lớp trừu tượng cho phép bạn thực hiện một phần lớp của mình, trong khi các giao diện không chứa triển khai cho bất kỳ thành viên nào.

Sao chép từ: http://msdn.microsoft.com/en-us/l Library / scsyfw1d% 28v = vs.71% 29.aspx


Không có gì trong UML loại trừ thừa kế nhiều lớp. Nhiều kế thừa được xác định bởi một ngôn ngữ lập trình, không phải bởi UML. Ví dụ, thừa kế nhiều lớp không được phép trong Java và C #, nhưng được phép trong C ++.
BobRodes

@BobRodes: Có một số tính năng mà các khung hướng đối tượng có thể cung cấp trong các kết hợp khác nhau, nhưng không phải trong tất cả các kết hợp. Đa kế thừa loại trừ một số kết hợp các tính năng hữu ích khác bao gồm khả năng truyền tham chiếu trực tiếp đến bất kỳ loại cha mẹ nào của thực thể hoặc bất kỳ loại giao diện nào được hỗ trợ qua đó và khả năng biên dịch độc lập các loại cơ sở và các loại dẫn xuất và tham gia chúng trong thời gian chạy.
supercat

@supercat Yours là một lời giải thích tốt về một số vấn đề phát sinh từ việc sử dụng nhiều kế thừa. Tuy nhiên, không có gì trong UML loại trừ việc thừa kế nhiều lớp trong một sơ đồ. Tôi đã trả lời "Các lớp có thể kế thừa chỉ từ một lớp cơ sở ..." không hoàn toàn như vậy.
BobRodes

@BobRodes: Câu hỏi đã được gắn thẻ Java. Java bao gồm các tính năng được chỉ định và do đó bị giới hạn ở các dạng thừa kế không thể tạo ra một "viên kim cương chết người" (mặc dù trên thực tế cách chúng đã triển khai các triển khai giao diện mặc định làm cho kim cương chết người có thể).
supercat

@supercat ơi, ok. Tôi thường không nhìn vào các thẻ java, vì vậy tại thời điểm tôi viết rằng tôi ít nhất nghĩ rằng tôi đang bình luận về một câu trả lời UML. Trong mọi trường hợp, tôi đồng ý với nhận xét của bạn.
BobRodes

3

Cân nhắc sử dụng các lớp trừu tượng nếu bất kỳ câu lệnh nào trong số này áp dụng cho tình huống của bạn:

  1. Bạn muốn chia sẻ mã giữa một số lớp liên quan chặt chẽ.
  2. Bạn mong đợi rằng các lớp mở rộng lớp trừu tượng của bạn có nhiều phương thức hoặc trường phổ biến hoặc yêu cầu các sửa đổi truy cập khác ngoài công khai (như được bảo vệ và riêng tư).
  3. Bạn muốn khai báo các trường không tĩnh hoặc không cuối cùng. Điều này cho phép bạn xác định các phương thức có thể truy cập và sửa đổi trạng thái của đối tượng mà chúng thuộc về.

Cân nhắc sử dụng các giao diện nếu bất kỳ câu lệnh nào trong số này áp dụng cho tình huống của bạn:

  1. Bạn mong đợi rằng các lớp không liên quan sẽ thực hiện giao diện của bạn. Ví dụ, các giao diện So sánh và Clonizable được thực hiện bởi nhiều lớp không liên quan.
  2. Bạn muốn chỉ định hành vi của một loại dữ liệu cụ thể, nhưng không quan tâm đến việc ai thực hiện hành vi của nó.
  3. Bạn muốn tận dụng nhiều kế thừa.

Nguồn


2

Các câu trả lời khác nhau giữa các ngôn ngữ. Ví dụ, trong Java, một lớp có thể thực hiện (kế thừa từ) nhiều giao diện nhưng chỉ kế thừa từ một lớp trừu tượng. Vì vậy, giao diện cho phép bạn linh hoạt hơn. Nhưng điều này không đúng trong C ++.


2

Đối với tôi, tôi sẽ đi với giao diện trong nhiều trường hợp. Nhưng tôi thích các lớp trừu tượng trong một số trường hợp.

Các lớp trong OO tướng đề cập đến việc thực hiện. Tôi sử dụng các lớp trừu tượng khi tôi muốn buộc một số chi tiết triển khai cho các đứa trẻ khác mà tôi đi với các giao diện.

Tất nhiên, các lớp trừu tượng không chỉ hữu ích trong việc ép buộc thực hiện mà còn trong việc chia sẻ một số chi tiết cụ thể giữa nhiều lớp liên quan.


1

Sử dụng một lớp trừu tượng nếu bạn muốn cung cấp một số triển khai cơ bản.


1
Cảm ơn Sebastian. Nhưng nếu tôi không cần phải thực hiện cơ bản thì sao? Sẽ không có một lớp trừu tượng và giao diện giống nhau sau đó nếu đây là sự khác biệt duy nhất giữa chúng? Tại sao lại có một sự khác biệt?
Chirantan

1
Bởi vì một số ngôn ngữ không có giao diện - C ++.
jmucchiello

1

trong java, bạn có thể kế thừa từ một lớp (trừu tượng) để "cung cấp" chức năng và bạn có thể thực hiện nhiều giao diện để "đảm bảo" chức năng


lil 'gợi ý: nếu bạn muốn kế thừa từ một lớp trừu tượng và một giao diện, hãy chắc chắn, rằng lớp trừu tượng thực hiện giao diện
Andreas Niedermair

1

Hoàn toàn dựa trên cơ sở thừa kế, bạn sẽ sử dụng Tóm tắt trong đó bạn xác định rõ ràng mối quan hệ con cháu, trừu tượng (ví dụ: động vật-> mèo) và / hoặc yêu cầu thừa kế các thuộc tính ảo hoặc không công khai, đặc biệt là trạng thái chia sẻ (mà Giao diện không thể hỗ trợ ).

Mặc dù vậy, bạn nên thử và ưu tiên thành phần (thông qua tiêm phụ thuộc) so với thừa kế ở nơi bạn có thể và lưu ý rằng Giao diện là hợp đồng hỗ trợ kiểm tra đơn vị, tách mối quan tâm và (thay đổi ngôn ngữ) theo cách thừa kế theo cách không thể tóm tắt.


1

Một vị trí thú vị nơi giao diện có giá tốt hơn các lớp trừu tượng là khi bạn cần thêm chức năng bổ sung vào một nhóm các đối tượng (liên quan hoặc không liên quan). Nếu bạn không thể cung cấp cho họ một lớp trừu tượng cơ bản (ví dụ: họ sealedđã hoặc đã có cha mẹ), bạn có thể cung cấp cho họ một giao diện giả (trống), và sau đó chỉ cần viết các phương thức mở rộng cho giao diện đó.


0

Đây có thể là một cuộc gọi rất khó thực hiện ...

Một con trỏ tôi có thể đưa ra: Một đối tượng có thể thực hiện nhiều giao diện, trong khi một đối tượng chỉ có thể kế thừa một lớp cơ sở (trong ngôn ngữ OO hiện đại như c #, tôi biết C ++ có nhiều sự kế thừa - nhưng không phải vì thế mà sao?)


Đa kế thừa cho phép Mixin được triển khai một cách dường như, Mixin được viết tốt rất dễ để làm việc nhưng rất khó để thực hiện và khó viết mà không bị rơi ở đâu đó. Mixin khá tuyệt khi nói chung là IMO.
Martijn Laarman

Trên thực tế tôi đã không, nhiều sự kế thừa thực sự là một tia lửa tranh luận chắc chắn giữa chúng tôi, tôi thấy hoàn toàn không có lý do gì để hạ bệ. Trong thực tế, tôi nêu lên câu trả lời của bạn.
Martijn Laarman

Điểm duy nhất tôi đã cố gắng thực hiện là các cách để Mixin trong các ngôn ngữ với Kế thừa duy nhất cũng có thể (C #, PHP, javascript) nhưng hành vi hacky hoặc cú pháp khó hiểu. Tôi yêu Mixin khi họ làm việc nhưng tôi vẫn không quyết đoán về việc có nên thừa kế hay không.
Martijn Laarman

Câu trả lời này là một sự khác biệt cú pháp hơn là một sự khác biệt thiết kế. Tôi nghĩ anh ấy đang yêu cầu một sự khác biệt về thiết kế
Pramod Setlur

0

Một lớp trừu tượng có thể có triển khai.

Một giao diện không có triển khai, nó chỉ đơn giản là xác định một loại hợp đồng.

Cũng có thể có một số khác biệt phụ thuộc vào ngôn ngữ: ví dụ C # không có nhiều kế thừa, nhưng nhiều giao diện có thể được thực hiện trong một lớp.


Khi bạn nói, "một loại hợp đồng", bạn có nghĩa là như trong các dịch vụ web?
Chirantan

Về mặt kỹ thuật, các dịch vụ web không hoạt động với giao diện. Với hợp đồng tôi có nghĩa là người dùng của một đối tượng biết phương thức nào có mặt trên đối tượng đó. Ví dụ: giao diện IMouse sẽ có phương thức Move và sự kiện nút chuột trái và phải.
Gerrie Schenck

-1

Quy tắc ngón tay cái cơ bản là: Đối với "Danh từ", hãy sử dụng lớp Tóm tắt và cho "Động từ" sử dụng giao diện

Ví dụ: carlà một lớp trừu tượng và drive, chúng ta có thể biến nó thành một giao diện.


5
Điều này không có ý nghĩa gì, chúng ta cũng có thể đặt chức năng của drivetrong xe - đó là một lớp trừu tượng.
Arslan Ali
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.