Làm sao tôi biết khi nào cần tạo giao diện?


196

Tôi đang ở một thời điểm trong quá trình học phát triển của mình, nơi tôi cảm thấy mình phải học nhiều hơn về giao diện.

Tôi thường xuyên đọc về họ nhưng có vẻ như tôi không thể nắm bắt được chúng.

Tôi đã đọc các ví dụ như: Lớp cơ sở động vật, với giao diện IAnimal cho những thứ như 'Đi bộ', 'Chạy', 'GetLegs', v.v. - nhưng tôi chưa bao giờ làm việc trên một cái gì đó và cảm thấy như "Này tôi nên sử dụng giao diện đây!"

Tôi đang thiếu gì? Tại sao nó là một khái niệm khó khăn cho tôi để nắm bắt! Tôi chỉ bị đe dọa bởi thực tế là tôi có thể không bao giờ nhận ra một nhu cầu cụ thể cho một người - chủ yếu là do một số khía cạnh thiếu hiểu biết về chúng! Nó làm tôi cảm thấy như mình đang thiếu thứ gì đó lên hàng đầu về mặt trở thành nhà phát triển! Nếu bất cứ ai đã có một trải nghiệm như thế này và có một bước đột phá tôi sẽ đánh giá cao một số mẹo về cách hiểu khái niệm này. Cảm ơn bạn.


1
Bạn phải kiểm tra stackoverflow.com/q/8531292/1055241
gprathour

Câu trả lời:


151

nó giải quyết vấn đề cụ thể này:

bạn có a, b, c, d gồm 4 loại khác nhau. tất cả trên mã của bạn, bạn có một cái gì đó như:

a.Process();
b.Process();
c.Process();
d.Process();

Tại sao họ không thực hiện IProcessable, và sau đó làm

List<IProcessable> list;

foreach(IProcessable p in list)
    p.Process();

điều này sẽ mở rộng tốt hơn nhiều khi bạn thêm, giả sử, 50 loại lớp mà tất cả đều làm điều tương tự.


Một vấn đề cụ thể khác:

Bạn đã bao giờ nhìn vào System.Linq.Enumerable chưa? Nó định nghĩa một tấn các phương thức mở rộng hoạt động trên bất kỳ loại nào thực hiện IEnumerable. Bởi vì bất cứ điều gì thực hiện IEnumerable về cơ bản đều nói "Tôi hỗ trợ phép lặp trong mẫu kiểu foreach không có thứ tự", bạn có thể xác định các hành vi phức tạp (Count, Max, Where, Chọn, v.v.) cho bất kỳ loại nào.


2
Điều đó có ích. Lợi thế của việc có một giao diện hiện tại thay vì chỉ có các kiểu thực hiện cho phương thức Process () là gì?
dùng53885

Bạn sẽ không thể sử dụng cùng một biến p trừ khi chúng là tất cả các lớp con cùng loại cơ sở hoặc chúng đã triển khai giao diện.
Karl

Bạn không phải phân vai, trái ngược với 50 lớp khác nhau bằng phương thức Process. C # không sử dụng "gõ vịt", vì vậy chỉ vì A có Process () và B có Process () không có nghĩa là có bất kỳ cách gọi chung nào. Bạn cần một Giao diện cho điều đó.
dùng7116

đúng. Tôi chỉ thay đổi "var" thành "IProcessable" để làm cho ví dụ có ý nghĩa hơn.
Jimmy

2
@Rogerio: Tôi đã cố gắng để được chung chung. Vấn đề không phải là "khi bạn có những thứ có hàm Process ()" thì "khi bạn có những thứ có chung một bộ phương thức". ví dụ có thể dễ dàng thay đổi thànhforeach(IMyCompanyWidgetFrobber a in list) a.Frob(widget, context);
Jimmy

133

