Nó có ý nghĩa gì với chương trình trên một giao diện


814

Tôi đã thấy điều này được đề cập một vài lần và tôi không rõ ý nghĩa của nó. Khi nào và tại sao bạn sẽ làm điều này?

Tôi biết những gì giao diện làm, nhưng thực tế tôi không rõ ràng về điều này làm cho tôi nghĩ rằng tôi đang bỏ lỡ việc sử dụng chúng một cách chính xác.

Có phải chỉ là như vậy nếu bạn phải làm:

IInterface classRef = new ObjectWhatever()

Bạn có thể sử dụng bất kỳ lớp nào thực hiện IInterface? Khi nào bạn cần làm điều đó? Điều duy nhất tôi có thể nghĩ là nếu bạn có một phương thức và bạn không chắc chắn về đối tượng nào sẽ được thông qua ngoại trừ việc nó thực hiện IInterface. Tôi không thể nghĩ mức độ thường xuyên bạn sẽ cần phải làm điều đó.

Ngoài ra, làm thế nào bạn có thể viết một phương thức trong một đối tượng thực hiện giao diện? Điều đó có thể không?


3
Nếu bạn có thể nhớ và chương trình của bạn cần phải tối ưu, ngay trước khi biên dịch, bạn có thể muốn trao đổi khai báo Giao diện để thực hiện thực tế. Khi sử dụng một giao diện sẽ thêm một mức độ gián tiếp mang lại hiệu quả. Phân phối mã của bạn được lập trình cho các giao diện mặc dù ...
Ande Turner

18
@Ande Turner: đó là lời khuyên tồi. 1). "Chương trình của bạn cần phải tối ưu" không phải là lý do chính đáng để tráo đổi giao diện! Sau đó, bạn nói "Phân phối mã của bạn được lập trình cho các giao diện mặc dù ..." vì vậy bạn đang khuyên rằng yêu cầu được đưa ra (1) sau đó bạn phát hành mã tối ưu phụ?!?
Mitch Wheat

74
Hầu hết các câu trả lời ở đây không hoàn toàn đúng. Nó hoàn toàn không có nghĩa hay thậm chí ngụ ý "sử dụng từ khóa giao diện". Giao diện là một thông số kỹ thuật về cách sử dụng một cái gì đó - đồng nghĩa với hợp đồng (tra cứu nó). Khác với đó là việc thực hiện, đó là cách thực hiện hợp đồng đó. Chương trình chỉ chống lại sự đảm bảo của phương thức / loại để khi phương thức / loại được thay đổi theo cách vẫn tuân theo hợp đồng, nó không phá vỡ mã bằng cách sử dụng nó.
jyoungdev

2
@ apollodude217 đó thực sự là câu trả lời tốt nhất trên toàn bộ trang. Ít nhất là cho câu hỏi trong tiêu đề, vì có ít nhất 3 câu hỏi khá khác nhau ở đây ...
Andrew Spencer

4
Vấn đề cơ bản với các câu hỏi như thế này là nó giả định rằng "lập trình cho giao diện" có nghĩa là "bọc mọi thứ trong một giao diện trừu tượng", thật ngớ ngẩn nếu bạn xem xét thuật ngữ này trước khái niệm giao diện trừu tượng kiểu Java.
Jonathan Allen

Câu trả lời:


1634

Có một số câu trả lời tuyệt vời ở đây cho câu hỏi này có được tất cả các loại chi tiết tuyệt vời về giao diện và mã ghép lỏng lẻo, đảo ngược điều khiển và vân vân. Có một số cuộc thảo luận khá sôi nổi, vì vậy tôi muốn tận dụng cơ hội để phá vỡ mọi thứ một chút để hiểu tại sao một giao diện lại hữu ích.

Khi tôi lần đầu tiên bắt đầu tiếp xúc với các giao diện, tôi cũng bối rối về sự liên quan của chúng. Tôi không hiểu tại sao bạn cần chúng. Nếu chúng ta đang sử dụng một ngôn ngữ như Java hoặc C #, chúng ta đã có sự kế thừa và tôi đã xem các giao diện như một hình thức thừa kế và suy nghĩ yếu hơn , "tại sao phải bận tâm?" Theo một nghĩa nào đó, tôi đã đúng, bạn có thể nghĩ các giao diện là một dạng thừa kế yếu, nhưng cuối cùng tôi đã hiểu cách sử dụng chúng như một cấu trúc ngôn ngữ bằng cách nghĩ về chúng như một phương tiện phân loại các đặc điểm hoặc hành vi phổ biến được thể hiện bởi có khả năng nhiều lớp không liên quan của các đối tượng.

Ví dụ: giả sử bạn có trò chơi SIM và có các lớp sau:

class HouseFly inherits Insect {
    void FlyAroundYourHead(){}
    void LandOnThings(){}
}

class Telemarketer inherits Person {
    void CallDuringDinner(){}
    void ContinueTalkingWhenYouSayNo(){}
}

Rõ ràng, hai đối tượng này không có gì chung về sự kế thừa trực tiếp. Nhưng, bạn có thể nói rằng cả hai đều gây phiền nhiễu.

Hãy nói rằng trò chơi của chúng tôi cần phải có một số loại ngẫu nhiên điều mà làm phiền người chơi trò chơi khi họ ăn bữa tối. Đây có thể là một HouseFlyhoặc Telemarketercả hai - nhưng làm thế nào để bạn cho phép cả hai với một chức năng duy nhất? Và làm thế nào để bạn yêu cầu mỗi loại đối tượng khác nhau "làm điều khó chịu của họ" theo cùng một cách?

Chìa khóa để nhận ra là cả a TelemarketerHouseFlychia sẻ một hành vi được giải thích một cách lỏng lẻo mặc dù chúng không giống nhau về mặt mô hình hóa chúng. Vì vậy, hãy tạo một giao diện mà cả hai có thể thực hiện:

interface IPest {
    void BeAnnoying();
}

class HouseFly inherits Insect implements IPest {
    void FlyAroundYourHead(){}
    void LandOnThings(){}

    void BeAnnoying() {
        FlyAroundYourHead();
        LandOnThings();
    }
}

class Telemarketer inherits Person implements IPest {
    void CallDuringDinner(){}
    void ContinueTalkingWhenYouSayNo(){}

    void BeAnnoying() {
        CallDuringDinner();
        ContinueTalkingWhenYouSayNo();
    }
}

Bây giờ chúng ta có hai lớp mà mỗi lớp có thể gây phiền nhiễu theo cách riêng của chúng. Và họ không cần xuất phát từ cùng một lớp cơ sở và chia sẻ các đặc điểm vốn có chung - họ chỉ cần thỏa mãn hợp đồng IPest- hợp đồng đó rất đơn giản. Bạn chỉ cần có BeAnnoying. Về vấn đề này, chúng ta có thể mô hình hóa như sau:

class DiningRoom {

    DiningRoom(Person[] diningPeople, IPest[] pests) { ... }

    void ServeDinner() {
        when diningPeople are eating,

        foreach pest in pests
        pest.BeAnnoying();
    }
}

Ở đây chúng tôi có một phòng ăn chấp nhận một số thực khách và một số loài gây hại - lưu ý việc sử dụng giao diện. Điều này có nghĩa là trong thế giới nhỏ bé của chúng ta, một thành viên của pestsmảng thực sự có thể là một Telemarketerđối tượng hoặc một HouseFlyđối tượng.

Các ServeDinnerphương pháp được gọi khi ăn tối được phục vụ và nhân dân ta trong phòng ăn có nghĩa vụ phải ăn. Trong trò chơi nhỏ của chúng tôi, đó là khi sâu bệnh của chúng tôi thực hiện công việc của chúng - mỗi loài gây hại được hướng dẫn là gây phiền nhiễu bằng cách IPestgiao diện. Theo cách này, chúng ta có thể dễ dàng có cả hai TelemarketersHouseFlysgây phiền nhiễu theo từng cách riêng của mình - chúng tôi chỉ quan tâm rằng chúng tôi có một thứ gì đó trong DiningRoomđối tượng là dịch hại, chúng tôi không thực sự quan tâm đó là gì và chúng không thể có gì trong chung với người khác.

Ví dụ mã giả rất giả tạo này (kéo dài hơn nhiều so với tôi dự đoán) chỉ đơn giản là để minh họa cho loại thứ cuối cùng bật đèn cho tôi khi chúng ta có thể sử dụng giao diện. Tôi xin lỗi trước vì sự khôn ngoan của ví dụ, nhưng hy vọng rằng nó sẽ giúp bạn hiểu. Và, để chắc chắn, các câu trả lời được đăng khác mà bạn nhận được ở đây thực sự bao hàm toàn bộ việc sử dụng giao diện ngày nay trong các mẫu thiết kế và phương pháp phát triển.


3
Một điều khác cần xem xét là trong một số trường hợp, có thể hữu ích khi có giao diện cho những thứ "có thể" gây phiền nhiễu và có nhiều đối tượng thực hiện BeAnnoyingdưới dạng không hoạt động; giao diện này có thể tồn tại ở vị trí của, hoặc bổ sung cho, giao diện cho những điều khó chịu (nếu cả hai giao diện tồn tại, "điều mà rất khó chịu" giao diện nên kế thừa từ "những điều mà có thể gây phiền nhiễu" giao diện). Nhược điểm của việc sử dụng các giao diện như vậy là việc triển khai có thể trở nên nặng nề hơn khi thực hiện một số phương thức sơ khai "gây phiền nhiễu". Ưu điểm là ...
supercat

4
Các phương thức không nhằm thể hiện các phương thức trừu tượng - việc thực hiện chúng không liên quan đến câu hỏi tập trung vào các giao diện.
Peter Meyer

33
Các hành vi đóng gói, chẳng hạn như IPest được gọi là mô hình chiến lược chỉ trong trường hợp bất kỳ ai quan tâm đến việc theo dõi nhiều tài liệu hơn về chủ đề đó ...
nckbrz

9
Thật thú vị, bạn không chỉ ra rằng vì các đối tượng trong các IPest[]tham chiếu IPest, bạn có thể gọi BeAnnoying()vì chúng có phương thức đó, trong khi bạn không thể gọi các phương thức khác mà không cần truyền. Tuy nhiên, mỗi đối tượng BeAnnoying()phương thức riêng sẽ được gọi.
D. Ben Knoble

4
Giải thích rất hay ... Tôi chỉ cần nói ở đây: Tôi chưa bao giờ nghe nói giao diện là một loại cơ chế thừa kế lỏng lẻo, nhưng thay vào đó tôi biết rằng kế thừa được sử dụng như một cơ chế kém để xác định giao diện (ví dụ, trong Python thông thường, bạn Do đó tất cả các thời gian).
Carlos H Romano

283

Ví dụ cụ thể tôi từng đưa ra cho sinh viên là họ nên viết

List myList = new ArrayList(); // programming to the List interface

thay vì

ArrayList myList = new ArrayList(); // this is bad

Chúng trông giống hệt nhau trong một chương trình ngắn, nhưng nếu bạn tiếp tục sử dụng myList100 lần trong chương trình của mình, bạn có thể bắt đầu thấy sự khác biệt. Khai báo đầu tiên đảm bảo rằng bạn chỉ gọi các phương thức trên myListđược xác định bởi Listgiao diện (vì vậy không có ArrayListphương thức cụ thể nào ). Nếu bạn đã lập trình giao diện theo cách này, sau này bạn có thể quyết định rằng bạn thực sự cần

List myList = new TreeList();

và bạn chỉ phải thay đổi mã của bạn ở một điểm đó. Bạn đã biết rằng phần còn lại của mã của bạn sẽ không làm bất cứ điều gì sẽ bị phá vỡ bằng cách thay đổi việc triển khai vì bạn đã lập trình cho giao diện .

Những lợi ích thậm chí còn rõ ràng hơn (tôi nghĩ) khi bạn nói về các tham số phương thức và trả về giá trị. Lấy ví dụ này:

public ArrayList doSomething(HashMap map);

Phương pháp khai báo đó liên kết bạn với hai triển khai cụ thể ( ArrayListHashMap). Ngay khi phương thức đó được gọi từ mã khác, mọi thay đổi đối với các loại đó có thể có nghĩa là bạn cũng sẽ phải thay đổi mã gọi. Nó sẽ tốt hơn để lập trình cho các giao diện.

public List doSomething(Map map);

Bây giờ không quan trọng Listbạn trở về loại nào, hoặc loại nào Mapđược truyền vào dưới dạng tham số. Những thay đổi bạn thực hiện bên trong doSomethingphương thức sẽ không buộc bạn thay đổi mã cuộc gọi.


Bình luận không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
Yvette

Giải thích rất rõ ràng. Rất hữu ích
samuel luswata

Tôi có một câu hỏi về lý do bạn đề cập "Khai báo đầu tiên đảm bảo rằng bạn chỉ gọi các phương thức trên myList được xác định bởi giao diện Danh sách (vì vậy không có phương thức cụ thể ArrayList nào). Nếu bạn đã lập trình cho giao diện theo cách này, sau này bạn có thể quyết định rằng bạn thực sự cần List myList = new TreeList (); và bạn chỉ phải thay đổi mã của mình ở vị trí đó. " Có thể tôi đã hiểu nhầm, tôi tự hỏi tại sao bạn cần thay đổi ArrayList thành TreeList nếu bạn muốn "đảm bảo rằng bạn chỉ gọi các phương thức trên myList"?
dùng4954901

1
@ user3014901 Có bất kỳ lý do nào bạn có thể muốn thay đổi loại danh sách bạn đang sử dụng. Một người có thể có hiệu suất tra cứu tốt hơn, ví dụ. Vấn đề là, nếu bạn lập trình lên giao diện Danh sách, việc thay đổi mã của bạn thành một triển khai khác sau này sẽ dễ dàng hơn.
Bill the Lizard

73

Lập trình cho một giao diện có nội dung: "Tôi cần chức năng này và tôi không quan tâm nó đến từ đâu."

Xem xét (trong Java), Listgiao diện so với ArrayListLinkedListcác lớp cụ thể. Nếu tất cả những gì tôi quan tâm là tôi có cấu trúc dữ liệu chứa nhiều mục dữ liệu mà tôi nên truy cập thông qua phép lặp, tôi sẽ chọn một List(và đó là 99% thời gian). Nếu tôi biết rằng tôi cần chèn / xóa liên tục từ một trong hai đầu của danh sách, tôi có thể chọn LinkedListtriển khai cụ thể (hoặc nhiều khả năng hơn, sử dụng giao diện Hàng đợi ). Nếu tôi biết tôi cần truy cập ngẫu nhiên theo chỉ mục, tôi sẽ chọn ArrayListlớp cụ thể.


1
hoàn toàn đồng ý tức là sự độc lập giữa những gì được thực hiện so với cách nó được thực hiện. Bằng cách phân vùng hệ thống cùng các thành phần độc lập, bạn kết thúc với một hệ thống mà nó là đơn giản và tái sử dụng (xem Simple Made Easy bởi người đã tạo ra Clojure)
beluchin