Tôi thích câu trả lời của Jimmy rất nhiều, nhưng tôi cảm thấy mình cần thêm một cái gì đó vào đó. Chìa khóa cho toàn bộ là "khả năng" trong IProcess có thể. Nó chỉ ra một khả năng (hoặc thuộc tính, nhưng có nghĩa là "chất lượng nội tại", không phải theo nghĩa các thuộc tính C #) của đối tượng thực hiện giao diện. IAnimal có lẽ không phải là một ví dụ tốt cho giao diện, nhưng IWalkable có thể là một giao diện tốt để có nếu hệ thống của bạn có nhiều thứ có thể đi được. Bạn có thể có các lớp bắt nguồn từ Động vật như Chó, Bò, Cá, Rắn. Hai cái đầu tiên có thể sẽ thực hiện IWalkable, hai cái sau không đi bộ, vì vậy chúng sẽ không. Bây giờ bạn hỏi "tại sao không có một siêu lớp khác, WalkingAnimal, mà Chó và Bò bắt nguồn từ đâu?". Câu trả lời là khi bạn có một cái gì đó hoàn toàn bên ngoài cây thừa kế cũng có thể đi bộ, chẳng hạn như robot. Robot sẽ triển khai IWalkable, nhưng có lẽ sẽ không xuất phát từ Animal. Nếu bạn muốn có một danh sách những thứ có thể đi bộ,

Bây giờ thay thế IWalkable bằng một cái gì đó giống phần mềm hơn như IPersistable và sự tương tự trở nên gần gũi hơn với những gì bạn thấy trong một chương trình thực.


9
Tôi thích những gì bạn đã đưa ra - "Nó cho thấy một khả năng". Các giao diện thực sự cần thiết khi bạn phải xác định khả năng vì những điều cơ bản phải bám vào lớp "Cơ sở" của nó.
Ramiz Uddin

71

Sử dụng giao diện khi triển khai cùng chức năng sẽ khác nhau.

Sử dụng một lớp trừu tượng / cơ sở khi bạn cần chia sẻ một triển khai cụ thể chung.


8
Đầu tiên được gọi là đa hình. Thứ hai là dầu rắn - trừ khi sublcass baseclass (không vi phạm nguyên tắc thay thế Liskov), Bạn nên ưu tiên thành phần hơn thừa kế.
Arnis Lapsa

@ArnisLapsa Tôi hoàn toàn không hiểu ý của bạn là gì "trừ khi sublcass là baseclass". Khi nào một lớp con không "là" lớp cơ sở? (như trong istừ khóa)
Marc.2377

32

Hãy nghĩ về một giao diện giống như một Hợp đồng. Đó là một cách để nói, "Các lớp này nên tuân theo các quy tắc này."

Vì vậy, trong ví dụ IAnimal, đó là một cách để nói, "Tôi PHẢI có thể gọi Run, Walk, v.v. trên các lớp thực hiện IAnimal."

Tại sao điều này hữu ích? Bạn có thể muốn xây dựng một chức năng dựa trên thực tế là bạn phải có thể gọi Run và Walk, ví dụ, trên đối tượng. Bạn có thể có những điều sau đây:

public void RunThenWalk(Monkey m) {
    m.Run();
    m.Walk();
}

public void RunThenWalk(Dog d) {
    d.Run();
    d.Walk();
}

... Và lặp lại điều đó cho tất cả các đối tượng mà bạn biết có thể chạy và đi bộ. Tuy nhiên, với giao diện IAnimal của bạn, bạn có thể xác định chức năng một lần như sau:

public void RunThenWalk(IAnimal a) {
    a.Run();
    a.Walk();
}

Bằng cách lập trình dựa trên giao diện, về cơ bản, bạn tin tưởng các lớp để thực hiện ý định của giao diện. Vì vậy, trong ví dụ của chúng tôi, suy nghĩ là "Tôi không quan tâm đến cách họ chạy và đi bộ, miễn là họ chạy và đi bộ. RunThenWalk của tôi sẽ có hiệu lực miễn là họ thực hiện thỏa thuận đó. Nó hoạt động hoàn toàn tốt mà không cần biết gì khác về lớp."

Ngoài ra còn có một cuộc thảo luận tốt trong câu hỏi liên quan này .


1
Đừng quên độc lập thực hiện. Giao diện cho phép người ta không quan tâm đến cách thức thực hiện chức năng cơ bản, chỉ là nó thực hiện những gì Giao diện nói.
Matthew Brubaker

18

Đừng lo lắng quá. Rất nhiều nhà phát triển, sẽ hiếm khi cần phải viết một giao diện. Bạn sẽ thường xuyên sử dụng các giao diện có sẵn trong .NET framework, nhưng nếu bạn không cảm thấy cần phải viết sớm bất cứ lúc nào thì không có gì đáng ngạc nhiên về điều đó.

Ví dụ tôi luôn đưa ra cho ai đó là nếu bạn có lớp Thuyền buồm và lớp Viper. Họ kế thừa lớp Thuyền và lớp Xe tương ứng. Bây giờ nói rằng bạn cần lặp qua tất cả các đối tượng này và gọi Drive()phương thức của chúng . Trong khi bạn có thể viết một số mã như sau:

if(myObject is Boat)
    ((Boat)myObject).Drive()
else
    if (myObject is Car)
        ((Car)myObject).Drive()

Nó sẽ đơn giản hơn nhiều để viết:

((IDrivable)myObject).Drive()

16

Jimmy có quyền, khi bạn muốn có thể sử dụng một biến duy nhất cho nhiều loại, nhưng tất cả các loại đó đều thực hiện cùng một phương thức thông qua khai báo giao diện. Sau đó, bạn có thể gọi chúng là phương thức chính trên biến gõ giao diện.

Có một lý do thứ hai để sử dụng giao diện, tuy nhiên. Khi kiến ​​trúc sư dự án là một người khác với người viết mã triển khai, hoặc có một số người mã hóa thực hiện và một người quản lý dự án. Người phụ trách có thể viết cả đống giao diện và thấy rằng hệ thống tương tác với nhau, sau đó để lại cho các nhà phát triển để điền vào các giao diện với các lớp thực hiện. Đây là cách tốt nhất để đảm bảo nhiều người viết các lớp tương thích và họ có thể làm điều đó song song.


15

Tôi thích sự tương tự quân đội.

Trung sĩ không quan tâm nếu bạn là nhà phát triển phần mềm , nhạc sĩ hoặc luật sư .
Bạn được đối xử như một người lính .

ừm

Đó là dễ dàng hơn cho trung sĩ không bận tâm với các chi tiết cụ thể của người anh đang làm việc với,
tất cả mọi người điều trị như trừu tượng người lính (... và trừng phạt họ khi họ thất bại trong việc hành động như những người thân).

Khả năng cho những người hành động như những người lính được gọi là đa hình.

Giao diện là các cấu trúc phần mềm giúp đạt được tính đa hình.

Cần chi tiết trừu tượng để đạt được sự đơn giản là câu trả lời cho câu hỏi của bạn.

Đa hình , có nghĩa từ nguyên có nghĩa là "nhiều dạng", là khả năng xử lý một đối tượng của bất kỳ lớp con nào của lớp cơ sở như thể nó là một đối tượng của lớp cơ sở. Do đó, một lớp cơ sở có nhiều dạng: chính lớp cơ sở và bất kỳ lớp con nào của nó.

(..) Điều này làm cho mã của bạn dễ viết hơn và dễ hiểu hơn cho người khác. Nó cũng làm cho mã của bạn có thể mở rộng được, bởi vì các lớp con khác có thể được thêm vào sau họ của các kiểu và các đối tượng của các lớp con mới đó cũng sẽ hoạt động với mã hiện có.


14

Theo kinh nghiệm của tôi, động lực tạo giao diện đã không xảy ra cho đến khi tôi bắt đầu thực hiện thử nghiệm đơn vị với khung mô phỏng. Rõ ràng là việc sử dụng các giao diện sẽ giúp việc chế nhạo trở nên dễ dàng hơn nhiều (vì khung phụ thuộc vào các phương thức là ảo). Khi tôi bắt đầu, tôi thấy giá trị của việc trừu tượng hóa giao diện đến lớp của tôi từ việc triển khai. Ngay cả khi tôi không tạo giao diện thực tế, tôi vẫn cố gắng biến các phương thức của mình thành ảo (cung cấp giao diện ngầm có thể bị ghi đè).

Có rất nhiều lý do khác mà tôi đã tìm thấy để củng cố thực tiễn tốt về tái cấu trúc các giao diện, nhưng điều thử nghiệm / chế giễu đơn vị là thứ mang lại "khoảnh khắc aha" sâu sắc của trải nghiệm thực tế.

EDIT : Để làm rõ, với thử nghiệm đơn vị và chế nhạo tôi luôn có hai triển khai - triển khai thực tế, cụ thể và triển khai giả thay thế được sử dụng trong thử nghiệm. Khi bạn có hai lần triển khai, giá trị của giao diện sẽ trở nên rõ ràng - giải quyết nó theo giao diện để bạn có thể thay thế việc thực hiện bất cứ lúc nào. Trong trường hợp này, tôi sẽ thay thế nó bằng một giao diện giả. Tôi biết rằng tôi có thể làm điều này mà không cần giao diện thực tế nếu lớp của tôi được xây dựng đúng, nhưng sử dụng giao diện thực tế sẽ củng cố điều này và làm cho nó sạch hơn (rõ ràng hơn với người đọc). Nếu không có động lực này, tôi không nghĩ rằng tôi sẽ đánh giá cao giá trị của các giao diện vì hầu hết các lớp của tôi chỉ có một triển khai cụ thể.


Điều đúng vì lý do sai. Trong trường hợp của bạn - Bạn kết thúc với cái gọi là "giao diện tiêu đề" và thêm độ phức tạp vượt quá trọng lượng đạt được sự đơn giản.
Arnis Lapsa

@Arni - kiểm thử đơn vị là "lý do sai", dễ dàng chế giễu các lớp để loại bỏ sự phụ thuộc trong kiểm tra là "lý do sai". Xin lỗi, nhưng tôi không đồng ý.
tvanfosson

1
các thử nghiệm sẽ ảnh hưởng đến thiết kế một cách gián tiếp bằng cách cung cấp thông tin phản hồi nếu mã là hoặc không thể kiểm tra được. thêm điểm mở rộng để cải thiện khả năng kiểm tra chính nó cũng giống như gian lận. Tôi nghĩ Mark Seeman tổng hợp nó tốt nhất bit.ly/esi8Wp
Arnis Lapsa

1
@Arni - vậy bạn có sử dụng tất cả trong các bài kiểm tra đơn vị của mình không? Nếu không, làm thế nào để bạn loại bỏ một sự phụ thuộc vào phụ thuộc. Bạn có đang sử dụng DI không? Kiểm tra đơn vị đã thúc đẩy tôi sử dụng chế độ chế nhạo và DI; chế giễu và DI đã chứng minh giá trị của việc sử dụng các giao diện để xác định hợp đồng theo cách mà không có sự hiểu biết hàn lâm nào có thể thực hiện được. Do việc áp dụng TDD của tôi, mã của tôi được ghép ít hơn nhiều so với mã khác. Tôi nghĩ đó là một điều tốt.
tvanfosson

chỉ nói rằng sự phân hủy không cùng được gọi là khớp tự nhiên dẫn đến sự gắn kết thấp và độ phức tạp không cần thiết.
Arnis Lapsa

10

Một số ví dụ không lập trình có thể giúp bạn thấy việc sử dụng giao diện phù hợp trong lập trình.

Có một giao diện giữa các thiết bị điện và mạng điện - đó là tập hợp các quy ước về hình dạng của phích cắm và ổ cắm và điện áp / dòng điện trên chúng. Nếu bạn muốn triển khai một thiết bị điện mới, miễn là phích cắm của bạn tuân theo các quy tắc, nó sẽ có thể nhận các dịch vụ từ mạng. Điều này làm cho khả năng mở rộng rất dễ dàng và loại bỏ hoặc giảm chi phí phối hợp : bạn không phải thông báo cho nhà cung cấp điện về cách thức thiết bị mới của bạn hoạt động và đi đến một thỏa thuận riêng về cách cắm thiết bị mới của bạn vào mạng.

Các nước có đồng hồ đo đường sắt tiêu chuẩn. Điều này cho phép phân công lao động giữa các công ty kỹ thuật đặt đường ray và các công ty kỹ thuật xây dựng tàu để chạy trên đường ray đó và giúp các công ty đường sắt có thể thay thế và nâng cấp tàu mà không cần nghiên cứu lại toàn bộ hệ thống.

Dịch vụ mà doanh nghiệp trình bày cho khách hàng có thể được mô tả như một giao diện: giao diện được xác định rõ sẽ nhấn mạnh dịch vụ và ẩn phương tiện . Khi bạn đặt thư vào hộp thư, bạn mong muốn hệ thống bưu chính gửi thư trong một thời gian nhất định nhưng bạn không mong đợi về cách gửi thư: bạn không cần biết và dịch vụ bưu chính có thể linh hoạt chọn phương tiện giao hàng đáp ứng tốt nhất các yêu cầu và hoàn cảnh hiện tại. Một ngoại lệ cho điều này là khả năng khách hàng chọn đường hàng không - đó không phải là giao diện mà một lập trình viên máy tính hiện đại sẽ thiết kế, vì nó tiết lộ quá nhiều việc thực hiện.

Ví dụ từ tự nhiên: Tôi không quá quan tâm đến các ví dụ eats (), makeSound (), di chuyển (), v.v. Họ mô tả hành vi, điều này đúng, nhưng họ không mô tả các tương tác và cách chúng được kích hoạt . Các ví dụ rõ ràng về các giao diện cho phép tương tác trong tự nhiên là liên quan đến sinh sản, ví dụ một bông hoa cung cấp một giao diện nhất định cho một con ong để quá trình thụ phấn có thể diễn ra.


5

Hoàn toàn có thể đi cả đời với tư cách là nhà phát triển .net và không bao giờ viết giao diện của riêng bạn. Rốt cuộc, chúng tôi vẫn sống tốt mà không cần chúng trong nhiều thập kỷ và ngôn ngữ của chúng tôi vẫn hoàn chỉnh.

Tôi không thể cho bạn biết lý do tại sao bạn cần giao diện, nhưng tôi có thể cung cấp cho bạn danh sách nơi chúng tôi sử dụng chúng trong dự án hiện tại của chúng tôi:

  1. Trong mô hình trình cắm của chúng tôi, chúng tôi tải các trình cắm theo giao diện và cung cấp giao diện đó cho các trình soạn thảo trình cắm để tuân thủ.

  2. Trong hệ thống nhắn tin liên máy tính của chúng tôi, tất cả các lớp thông báo đều thực hiện một giao diện cụ thể và được "mở khóa" bằng giao diện.

  3. Hệ thống quản lý cấu hình của chúng tôi xác định giao diện được sử dụng để đặt và truy xuất cài đặt cấu hình.

  4. Chúng tôi có một giao diện mà chúng tôi sử dụng để tránh sự cố tham chiếu vòng tròn khó chịu. (Đừng làm điều này nếu bạn không phải làm.)

Tôi đoán nếu có một quy tắc, đó là sử dụng các giao diện khi bạn muốn nhóm một số lớp trong mối quan hệ is-a, nhưng bạn không muốn cung cấp bất kỳ triển khai nào trong lớp cơ sở.


5

Một ví dụ mã (kết hợp của Andrew với một phần bổ sung của tôi tại mục đích của giao diện ), cũng tạo ra một trường hợp về lý do tại sao giao diện thay vì một lớp trừu tượng trên các ngôn ngữ không hỗ trợ cho nhiều kế thừa (c # và java):

interface ILogger
{
    void Log();
}
class FileLogger : ILogger
{
    public void Log() { }
}
class DataBaseLogger : ILogger
{
    public void Log() { }
}
public class MySpecialLogger : SpecialLoggerBase, ILogger
{
    public void Log() { }
}

Lưu ý rằng FileLogger và DataBaseLogger không cần giao diện (có thể là lớp cơ sở trừu tượng Logger). Nhưng hãy xem xét bạn bắt buộc phải sử dụng trình ghi nhật ký của bên thứ ba buộc bạn phải sử dụng lớp cơ sở (giả sử nó phơi bày các phương thức được bảo vệ mà bạn cần sử dụng). Vì ngôn ngữ không hỗ trợ nhiều kế thừa, bạn sẽ không thể sử dụng cách tiếp cận lớp cơ sở trừu tượng.

Điểm mấu chốt là: sử dụng giao diện khi có thể để có thêm tính linh hoạt cho mã của bạn. Việc thực hiện của bạn ít bị ràng buộc hơn, vì vậy nó có thể thay đổi tốt hơn.


4

Tôi đã sử dụng các giao diện bây giờ và sau đó và đây là cách sử dụng mới nhất của tôi (tên đã được khái quát hóa):

Tôi có một loạt các điều khiển tùy chỉnh trên WinForm cần lưu dữ liệu vào đối tượng kinh doanh của mình. Một cách tiếp cận là gọi riêng từng điều khiển:

myBusinessObject.Save(controlA.Data);
myBusinessObject.Save(controlB.Data);
myBusinessObject.Save(controlC.Data);

Vấn đề với việc triển khai này là bất cứ khi nào tôi thêm điều khiển, tôi phải vào phương thức "Lưu dữ liệu" và thêm điều khiển mới.

Tôi đã thay đổi các điều khiển của mình để thực hiện giao diện ISavizable có phương thức SaveToBusinessObject (...) vì vậy bây giờ phương thức "Lưu dữ liệu" của tôi chỉ lặp qua các điều khiển và nếu nó tìm thấy một giao diện có thể truy cập được, nó sẽ gọi SaveToBusinessObject. Vì vậy, bây giờ khi cần một điều khiển mới, tất cả ai đó phải làm là triển khai ISavizable trong đối tượng đó (và không bao giờ chạm vào lớp khác).

foreach(Control c in Controls)
{
  ISaveable s = c as ISaveable;

  if( s != null )
      s.SaveToBusinessObject(myBusinessObject);
}

Lợi ích chưa được thực hiện đối với các giao diện là bạn bản địa hóa các sửa đổi. Sau khi được xác định, bạn sẽ hiếm khi thay đổi dòng tổng thể của ứng dụng nhưng bạn sẽ thường thực hiện thay đổi ở cấp độ chi tiết. Khi bạn giữ các chi tiết trong các đối tượng cụ thể, một thay đổi trong ProcessA sẽ không ảnh hưởng đến thay đổi trong ProcessB. (Các lớp cơ sở cũng cung cấp cho bạn lợi ích này.)

EDIT: Một lợi ích khác là tính cụ thể trong hành động. Giống như trong ví dụ của tôi, tất cả những gì tôi muốn làm là lưu dữ liệu; Tôi không quan tâm loại kiểm soát đó là gì hoặc nếu nó có thể làm bất cứ điều gì khác - tôi chỉ muốn biết liệu tôi có thể lưu dữ liệu trong điều khiển hay không. Nó làm cho mã lưu của tôi khá rõ ràng - không có kiểm tra xem liệu đó là văn bản, số, boolean hay bất cứ điều gì vì điều khiển tùy chỉnh xử lý tất cả điều đó.


4

Bạn nên xác định một giao diện một khi bạn cần buộc một hành vi cho lớp của bạn.

Hành vi của Động vật có thể liên quan đến Đi bộ, Ăn, Chạy, v.v. Do đó, bạn xác định chúng là giao diện.

Một ví dụ thực tế khác là giao diện ActionListener (hoặc Runnable). Bạn sẽ thực hiện chúng khi bạn cần theo dõi một sự kiện cụ thể. Do đó, bạn cần cung cấp việc triển khai cho actionPerformed(Event e)phương thức trong lớp của bạn (hoặc lớp con). Tương tự, đối với giao diện Runnable, bạn cung cấp cách triển khai cho public void run()phương thức.

Ngoài ra, bạn có thể có các giao diện này được thực hiện bởi bất kỳ số lượng các lớp.

Một trường hợp khác trong đó Giao diện được sử dụng (trong Java) là để triển khai nhiều kế thừa được cung cấp trong C ++.


3
Xin Chúa làm cho họ ngừng nói những điều như nhiều kế thừa liên quan đến giao diện. Bạn KHÔNG được thừa hưởng một giao diện trong một lớp. Bạn THỰC HIỆN nó.
Andrei Rînea

4

Giả sử bạn muốn mô hình hóa những phiền toái có thể xảy ra khi bạn cố gắng đi ngủ.

Mô hình trước các giao diện

nhập mô tả hình ảnh ở đây

class Mosquito {
    void flyAroundYourHead(){}
}

class Neighbour{
    void startScreaming(){}
}

class LampJustOutsideYourWindow(){
    void shineJustThroughYourWindow() {}
}

Như bạn thấy rõ, nhiều "điều" có thể gây phiền nhiễu khi bạn cố gắng ngủ.

Sử dụng các lớp không có giao diện

Nhưng khi nói đến việc sử dụng các lớp này, chúng tôi có một vấn đề. Họ không có gì chung. Bạn phải gọi riêng từng phương thức.

class TestAnnoyingThings{
    void testAnnoyingThinks(Mosquito mosquito, Neighbour neighbour, LampJustOutsideYourWindow lamp){
         if(mosquito != null){
             mosquito.flyAroundYourHead();
         }
         if(neighbour!= null){
             neighbour.startScreaming();
         }
         if(lamp!= null){
             lamp.shineJustThroughYourWindow();
         }
    }
}

Mô hình với giao diện

Để khắc phục vấn đề này, chúng tôi có thể giới thiệu một iterfacenhập mô tả hình ảnh ở đây

interface Annoying{
   public void annoy();

}

Và thực hiện nó trong các lớp học

class Mosquito implements Annoying {
    void flyAroundYourHead(){}

    void annoy(){
        flyAroundYourHead();
    }
}

class Neighbour implements Annoying{
    void startScreaming(){}

    void annoy(){
        startScreaming();
    }
}

class LampJustOutsideYourWindow implements Annoying{
    void shineJustThroughYourWindow() {}

    void annoy(){
        shineJustThroughYourWindow();
    }
}

Sử dụng với giao diện

Điều này sẽ làm cho việc sử dụng các lớp này dễ dàng hơn nhiều

class TestAnnoyingThings{
    void testAnnoyingThinks(Annoying annoying){
        annoying.annoy();
    }
}

Ok, nhưng không NeighbourLampJustOutsideYourWindowcũng phải thực hiện Annoying?
Stardust

Vâng, cảm ơn đã chỉ ra rằng. Tôi đã thực hiện chỉnh sửa với thay đổi này
Marcin Szymczak

2

Ví dụ dễ nhất để đưa ra là một cái gì đó giống như Bộ xử lý thanh toán. (Paypal, PDS, v.v.).

Giả sử bạn tạo một giao diện IPaymentProcessor có các phương thức ProcessACH và ProcessCreditCard.

Bây giờ bạn có thể thực hiện triển khai Paypal cụ thể. Làm cho các phương thức đó gọi các chức năng cụ thể của PayPal.

Nếu bạn quyết định sau này bạn cần chuyển sang nhà cung cấp khác, bạn có thể. Chỉ cần tạo một triển khai cụ thể cho nhà cung cấp mới. Vì tất cả những gì bạn bị ràng buộc là giao diện (hợp đồng) của bạn, bạn có thể trao đổi ứng dụng nào mà ứng dụng của bạn sử dụng mà không thay đổi mã tiêu thụ.


2

Nó cũng cho phép bạn thực hiện kiểm tra đơn vị Mock (.Net). Nếu lớp của bạn sử dụng một giao diện, bạn có thể giả định đối tượng trong thử nghiệm đơn vị của mình và dễ dàng kiểm tra logic (mà không thực sự nhấn vào cơ sở dữ liệu hoặc dịch vụ web, v.v.).

http://www.nmock.org/


2

Nếu bạn duyệt các cụm .NET Framework và đi sâu vào các lớp cơ sở cho bất kỳ đối tượng tiêu chuẩn nào, bạn sẽ nhận thấy nhiều giao diện (các thành viên có tên là ISomeName).

Các giao diện về cơ bản là để thực hiện các khung, lớn hay nhỏ. Tôi cũng cảm thấy như vậy về các giao diện cho đến khi tôi muốn viết một khung công tác của riêng mình. Tôi cũng thấy rằng các giao diện hiểu biết đã giúp tôi học các khung nhanh hơn nhiều. Thời điểm mà bạn muốn viết một giải pháp thanh lịch hơn cho bất cứ điều gì, bạn sẽ thấy rằng một giao diện có rất nhiều ý nghĩa. Nó giống như một phương pháp để cho một lớp mặc quần áo phù hợp cho công việc. Quan trọng hơn, các giao diện cho phép các hệ thống trở nên tự viết tài liệu hơn nhiều, bởi vì các đối tượng phức tạp trở nên ít phức tạp hơn khi lớp thực hiện các giao diện, giúp phân loại chức năng của nó.

Các lớp thực hiện các giao diện khi họ muốn có thể tham gia vào một khung rõ ràng hoặc ngầm định. Ví dụ: IDis Dùng là một giao diện phổ biến cung cấp chữ ký phương thức cho phương thức Dispose () phổ biến và hữu ích. Trong một khung công tác, tất cả những gì bạn hoặc nhà phát triển khác cần biết về một lớp là nếu nó triển khai IDis Dùng một lần, thì bạn biết rằng ((IDis Dùng) myObject) .Dispose () có sẵn để được gọi cho mục đích dọn dẹp.

VÍ DỤ LỚP: không thực hiện giao diện IDis Dùng một lần, bạn không thể sử dụng cấu trúc từ khóa "bằng cách sử dụng" trong C #, vì nó yêu cầu bất kỳ đối tượng nào được chỉ định làm tham số đều có thể được chuyển sang IDis Dùng một cách ngầm định.

VÍ DỤ COMPLEX: Một ví dụ phức tạp hơn sẽ là lớp System.ComponentModel.Component. Lớp này thực hiện cả IDis Dùng và IComponent. Hầu hết, nếu không phải tất cả, các đối tượng .NET có trình thiết kế trực quan được liên kết với chúng triển khai IComponent để IDE có thể tương tác với thành phần.

KẾT LUẬN: Khi bạn trở nên quen thuộc hơn với .NET Framework, điều đầu tiên bạn sẽ làm khi gặp một lớp mới trong Trình duyệt đối tượng hoặc trong công cụ .NET Reflector (miễn phí) (miễn phí) http://www.red-gate.com / Products / Reflector / ) là để kiểm tra xem nó kế thừa lớp nào và cả các giao diện mà nó thực hiện. .NET Reflector thậm chí còn tốt hơn Trình duyệt đối tượng vì nó cũng cho phép bạn xem các lớp Derogen. Điều đó cho phép bạn tìm hiểu về tất cả các đối tượng xuất phát từ một lớp cụ thể, do đó có khả năng học về chức năng khung mà bạn không biết đã tồn tại. Điều này đặc biệt quan trọng khi các không gian tên được cập nhật hoặc mới được thêm vào .NET Framework.


2

Hãy xem xét bạn đang làm một trò chơi bắn súng người đầu tiên. Người chơi có nhiều súng để lựa chọn.

Chúng ta có thể có một giao diện Gunxác định chức năngshoot() .

Chúng ta cần các lớp con khác nhau của Gunlớp cụ thể ShotGun Snipervà như vậy.

ShotGun implements Gun{
    public void shoot(){
       \\shotgun implementation of shoot.
    } 
}

Sniper implements Gun{
    public void shoot(){
       \\sniper implementation of shoot.
    } 
}

Lớp bắn súng

Người bắn có tất cả súng trong Giáp của mình. Hãy tạo một Listđại diện cho nó.

List<Gun> listOfGuns = new ArrayList<Gun>();

Người bắn quay vòng qua súng của mình, khi cần, sử dụng chức năng switchGun()

public void switchGun(){
    //code to cycle through the guns from the list of guns.
    currentGun = //the next gun in the list.
}

Chúng ta có thể đặt Gun hiện tại, sử dụng chức năng trên và chỉ cần gọi shoot()chức năng, khi fire()được gọi.

public void fire(){
    currentGun.shoot();
}

Hành vi của chức năng bắn sẽ thay đổi tùy theo việc thực hiện khác nhau của Gun giao diện.

Phần kết luận

Tạo một giao diện, khi một chức năng lớp phụ thuộc vào một chức năng từ một lớp khác , có thể thay đổi hành vi của nó, dựa trên thể hiện (đối tượng) của lớp được triển khai.

ví dụ fire()chức năng từ Shooterlớp mong đợi súng ( Sniper, ShotGun) thực hiện shoot()chức năng. Vì vậy, nếu chúng ta chuyển súng và bắn.

shooter.switchGun();
shooter.fire();

Chúng tôi đã thay đổi hành vi của fire()chức năng.


1

Để mở rộng những gì Larsenal đã nói. Giao diện là một hợp đồng mà tất cả các lớp thực hiện phải tuân theo. Vì điều này, bạn có thể sử dụng một kỹ thuật gọi là lập trình cho hợp đồng. Điều này cho phép phần mềm của bạn trở nên độc lập.


1

Các giao diện thường được sử dụng khi bạn muốn xác định một hành vi mà các đối tượng có thể thể hiện.

Một ví dụ điển hình cho điều này trong thế giới .NET là giao diện IDis Dùng, được sử dụng trên bất kỳ lớp Microsoft nào sử dụng tài nguyên hệ thống phải được phát hành thủ công. Nó yêu cầu lớp thực hiện nó có phương thức Dispose ().

Phương thức Dispose () cũng được gọi bằng cách sử dụng cấu trúc ngôn ngữ cho VB.NETC # , chỉ hoạt động trênIDisposable s)

Hãy nhớ rằng bạn có thể kiểm tra xem một đối tượng có thực hiện giao diện cụ thể hay không bằng cách sử dụng các cấu trúc như TypeOf ... Is(VB.NET), is(C #), instanceof(Java), v.v ...


1

Như nhiều người có thể đã trả lời, các giao diện có thể được sử dụng để thực thi một số hành vi nhất định giữa các lớp sẽ không thực hiện các hành vi đó theo cùng một cách. Vì vậy, bằng cách thực hiện một giao diện, bạn đang nói rằng lớp của bạn có hành vi của giao diện. Giao diện IAnimal sẽ không phải là giao diện điển hình vì các lớp Dog, Cat, Bird, v.v. là các loại động vật và có lẽ nên mở rộng nó, đó là một trường hợp thừa kế. Thay vào đó, một giao diện sẽ giống như hành vi của động vật trong trường hợp này, chẳng hạn như IRunnable, IFlyable, ITrainable, v.v.

Giao diện là tốt cho nhiều thứ, một trong những điều quan trọng là khả năng cắm. Ví dụ, khai báo một phương thức có tham số Danh sách sẽ cho phép mọi thứ thực hiện giao diện Danh sách được truyền vào, cho phép nhà phát triển xóa và cắm vào một danh sách khác sau đó mà không phải viết lại một tấn mã.

Có thể bạn sẽ không bao giờ sử dụng giao diện, nhưng nếu bạn đang thiết kế một dự án từ đầu, đặc biệt là một khung nào đó, có lẽ bạn sẽ muốn làm quen với chúng.

Tôi khuyên bạn nên đọc chương về các giao diện trong Thiết kế Java của Coad, Mayfield và Kern. Họ giải thích nó tốt hơn một chút so với văn bản giới thiệu trung bình. Nếu bạn không sử dụng Java, bạn chỉ có thể đọc phần đầu của chương, chủ yếu là các khái niệm.


1

Như bất kỳ kỹ thuật lập trình nào thêm linh hoạt vào hệ thống của bạn, các giao diện cũng thêm một số mức độ phức tạp. Chúng thường rất tuyệt và bạn có thể sử dụng nó ở mọi nơi (bạn có thể tạo giao diện cho tất cả các lớp của mình) - nhưng làm như vậy, bạn sẽ tạo ra một hệ thống phức tạp hơn, khó bảo trì hơn.

Có một sự đánh đổi ở đây, như thường lệ: linh hoạt hơn khả năng bảo trì. Cái nào quan trọng hơn? Không có câu trả lời - nó phụ thuộc vào dự án. Nhưng chỉ cần nhớ rằng mọi phần mềm sẽ phải được bảo trì ...

Vì vậy, lời khuyên của tôi: không sử dụng giao diện cho đến khi bạn thực sự cần chúng. (Với Visual Studio, bạn có thể trích xuất một giao diện từ một lớp hiện có trong 2 giây - vì vậy đừng vội vàng.)

Có nói rằng, khi nào bạn cần tạo một giao diện?

Tôi làm điều đó khi tôi tái cấu trúc một phương thức đột nhiên cần xử lý hai hoặc nhiều lớp tương tự. Sau đó tôi tạo một giao diện, gán giao diện này cho hai (hoặc nhiều) lớp tương tự và tôi thay đổi loại tham số phương thức (thay thế loại lớp bằng loại giao diện).

Và nó hoạt động: o)

Một ngoại lệ: khi tôi giả lập các đối tượng, giao diện dễ sử dụng hơn nhiều. Vì vậy, tôi thường tạo giao diện chỉ cho việc này.

PS: khi tôi viết "giao diện", ý tôi là: "giao diện của bất kỳ lớp cơ sở nào", bao gồm các lớp giao diện thuần túy. Lưu ý rằng các lớp trừu tượng thường được đặt cược tốt hơn sau đó là các giao diện thuần túy vì bạn có thể thêm logic vào chúng.

Trân trọng, Sylvain.


1

Các giao diện sẽ trở nên rõ ràng khi bạn trở thành một nhà phát triển thư viện (một người viết mã cho các lập trình viên khác). Hầu hết chúng ta bắt đầu là nhà phát triển ứng dụng , nơi chúng tôi sử dụng các API và thư viện lập trình hiện có.

Dọc theo cùng một dòng rằng Giao diện là một hợp đồng , chưa có ai đề cập rằng Giao diện là một cách tuyệt vời để làm cho một số phần trong mã của bạn ổn định . Điều đó đặc biệt hữu ích khi đó là một dự án nhóm (hoặc khi bạn đang phát triển mã được sử dụng bởi các nhà phát triển khác). Vì vậy, đây là một kịch bản cụ thể cho bạn:

Khi bạn đang phát triển mã trong một nhóm , những người khác có thể sẽ sử dụng mã bạn viết. Họ sẽ rất vui khi họ mã hóa các giao diện (ổn định) của bạn và bạn sẽ rất vui khi bạn có quyền tự do thay đổi các triển khai của mình (ẩn đằng sau giao diện) mà không phá vỡ mã nhóm của bạn. Đó là một biến thể của ẩn thông tin (giao diện là công khai, việc triển khai bị ẩn khỏi các lập trình viên máy khách). Tìm hiểu thêm về các biến thể được bảo vệ .

Cũng xem câu hỏi liên quan này về mã hóa vào Giao diện .


1

Có rất nhiều mục đích để sử dụng một giao diện.

  1. Sử dụng trong hành vi đa hình. Nơi bạn muốn gọi các phương thức cụ thể của một lớp con với một khoảng không có tham chiếu đến lớp con.

  2. Có một hợp đồng với các lớp để thực hiện tất cả các phương thức cần thiết, giống như việc sử dụng phổ biến nhất là với các đối tượng COM, trong đó một lớp bao bọc được tạo trên một DLL kế thừa giao diện; các phương thức này được gọi phía sau hậu trường và bạn chỉ cần thực hiện chúng nhưng với cấu trúc tương tự như được định nghĩa trong COM DLL mà bạn chỉ có thể biết thông qua giao diện mà chúng phơi bày.

  3. Để giảm mức sử dụng bộ nhớ bằng cách tải các phương thức cụ thể trong một lớp. Giống như nếu bạn có ba đối tượng kinh doanh và chúng được triển khai trong một lớp duy nhất, bạn có thể sử dụng ba giao diện.

Ví dụ: IUser, IOrder, IOrderItem

public interface IUser()
{

void AddUser(string name ,string fname);

}

// Same for IOrder and IOrderItem
//


public class  BusinessLayer: IUser, IOrder, IOrderItem

{    
    public void AddUser(string name ,string fname)
    {
        // Do stuffs here.
    }

    // All methods from all interfaces must be implemented.

}

Nếu bạn chỉ muốn thêm người dùng, hãy làm như thế này:

IUser user = new (IUser)BusinessLayer();

// It will load  all methods into memory which are declared in the IUser interface.

user.AddUser();
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.