38

Sử dụng các giao diện là một yếu tố quan trọng trong việc làm cho mã của bạn dễ dàng kiểm tra ngoài việc loại bỏ các khớp nối không cần thiết giữa các lớp của bạn. Bằng cách tạo giao diện xác định các hoạt động trên lớp của bạn, bạn cho phép các lớp muốn sử dụng chức năng đó khả năng sử dụng nó mà không phụ thuộc trực tiếp vào lớp triển khai của bạn. Nếu sau này bạn quyết định thay đổi và sử dụng một triển khai khác, bạn chỉ cần thay đổi một phần của mã nơi triển khai được khởi tạo. Phần còn lại của mã không cần thay đổi vì nó phụ thuộc vào giao diện chứ không phải lớp triển khai.

Điều này rất hữu ích trong việc tạo các bài kiểm tra đơn vị. Trong lớp được kiểm tra, bạn có nó phụ thuộc vào giao diện và đưa một thể hiện của giao diện vào lớp (hoặc một nhà máy cho phép nó xây dựng các thể hiện của giao diện khi cần) thông qua hàm tạo hoặc bộ giải quyết thuộc tính. Lớp sử dụng giao diện được cung cấp (hoặc được tạo) trong các phương thức của nó. Khi bạn đi viết bài kiểm tra của mình, bạn có thể giả định hoặc giả mạo giao diện và cung cấp giao diện phản hồi với dữ liệu được định cấu hình trong bài kiểm tra đơn vị của bạn. Bạn có thể làm điều này bởi vì lớp của bạn dưới bài kiểm tra chỉ giao dịch với giao diện, không phải việc triển khai cụ thể của bạn. Bất kỳ lớp nào thực hiện giao diện, bao gồm cả lớp giả hoặc lớp giả của bạn, sẽ làm.

EDIT: Dưới đây là một liên kết đến một bài viết trong đó Erich Gamma thảo luận về câu nói của mình, "Chương trình cho một giao diện, không phải là một triển khai."

http://www.artima.com/lejava/articles/designprinciples.html


3
Xin vui lòng, đọc lại cuộc phỏng vấn này: Gamma tất nhiên đã nói về khái niệm giao diện OO, không phải là loại lớp học đặc biệt JAVA hoặc C # (IS Something). Vấn đề là, hầu hết mọi người mặc dù anh ta đang nói về từ khóa, vì vậy chúng ta hiện có rất nhiều giao diện không cần thiết (IS Something).
Sylvain

Phỏng vấn rất tốt. Xin hãy cẩn thận cho những độc giả tương lai, có bốn trang trong cuộc phỏng vấn. Tôi gần như sẽ đóng trình duyệt trước khi nhìn thấy nó.
Quảng cáo Infinitum

38

Lập trình cho một giao diện hoàn toàn không có gì để làm với các giao diện trừu tượng như chúng ta thấy trong Java hoặc .NET. Nó thậm chí không phải là một khái niệm OOP.

Điều đó có nghĩa là đừng đi loanh quanh với phần bên trong của một đối tượng hoặc cấu trúc dữ liệu. Sử dụng Giao diện chương trình trừu tượng hoặc API để tương tác với dữ liệu của bạn. Trong Java hoặc C # có nghĩa là sử dụng các thuộc tính và phương thức công khai thay vì truy cập trường thô. Đối với C có nghĩa là sử dụng các hàm thay vì con trỏ thô.

EDIT: Và với cơ sở dữ liệu, điều đó có nghĩa là sử dụng các khung nhìn và các thủ tục được lưu trữ thay vì truy cập bảng trực tiếp.


5
Câu trả lời tốt nhất. Gamma đưa ra một lời giải thích tương tự ở đây: artima.com/lejava/articles/designprinciples.html (xem trang 2). Anh ta đang đề cập đến khái niệm OO nhưng bạn đã đúng: nó lớn hơn thế.
Sylvain

36

Bạn nên xem xét Inversion of Control:

Trong trường hợp như vậy, bạn sẽ không viết điều này:

IInterface classRef = new ObjectWhatever();

Bạn sẽ viết một cái gì đó như thế này:

IInterface classRef = container.Resolve<IInterface>();

Điều này sẽ đi vào một thiết lập dựa trên quy tắc trong container đối tượng và xây dựng đối tượng thực tế cho bạn, có thể là ObjectWhthing. Điều quan trọng là bạn có thể thay thế quy tắc này bằng một thứ đã sử dụng hoàn toàn một loại đối tượng khác và mã của bạn vẫn hoạt động.

Nếu chúng tôi rời khỏi IoC, bạn có thể viết mã biết rằng nó có thể nói chuyện với một đối tượng thực hiện điều gì đó cụ thể , nhưng không phải loại đối tượng nào hoặc cách thức thực hiện.

Điều này sẽ có ích khi truyền tham số.

Đối với câu hỏi được ngoặc đơn của bạn "Ngoài ra, làm thế nào bạn có thể viết một phương thức lấy một đối tượng thực hiện Giao diện? Điều đó có thể không?", Trong C #, bạn chỉ cần sử dụng loại giao diện cho loại tham số, như sau:

public void DoSomethingToAnObject(IInterface whatever) { ... }

Điều này cắm ngay vào "nói chuyện với một đối tượng làm một cái gì đó cụ thể." Phương thức được xác định ở trên biết những gì mong đợi từ đối tượng, rằng nó thực hiện mọi thứ trong IInterface, nhưng nó không quan tâm nó thuộc loại đối tượng nào, chỉ có điều nó tuân thủ hợp đồng, đó là giao diện là gì.

Ví dụ, bạn có thể quen thuộc với máy tính và có thể đã sử dụng khá nhiều trong những ngày của bạn, nhưng hầu hết thời gian đều khác nhau. Mặt khác, bạn biết máy tính tiêu chuẩn sẽ hoạt động như thế nào, vì vậy bạn có thể sử dụng tất cả chúng, ngay cả khi bạn không thể sử dụng các tính năng cụ thể mà mỗi máy tính có mà không có máy tính nào khác có.

Đây là vẻ đẹp của giao diện. Bạn có thể viết một đoạn mã, biết rằng nó sẽ đưa các đối tượng được truyền cho nó mà nó có thể mong đợi một số hành vi nhất định từ đó. Nó không quan tâm đến việc họ là loại đối tượng nào, chỉ có điều nó hỗ trợ hành vi cần thiết.

Hãy để tôi cho bạn một ví dụ cụ thể.

Chúng tôi có một hệ thống dịch được xây dựng tùy chỉnh cho các hình thức cửa sổ. Hệ thống này lặp lại thông qua các điều khiển trên một biểu mẫu và dịch văn bản trong mỗi điều khiển. Hệ thống biết cách xử lý các điều khiển cơ bản, như kiểu thuộc tính của điều khiển-có-có-một-văn bản và các công cụ cơ bản tương tự, nhưng đối với bất kỳ điều gì cơ bản, nó đều bị thiếu.

Bây giờ, do các điều khiển kế thừa từ các lớp được xác định trước mà chúng ta không có quyền kiểm soát, chúng ta có thể thực hiện một trong ba điều sau:

  1. Xây dựng hỗ trợ cho hệ thống dịch thuật của chúng tôi để phát hiện cụ thể loại điều khiển nào đang hoạt động và dịch các bit chính xác (cơn ác mộng bảo trì)
  2. Xây dựng hỗ trợ thành các lớp cơ sở (không thể, vì tất cả các điều khiển kế thừa từ các lớp được xác định trước khác nhau)
  3. Thêm hỗ trợ giao diện

Vì vậy, chúng tôi đã làm nr. 3. Tất cả các điều khiển của chúng tôi đều triển khai ILocalizable, đây là giao diện cung cấp cho chúng tôi một phương thức, khả năng dịch "chính nó" thành một thùng chứa văn bản / quy tắc dịch thuật. Như vậy, biểu mẫu không cần biết loại điều khiển nào đã tìm thấy, chỉ có điều nó thực hiện giao diện cụ thể và biết rằng có một phương thức mà nó có thể gọi để bản địa hóa điều khiển.


31
Tại sao lại đề cập đến IoC ngay từ đầu vì điều này sẽ chỉ gây thêm nhầm lẫn.
Kevin Le - Khnle

1
Đồng ý, tôi sẽ nói lập trình chống lại các giao diện chỉ là một kỹ thuật để làm cho IoC dễ dàng và đáng tin cậy hơn.
terjetyl

28

Mã cho giao diện Không phải việc triển khai KHÔNG CÓ gì để làm với Java, cũng như cấu trúc Giao diện của nó.

Khái niệm này đã được đưa ra để nổi bật trong các cuốn sách Mẫu / Gang of Four nhưng có lẽ hầu như xung quanh trước đó. Khái niệm này chắc chắn đã tồn tại trước khi Java tồn tại.

Cấu trúc Giao diện Java được tạo ra để hỗ trợ cho ý tưởng này (trong số những thứ khác) và mọi người đã quá tập trung vào cấu trúc như là trung tâm của ý nghĩa thay vì mục đích ban đầu. Tuy nhiên, đó là lý do chúng tôi có các phương thức và thuộc tính công khai và riêng tư trong Java, C ++, C #, v.v.

Nó có nghĩa là chỉ tương tác với một đối tượng hoặc giao diện chung của hệ thống. Đừng lo lắng hoặc thậm chí dự đoán cách nó làm những gì nó làm trong nội bộ. Đừng lo lắng về cách nó được thực hiện. Trong mã hướng đối tượng, đó là lý do tại sao chúng ta có các phương thức / thuộc tính công khai so với riêng tư. Chúng tôi dự định sử dụng các phương thức công khai vì các phương thức riêng tư chỉ có để sử dụng nội bộ, trong lớp. Chúng tạo nên việc thực hiện lớp và có thể được thay đổi theo yêu cầu mà không thay đổi giao diện chung. Giả sử rằng liên quan đến chức năng, một phương thức trên một lớp sẽ thực hiện cùng một hoạt động với cùng kết quả mong đợi mỗi khi bạn gọi nó với cùng tham số. Nó cho phép tác giả thay đổi cách lớp hoạt động, cách thực hiện của nó, mà không phá vỡ cách mọi người tương tác với nó.

Và bạn có thể lập trình cho giao diện, không phải thực hiện mà không cần sử dụng cấu trúc Giao diện. Bạn có thể lập trình cho giao diện không phải là triển khai trong C ++, không có cấu trúc Giao diện. Bạn có thể tích hợp hai hệ thống doanh nghiệp lớn mạnh mẽ hơn miễn là chúng tương tác thông qua các giao diện công cộng (hợp đồng) thay vì gọi các phương thức trên các đối tượng bên trong hệ thống. Các giao diện được dự kiến ​​sẽ luôn phản ứng theo cùng một cách dự kiến ​​với cùng các tham số đầu vào; nếu thực hiện với giao diện và không thực hiện. Khái niệm này hoạt động ở nhiều nơi.

Lắc suy nghĩ rằng Giao diện Java có bất cứ điều gì liên quan đến khái niệm 'Chương trình cho Giao diện, Không phải Triển khai'. Họ có thể giúp áp dụng khái niệm này, nhưng chúng không phải là khái niệm.


1
Câu đầu tiên nói lên tất cả. Đây phải là câu trả lời được chấp nhận.
madumlao

14

Nghe có vẻ như bạn hiểu cách các giao diện hoạt động nhưng không chắc chắn khi nào nên sử dụng chúng và những lợi thế mà chúng mang lại. Dưới đây là một vài ví dụ về thời điểm giao diện có ý nghĩa:

// if I want to add search capabilities to my application and support multiple search
// engines such as Google, Yahoo, Live, etc.

interface ISearchProvider
{
    string Search(string keywords);
}

sau đó tôi có thể tạo GoogleSearchProvider, YahooSearchProvider, LiveSearchProvider, v.v.

// if I want to support multiple downloads using different protocols
// HTTP, HTTPS, FTP, FTPS, etc.
interface IUrlDownload
{
    void Download(string url)
}

// how about an image loader for different kinds of images JPG, GIF, PNG, etc.
interface IImageLoader
{
    Bitmap LoadImage(string filename)
}

sau đó tạo JpegImageLoader, GifImageLoader, PngImageLoader, v.v.

Hầu hết các hệ thống bổ trợ và plugin hoạt động ngoài giao diện.

Một cách sử dụng phổ biến khác là cho mẫu Kho lưu trữ. Nói rằng tôi muốn tải danh sách mã zip từ các nguồn khác nhau

interface IZipCodeRepository
{
    IList<ZipCode> GetZipCodes(string state);
}

sau đó tôi có thể tạo một XMLZipCodeRep repository, SQLZipCodeRep repository, CSVZipCodeRep repository, v.v. Khi cơ sở dữ liệu đã sẵn sàng, tôi viết SQLRep repository để thay thế phiên bản XML. Phần còn lại của mã của tôi vẫn không thay đổi vì nó chỉ chạy khỏi giao diện.

Các phương thức có thể chấp nhận các giao diện như:

PrintZipCodes(IZipCodeRepository zipCodeRepository, string state)
{
    foreach (ZipCode zipCode in zipCodeRepository.GetZipCodes(state))
    {
        Console.WriteLine(zipCode.ToString());
    }
}

10

Nó làm cho mã của bạn mở rộng hơn rất nhiều và dễ bảo trì hơn khi bạn có các bộ các lớp tương tự. Tôi là một lập trình viên cơ sở, vì vậy tôi không phải là chuyên gia, nhưng tôi vừa hoàn thành một dự án đòi hỏi một cái gì đó tương tự.

Tôi làm việc trên phần mềm phía máy khách nói chuyện với một máy chủ đang chạy một thiết bị y tế. Chúng tôi đang phát triển một phiên bản mới của thiết bị này có một số thành phần mới mà khách hàng phải cấu hình đôi khi. Có hai loại thành phần mới, và chúng khác nhau, nhưng chúng cũng rất giống nhau. Về cơ bản, tôi phải tạo hai biểu mẫu cấu hình, hai lớp danh sách, hai thứ.

Tôi quyết định rằng tốt nhất là tạo một lớp cơ sở trừu tượng cho từng loại điều khiển chứa gần như tất cả logic thực, và sau đó các kiểu dẫn xuất để quan tâm đến sự khác biệt giữa hai thành phần. Tuy nhiên, các lớp cơ sở sẽ không thể thực hiện các thao tác trên các thành phần này nếu tôi phải lo lắng về các loại mọi lúc (tốt, chúng có thể có, nhưng sẽ có một câu lệnh "if" hoặc chuyển đổi trong mọi phương thức) .

Tôi đã định nghĩa một giao diện đơn giản cho các thành phần này và tất cả các lớp cơ sở nói về giao diện này. Bây giờ khi tôi thay đổi một cái gì đó, nó gần như 'chỉ hoạt động' ở mọi nơi và tôi không có sự sao chép mã.


10

Có rất nhiều lời giải thích ngoài kia, nhưng để làm cho nó thậm chí còn đơn giản hơn. Lấy ví dụ a List. Người ta có thể thực hiện một danh sách với:

  1. Một mảng nội bộ
  2. Một danh sách liên kết
  3. Thực hiện khác

Bằng cách xây dựng một giao diện, nói a List. Bạn chỉ viết mã theo định nghĩa của Danh sách hoặc Listcó nghĩa là gì trong thực tế.

Bạn có thể sử dụng bất kỳ loại thực hiện nào trong nội bộ nói rằng arrayviệc thực hiện. Nhưng giả sử bạn muốn thay đổi việc thực hiện vì một số lý do nói lỗi hoặc hiệu suất. Sau đó, bạn chỉ cần thay đổi khai báo List<String> ls = new ArrayList<String>()thành List<String> ls = new LinkedList<String>().

Không ở đâu khác trong mã, bạn sẽ phải thay đổi bất cứ điều gì khác; Bởi vì mọi thứ khác được xây dựng trên định nghĩa của List.


8

Nếu bạn lập trình bằng Java, JDBC là một ví dụ tốt. JDBC định nghĩa một tập các giao diện nhưng không nói gì về việc thực hiện. Các ứng dụng của bạn có thể được viết dựa trên bộ giao diện này. Về lý thuyết, bạn chọn một số trình điều khiển JDBC và ứng dụng của bạn sẽ hoạt động. Nếu bạn phát hiện ra trình điều khiển JDBC nhanh hơn hoặc "tốt hơn" hoặc rẻ hơn hoặc vì bất kỳ lý do gì, về lý thuyết, bạn có thể định cấu hình lại tệp thuộc tính của mình và không phải thực hiện bất kỳ thay đổi nào trong ứng dụng của mình, ứng dụng của bạn vẫn hoạt động.


Nó không chỉ hữu ích trong trường hợp trình điều khiển tốt hơn có sẵn, nó có thể thay đổi hoàn toàn các nhà cung cấp cơ sở dữ liệu.
Ken Liu

3
JDBC tệ đến mức cần phải thay thế. Tìm một ví dụ khác.
Joshua

JDBC là xấu nhưng không vì bất kỳ lý do nào để làm với giao diện so với triển khai hoặc mức độ trừu tượng. Và vì vậy để minh họa khái niệm trong câu hỏi, nó chỉ là hoàn hảo.
Erwin Smout

8

Lập trình để giao diện là tuyệt vời, nó thúc đẩy khớp nối lỏng lẻo. Như @lassevk đã đề cập, Inversion of Control là một công dụng tuyệt vời của việc này.

Ngoài ra, hãy nhìn vào các hiệu trưởng RẮN .đây là một loạt video

Nó đi qua một mã hóa cứng (ví dụ được kết hợp mạnh mẽ) sau đó xem xét các giao diện, cuối cùng tiến tới một công cụ IoC / DI (NInject)


7

Tôi là một người đến muộn cho câu hỏi này, nhưng tôi muốn đề cập ở đây rằng dòng "Chương trình cho một giao diện, không phải là một triển khai" đã có một số thảo luận tốt trong cuốn sách Các mẫu thiết kế của GoF (Gang of Four).

Nó nêu, trên p. 18:

Chương trình cho một giao diện, không phải là một triển khai

Đừng khai báo các biến là các thể hiện của các lớp cụ thể. Thay vào đó, chỉ cam kết với một giao diện được xác định bởi một lớp trừu tượng. Bạn sẽ thấy đây là một chủ đề phổ biến của các mẫu thiết kế trong cuốn sách này.

và trên đó, nó bắt đầu với:

Có hai lợi ích khi thao tác các đối tượng chỉ theo giao diện được xác định bởi các lớp trừu tượng:

  1. Khách hàng vẫn không biết về các loại đối tượng cụ thể mà họ sử dụng, miễn là các đối tượng tuân thủ giao diện mà khách hàng mong đợi.
  2. Khách hàng vẫn không biết về các lớp thực hiện các đối tượng này. Khách hàng chỉ biết về (các) lớp trừu tượng xác định giao diện.

Vì vậy, nói cách khác, đừng viết nó cho các lớp của bạn để nó có một quack()phương thức cho vịt, và sau đó là một bark()phương thức cho chó, vì chúng quá cụ thể cho việc triển khai cụ thể của một lớp (hoặc lớp con). Thay vào đó, hãy viết phương thức sử dụng các tên đủ chung để sử dụng trong lớp cơ sở, chẳng hạn như giveSound()hoặc move(), để chúng có thể được sử dụng cho vịt, chó hoặc thậm chí là ô tô, và sau đó khách hàng của lớp bạn chỉ có thể nói .giveSound()thay vì suy nghĩ về việc nên sử dụng quack()hoặc bark()thậm chí xác định loại trước khi đưa ra thông điệp chính xác được gửi đến đối tượng.


6

Ngoài câu trả lời đã được chọn (và các bài viết thông tin khác nhau ở đây), tôi rất khuyên bạn nên lấy một bản sao của Mẫu thiết kế đầu tiên . Nó rất dễ đọc và sẽ trả lời trực tiếp câu hỏi của bạn, giải thích tại sao nó quan trọng và cho bạn thấy nhiều mẫu lập trình bạn có thể sử dụng để sử dụng nguyên tắc đó (và các nguyên tắc khác).


5

Để thêm vào các bài đăng hiện có, đôi khi mã hóa cho các giao diện giúp cho các dự án lớn khi các nhà phát triển làm việc trên các thành phần riêng biệt cùng một lúc. Tất cả những gì bạn cần là xác định giao diện trả trước và viết mã cho chúng trong khi các nhà phát triển khác viết mã vào giao diện bạn đang triển khai.


4

Nó cũng tốt cho Kiểm thử đơn vị, bạn có thể đưa các lớp của riêng mình (đáp ứng các yêu cầu của giao diện) vào một lớp phụ thuộc vào nó


4

Nó có thể thuận lợi để lập trình cho các giao diện, ngay cả khi chúng ta không phụ thuộc vào trừu tượng.

Lập trình cho các giao diện buộc chúng ta phải sử dụng một tập hợp con phù hợp theo ngữ cảnh của một đối tượng . Điều đó giúp vì nó:

  1. ngăn chúng ta làm những việc không phù hợp theo ngữ cảnh và
  2. cho phép chúng tôi thay đổi một cách an toàn việc thực hiện trong tương lai.

Ví dụ, hãy xem xét một Personlớp thực hiện FriendEmployeegiao diện.

class Person implements AbstractEmployee, AbstractFriend {
}

Trong bối cảnh sinh nhật của một người, chúng tôi lập trình Friendgiao diện, để tránh đối xử với người đó như một Employee.

function party() {
    const friend: Friend = new Person("Kathryn");
    friend.HaveFun();
}

Trong bối cảnh công việc của người đó, chúng tôi lập trình Employeegiao diện, để ngăn chặn ranh giới nơi làm việc.

function workplace() {
    const employee: Employee = new Person("Kathryn");
    employee.DoWork();
}

Tuyệt quá. Chúng tôi đã cư xử phù hợp trong các bối cảnh khác nhau và phần mềm của chúng tôi đang hoạt động tốt.

Tương lai xa, nếu việc kinh doanh của chúng tôi thay đổi để làm việc với chó, chúng tôi có thể thay đổi phần mềm khá dễ dàng. Đầu tiên, chúng ta tạo một Doglớp thực hiện cả hai FriendEmployee. Sau đó, chúng tôi một cách an toàn thay đổi new Person()để new Dog(). Ngay cả khi cả hai hàm có hàng ngàn dòng mã, chỉnh sửa đơn giản đó sẽ hoạt động vì chúng tôi biết những điều sau đây là đúng:

  1. Hàm partychỉ sử dụng Friendtập con củaPerson .
  2. Hàm workplacechỉ sử dụng Employeetập con củaPerson .
  3. Lớp Dogthực hiện cả FriendEmployeegiao diện.

Mặt khác, nếu một trong hai partyhoặc workplaceđã được lập trình chống lại Person, sẽ có nguy cơ cả hai đều có Personmã đặc thù. Thay đổi từ Personthành Dogsẽ yêu cầu chúng tôi kết hợp thông qua mã để loại bỏ bất kỳ Personmã cụ thể Dognào không hỗ trợ.

Về mặt đạo đức : lập trình cho các giao diện giúp mã của chúng ta hành xử phù hợp và sẵn sàng thay đổi. Nó cũng chuẩn bị mã của chúng tôi để phụ thuộc vào trừu tượng, điều này mang lại nhiều lợi thế hơn nữa.


1
Giả sử bạn không có giao diện quá rộng, đó là.
Casey

4

Nếu tôi đang viết một lớp mới Swimmerđể thêm chức năng swim()và cần sử dụng một đối tượng của lớp Dog, và Doglớp này sẽ thực hiện giao diện Animalkhai báo swim().

Ở đầu phân cấp ( Animal), nó rất trừu tượng trong khi ở dưới cùng ( Dog) nó rất cụ thể. Cách tôi nghĩ về "lập trình cho các giao diện" là, khi tôi viết Swimmerlớp, tôi muốn viết mã của mình dựa trên giao diện theo thứ bậc đó trong trường hợp này là một thứAnimal đối tượng. Một giao diện không có chi tiết triển khai và do đó làm cho mã của bạn được ghép lỏng lẻo.

Các chi tiết triển khai có thể được thay đổi theo thời gian, tuy nhiên, nó sẽ không ảnh hưởng đến mã còn lại vì tất cả những gì bạn đang tương tác là với giao diện chứ không phải việc thực hiện. Bạn không quan tâm việc triển khai như thế nào ... tất cả những gì bạn biết là sẽ có một lớp sẽ thực hiện giao diện.


3

Vì vậy, để có được điều này, ưu điểm của giao diện là tôi có thể tách cách gọi của một phương thức khỏi bất kỳ lớp cụ thể nào. Thay vào đó, tạo một thể hiện của giao diện, trong đó việc triển khai được đưa ra từ bất kỳ lớp nào tôi chọn thực hiện giao diện đó. Do đó cho phép tôi có nhiều lớp, có chức năng tương tự nhưng hơi khác nhau và trong một số trường hợp (các trường hợp liên quan đến ý định của giao diện) không quan tâm nó là đối tượng nào.

Ví dụ, tôi có thể có một giao diện chuyển động. Một phương thức làm cho một cái gì đó 'di chuyển' và bất kỳ đối tượng nào (Người, Xe, Mèo) thực hiện giao diện chuyển động có thể được truyền vào và bảo di chuyển. Không có phương thức, mọi người đều biết loại lớp học.


3

Hãy tưởng tượng bạn có một sản phẩm gọi là 'Zebra' có thể được mở rộng bằng các plugin. Nó tìm thấy các plugin bằng cách tìm kiếm DLL trong một số thư mục. Nó tải tất cả các DLL đó và sử dụng sự phản chiếu để tìm bất kỳ lớp nào thực hiệnIZebraPlugin và sau đó gọi các phương thức của giao diện đó để giao tiếp với các plugin.

Điều này làm cho nó hoàn toàn độc lập với bất kỳ lớp plugin cụ thể nào - nó không quan tâm các lớp đó là gì. Nó chỉ quan tâm rằng họ đáp ứng các đặc điểm kỹ thuật giao diện.

Giao diện là một cách xác định các điểm có khả năng mở rộng như thế này. Mã nói chuyện với một giao diện được ghép lỏng hơn - thực tế nó không được ghép với bất kỳ mã cụ thể nào khác. Nó có thể hoạt động với các plugin được viết nhiều năm sau bởi những người chưa bao giờ gặp nhà phát triển ban đầu.

Thay vào đó, bạn có thể sử dụng một lớp cơ sở với các hàm ảo - tất cả các plugin sẽ được lấy từ lớp cơ sở. Nhưng điều này hạn chế hơn nhiều vì một lớp chỉ có thể có một lớp cơ sở, trong khi nó có thể thực hiện bất kỳ số lượng giao diện nào.


3

C ++ giải thích.

Hãy nghĩ về một giao diện như các lớp phương thức công khai của bạn.

Sau đó, bạn có thể tạo một mẫu 'phụ thuộc' vào các phương thức công khai này để thực hiện chức năng của chính nó (nó thực hiện các cuộc gọi hàm được xác định trong giao diện chung của lớp). Hãy nói rằng mẫu này là một thùng chứa, giống như một lớp Vector và giao diện mà nó phụ thuộc vào là một thuật toán tìm kiếm.

Bất kỳ lớp thuật toán nào xác định các hàm / giao diện Vector thực hiện các lệnh gọi sẽ thỏa mãn 'hợp đồng' (như ai đó đã giải thích trong câu trả lời ban đầu). Các thuật toán thậm chí không cần phải thuộc cùng một lớp cơ sở; yêu cầu duy nhất là các hàm / phương thức mà Vector phụ thuộc vào (giao diện) được xác định trong thuật toán của bạn.

Điểm chính của tất cả những điều này là bạn có thể cung cấp bất kỳ thuật toán / lớp tìm kiếm khác nhau miễn là nó cung cấp giao diện mà Vector phụ thuộc vào (tìm kiếm bong bóng, tìm kiếm tuần tự, tìm kiếm nhanh).

Bạn cũng có thể muốn thiết kế các thùng chứa khác (danh sách, hàng đợi) sẽ khai thác thuật toán tìm kiếm tương tự như Vector bằng cách chúng hoàn thành giao diện / hợp đồng mà thuật toán tìm kiếm của bạn phụ thuộc.

Điều này giúp tiết kiệm thời gian (nguyên tắc OOP 'sử dụng lại mã') vì bạn có thể viết một thuật toán một lần thay vì lặp đi lặp lại cụ thể cho mọi đối tượng mới mà bạn tạo mà không làm phức tạp vấn đề với cây thừa kế quá mức.

Đối với "bỏ lỡ" về cách mọi thứ vận hành; thời gian lớn (ít nhất là trong C ++), vì đây là cách mà hầu hết khuôn khổ của Thư viện TEMPLATE tiêu chuẩn hoạt động.

Tất nhiên khi sử dụng kế thừa và các lớp trừu tượng, phương pháp lập trình cho giao diện thay đổi; nhưng nguyên tắc là như nhau, các hàm / phương thức công khai của bạn là giao diện lớp của bạn.

Đây là một chủ đề lớn và là một trong những nguyên tắc nền tảng của Mẫu thiết kế.


3

Trong Java, các lớp cụ thể này đều thực hiện giao diện CharSequence:

CharBuffer, Chuỗi, StringBuffer, StringBuilder

Các lớp cụ thể này không có lớp cha chung chung ngoài Object, vì vậy không có gì liên quan đến chúng, ngoài thực tế chúng có liên quan đến các mảng ký tự, đại diện như vậy hoặc thao tác như vậy. Chẳng hạn, các ký tự của Chuỗi không thể được thay đổi sau khi một đối tượng Chuỗi được khởi tạo, trong khi các ký tự của StringBuffer hoặc StringBuilder có thể được chỉnh sửa.

Tuy nhiên, mỗi một trong số các lớp này có khả năng thực hiện phù hợp các phương thức giao diện CharSequence:

char charAt(int index)
int length()
CharSequence subSequence(int start, int end)
String toString()

Trong một số trường hợp, các lớp thư viện lớp Java được sử dụng để chấp nhận Chuỗi đã được sửa đổi để bây giờ chấp nhận giao diện CharSequence. Vì vậy, nếu bạn có một phiên bản của StringBuilder, thay vì trích xuất một đối tượng String (có nghĩa là khởi tạo một thể hiện đối tượng mới), thay vào đó, nó chỉ có thể vượt qua chính StringBuilder khi nó thực hiện giao diện CharSequence.

Giao diện Appendable mà một số lớp triển khai có cùng loại lợi ích cho mọi tình huống trong đó các ký tự có thể được thêm vào một thể hiện của thể hiện đối tượng lớp cụ thể bên dưới. Tất cả các lớp cụ thể này đều thực hiện giao diện Appendable:

BufferedWriter, CharArrayWriter, CharBuffer, FileWriter, FilterWriter, LogStream, OutputStreamWriter, PipedWriter, PrintStream, PrintWriter, StringBuffer, StringBuilder, StringWriter, Writer


Giao diện quá tệ như CharSequencevậy là rất thiếu máu. Tôi ước Java và .NET đã cho phép các giao diện được triển khai mặc định, để mọi người không làm giảm các giao diện hoàn toàn cho mục đích giảm thiểu mã soạn sẵn. Với bất kỳ CharSequencetriển khai hợp pháp nào, người ta có thể mô phỏng hầu hết các chức năng Stringchỉ sử dụng bốn phương pháp trên, nhưng nhiều triển khai có thể thực hiện các chức năng đó hiệu quả hơn nhiều theo những cách khác. Thật không may, ngay cả khi một triển khai cụ thể CharSequencegiữ mọi thứ trong một lần duy nhất char[]và có thể thực hiện nhiều ...
supercat

... Các thao tác indexOfnhanh chóng, không có cách nào mà một người gọi không quen với việc triển khai cụ CharSequencethể có thể yêu cầu nó thực hiện thay vì phải sử dụng charAtđể kiểm tra từng ký tự riêng lẻ.
supercat

3

Truyện ngắn: Một người đưa thư được yêu cầu về nhà sau khi nhận nhà và nhận bìa chứa (thư, tài liệu, séc, thẻ quà tặng, đơn, thư tình) với địa chỉ ghi trên đó để gửi.

Giả sử không có vỏ bọc và yêu cầu người đưa thư về nhà sau khi nhận nhà và nhận tất cả mọi thứ và giao cho người khác, người đưa thư có thể bị nhầm lẫn.

Vì vậy, tốt hơn nên bọc nó bằng bìa (trong câu chuyện của chúng tôi đó là giao diện) sau đó anh ta sẽ làm tốt công việc của mình.

Bây giờ công việc của người đưa thư là chỉ nhận và giao bìa (anh ta sẽ không làm phiền những gì bên trong trang bìa).

Tạo một loại interface không thực tế, nhưng thực hiện nó với loại thực tế.

Để tạo giao diện có nghĩa là các thành phần của bạn dễ dàng lấy Fit vào phần còn lại của mã

Tôi cho bạn một ví dụ.

bạn có giao diện AirPlane như dưới đây.

interface Airplane{
    parkPlane();
    servicePlane();
}

Giả sử bạn có các phương thức trong lớp Máy bay điều khiển của bạn như

parkPlane(Airplane plane)

servicePlane(Airplane plane)

thực hiện trong chương trình của bạn. Nó sẽ không BREAK mã của bạn. Ý tôi là, nó không cần phải thay đổi miễn là nó chấp nhận các đối số nhưAirPlane .

Bởi vì nó sẽ chấp nhận bất kỳ trên máy bay mặc dù loại thực tế, flyer, highflyr,fighter vv

Ngoài ra, trong một bộ sưu tập:

List<Airplane> plane; // Sẽ lấy tất cả các máy bay của bạn.

Ví dụ sau đây sẽ làm rõ sự hiểu biết của bạn.


Bạn có một máy bay chiến đấu thực hiện nó, vì vậy

public class Fighter implements Airplane {

    public void  parkPlane(){
        // Specific implementations for fighter plane to park
    }
    public void  servicePlane(){
        // Specific implementatoins for fighter plane to service.
    }
}

Điều tương tự đối với HighFlyer và các clasess khác:

public class HighFlyer implements Airplane {

    public void  parkPlane(){
        // Specific implementations for HighFlyer plane to park
    }

    public void  servicePlane(){
        // specific implementatoins for HighFlyer plane to service.
    }
}

Bây giờ nghĩ rằng các lớp điều khiển của bạn sử dụng AirPlanenhiều lần,

Giả sử lớp Trình điều khiển của bạn là ControlPlane như bên dưới,

public Class ControlPlane{ 
 AirPlane plane;
 // so much method with AirPlane reference are used here...
}

Ở đây phép thuật xuất hiện khi bạn có thể tạo ra các thể hiện AirPlanekiểu mới bao nhiêu tùy ý và bạn không thay đổi mã ControlPlanelớp.

Bạn có thể thêm một ví dụ ...

JumboJetPlane // implementing AirPlane interface.
AirBus        // implementing AirPlane interface.

Bạn cũng có thể loại bỏ các trường hợp của các loại được tạo trước đó.


2

Một giao diện giống như một hợp đồng, nơi bạn muốn lớp triển khai của mình thực hiện các phương thức được ghi trong hợp đồng (giao diện). Vì Java không cung cấp nhiều kế thừa, "lập trình cho giao diện" là một cách tốt để đạt được nhiều kế thừa.

Nếu bạn có một lớp A đã mở rộng một số lớp B khác, nhưng bạn muốn lớp A đó cũng tuân theo một số nguyên tắc nhất định hoặc thực hiện một hợp đồng nhất định, thì bạn có thể thực hiện bằng chiến lược "lập trình để giao diện".


2

Q: - ... "Bạn có thể sử dụng bất kỳ lớp nào thực hiện giao diện không?"
A: - Vâng.

Q: - ... "Khi nào bạn cần làm điều đó?"
A: - Mỗi lần bạn cần một lớp thực hiện (các) giao diện.

Lưu ý: Chúng tôi không thể khởi tạo một giao diện không được lớp triển khai - Đúng.

  • Tại sao?
  • Bởi vì giao diện chỉ có các nguyên mẫu phương thức, không phải định nghĩa (chỉ tên hàm, không phải logic của chúng)

AnIntf anInst = new Aclass();
// chúng ta chỉ có thể làm điều này nếu Aclass thực hiện AnIntf.
// anInst sẽ có tham chiếu Aclass.


Lưu ý: Bây giờ chúng ta có thể hiểu điều gì đã xảy ra nếu B class và C class triển khai cùng một Dintf.

Dintf bInst = new Bclass();  
// now we could call all Dintf functions implemented (defined) in Bclass.

Dintf cInst = new Cclass();  
// now we could call all Dintf functions implemented (defined) in Cclass.

Những gì chúng ta có: Các nguyên mẫu giao diện giống nhau (tên hàm trong giao diện) và gọi các triển khai khác nhau.

Tài liệu tham khảo: Nguyên mẫu - wikipedia


1

Chương trình thành một giao diện cho phép thay đổi việc thực hiện hợp đồng được xác định bởi giao diện một cách liền mạch. Nó cho phép khớp nối lỏng lẻo giữa hợp đồng và thực hiện cụ thể.

IInterface classRef = new ObjectWhatever()

Bạn có thể sử dụng bất kỳ lớp nào thực hiện IInterface? Khi nào bạn cần làm điều đó?

Có một cái nhìn vào câu hỏi SE này cho ví dụ tốt.

Tại sao giao diện cho một lớp Java nên được ưu tiên?

Có sử dụng hiệu suất hit Interface không?

Nếu có, bao nhiêu?

Đúng. Nó sẽ có hiệu suất hoạt động nhẹ trong vài giây. Nhưng nếu ứng dụng của bạn có yêu cầu thay đổi việc triển khai giao diện một cách linh hoạt, đừng lo lắng về tác động hiệu suất.

Làm thế nào bạn có thể tránh nó mà không phải duy trì hai bit mã?

Đừng cố gắng tránh nhiều triển khai giao diện nếu ứng dụng của bạn cần chúng. Trong trường hợp không có sự kết hợp chặt chẽ của giao diện với một triển khai cụ thể, bạn có thể phải triển khai bản vá để thay đổi một triển khai này sang triển khai khác.

Một trường hợp sử dụng tốt: Thực hiện mô hình Chiến lược:

Ví dụ thực tế về mô hình chiến lược


1

chương trình đến một giao diện là một thuật ngữ từ cuốn sách GOF. Tôi sẽ không trực tiếp nói rằng nó phải làm với giao diện java mà là giao diện thực. để đạt được sự phân tách lớp sạch, bạn cần tạo một số phân tách giữa các hệ thống, ví dụ: giả sử bạn có một cơ sở dữ liệu cụ thể mà bạn muốn sử dụng, bạn sẽ không bao giờ "lập trình cho cơ sở dữ liệu", thay vào đó bạn sẽ "lập trình cho giao diện lưu trữ". Tương tự như vậy, bạn sẽ không bao giờ "lập trình cho một dịch vụ web" mà thay vào đó bạn sẽ lập trình cho "giao diện máy khách". Điều này là để bạn có thể dễ dàng trao đổi mọi thứ.

tôi tìm thấy những quy tắc này giúp tôi:

1 . chúng tôi sử dụng giao diện java khi chúng tôi có nhiều loại đối tượng. Nếu tôi chỉ có một đối tượng, tôi không thấy điểm. Nếu có ít nhất hai triển khai cụ thể của một số ý tưởng, thì tôi sẽ sử dụng giao diện java.

2 . nếu như tôi đã nói ở trên, bạn muốn đưa việc tách rời khỏi một hệ thống bên ngoài (hệ thống lưu trữ) sang hệ thống của riêng bạn (DB cục bộ) thì cũng sử dụng một giao diện.

chú ý làm thế nào có hai cách để xem xét khi sử dụng chúng. hi vọng điêu nay co ich.


0

Ngoài ra tôi thấy rất nhiều câu trả lời hay và giải thích ở đây, vì vậy tôi muốn đưa ra quan điểm của mình ở đây, bao gồm một số thông tin bổ sung mà tôi nhận thấy khi sử dụng phương pháp này.

Kiểm tra đơn vị

Trong hai năm qua, tôi đã viết một dự án sở thích và tôi đã không viết bài kiểm tra đơn vị cho nó. Sau khi viết khoảng 50K dòng tôi phát hiện ra rằng nó thực sự cần thiết để viết bài kiểm tra đơn vị. Tôi đã không sử dụng các giao diện (hoặc rất ít) ... và khi tôi thực hiện bài kiểm tra đơn vị đầu tiên của mình, tôi phát hiện ra nó rất phức tạp. Tại sao?

Bởi vì tôi đã phải tạo ra rất nhiều thể hiện của lớp, được sử dụng cho đầu vào như là các biến lớp và / hoặc tham số. Vì vậy, các bài kiểm tra trông giống như các bài kiểm tra tích hợp (phải tạo ra một 'khung' hoàn chỉnh các lớp vì tất cả được gắn với nhau).

Sợ giao diện Vì vậy tôi quyết định sử dụng giao diện. Nỗi sợ hãi của tôi là tôi đã phải thực hiện tất cả các chức năng ở mọi nơi (trong tất cả các lớp được sử dụng) nhiều lần. Theo một cách nào đó thì điều này là đúng, tuy nhiên, bằng cách sử dụng tính kế thừa, nó có thể được giảm đi rất nhiều.

Kết hợp các giao diện và kế thừa Tôi phát hiện ra sự kết hợp này rất tốt để sử dụng. Tôi đưa ra một ví dụ rất đơn giản.

public interface IPricable
{
    int Price { get; }
}

public interface ICar : IPricable

public abstract class Article
{
    public int Price { get { return ... } }
}

public class Car : Article, ICar
{
    // Price does not need to be defined here
}

Cách sao chép mã này là không cần thiết, trong khi vẫn có lợi ích của việc sử dụng ô tô làm giao diện (ICar).


0

Trước tiên hãy bắt đầu với một số định nghĩa:

Giao diện n. Tập hợp tất cả các chữ ký được xác định bởi các hoạt động của một đối tượng được gọi là giao diện cho đối tượng

Loại n. Một giao diện cụ thể

Một ví dụ đơn giản của một giao diện theo quy định trên sẽ là tất cả các phương pháp đối tượng PDO như query(), commit(),close() vv, như một toàn thể, chứ không phải riêng rẽ. Các phương thức này, tức là giao diện của nó xác định bộ thông báo hoàn chỉnh, các yêu cầu có thể được gửi đến đối tượng.

Một loại như được xác định ở trên là một giao diện cụ thể. Tôi sẽ sử dụng giao diện hình làm-up để chứng minh: draw(), getArea(), getPerimeter()vv ..

Nếu một đối tượng thuộc loại Cơ sở dữ liệu, chúng tôi có nghĩa là nó chấp nhận các thông báo / yêu cầu của giao diện cơ sở dữ liệu query(), commit()v.v. Các đối tượng có thể có nhiều loại. Bạn có thể có một đối tượng cơ sở dữ liệu có kiểu hình miễn là nó thực hiện giao diện của nó, trong trường hợp này sẽ là kiểu gõ phụ .

Nhiều đối tượng có thể có nhiều giao diện / loại khác nhau và thực hiện giao diện đó khác nhau. Điều này cho phép chúng ta thay thế các đối tượng, cho phép chúng ta chọn sử dụng cái nào. Còn được gọi là đa hình.

Khách hàng sẽ chỉ nhận thức được giao diện chứ không phải việc thực hiện.

Vì vậy, trong chương trình cốt lõi để một giao diện sẽ bao gồm làm cho một số loại lớp trừu tượng như Shapevới giao diện chỉ định nghĩa draw(), getCoordinates(), getArea()vv .. Và sau đó có các lớp bê tông khác nhau thực hiện những giao diện như một lớp Circle, lớp Square, lớp Triangle. Do đó chương trình đến một giao diện không phải là một thực hiện.


0

"Chương trình giao diện" có nghĩa là không cung cấp mã cứng đúng cách, nghĩa là mã của bạn sẽ được mở rộng mà không phá vỡ chức năng trước đó. Chỉ là phần mở rộng, không chỉnh sửa mã trước đó.

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.