Hiểu lập trình trên nền tảng giao diện


29

Tôi đã bắt gặp thuật ngữ "lập trình cho một giao diện thay vì triển khai" rất nhiều và tôi nghĩ rằng tôi hiểu được ý nghĩa của nó. Nhưng tôi muốn chắc chắn rằng tôi hiểu lợi ích của nó và việc triển khai có thể.

"Lập trình cho một giao diện" có nghĩa là, khi có thể, người ta nên tham khảo một mức độ trừu tượng hơn của một lớp (một giao diện, lớp trừu tượng hoặc đôi khi là một siêu lớp của một loại nào đó), thay vì tham chiếu đến một triển khai cụ thể.

Một ví dụ phổ biến trong Java, là sử dụng:

List myList = new ArrayList();thay vì ArrayList myList = new ArrayList();.

Tôi có hai câu hỏi liên quan đến điều này:

  1. Tôi muốn chắc chắn rằng tôi hiểu những lợi ích chính của phương pháp này. Tôi nghĩ rằng lợi ích chủ yếu là linh hoạt. Khai báo một đối tượng như một tài liệu tham khảo cấp cao hơn, chứ không phải là một triển khai cụ thể, cho phép linh hoạt hơn và duy trì hơn trong suốt chu kỳ phát triển và trong toàn bộ mã. Điều này có đúng không? Là linh hoạt là lợi ích chính?

  2. Có nhiều cách hơn để "lập trình cho một giao diện" không? Hay là "khai báo một biến như một giao diện chứ không phải là một triển khai cụ thể" là cách thực hiện duy nhất của khái niệm này?

Tôi không nói về Giao diện xây dựng Java . Tôi đang nói về nguyên tắc OO "lập trình cho một giao diện, không phải là triển khai". Trong nguyên tắc này, "giao diện" thế giới đề cập đến bất kỳ "siêu kiểu" nào của một lớp - một giao diện, một lớp trừu tượng hoặc một siêu lớp đơn giản, trừu tượng hơn và ít cụ thể hơn các lớp con cụ thể hơn.


Có thể trùng lặp tại sao giao diện hữu ích?
gnat


1
Câu trả lời này cung cấp cho bạn một ví dụ dễ hiểu về lập trình
viên.stackexchange.com/a/314084/61852

Câu trả lời:


44

"Lập trình cho một giao diện" có nghĩa là, khi có thể, người ta nên tham khảo một mức độ trừu tượng hơn của một lớp (một giao diện, lớp trừu tượng hoặc đôi khi là một siêu lớp của một loại nào đó), thay vì tham chiếu đến một triển khai cụ thể.

Điều này không chính xác . Hoặc ít nhất, nó không hoàn toàn chính xác.

Điểm quan trọng hơn đến từ một quan điểm thiết kế chương trình. Ở đây, "lập trình cho một giao diện" có nghĩa là tập trung thiết kế của bạn vào những gì mã đang làm, chứ không phải nó làm như thế nào . Đây là một sự khác biệt quan trọng giúp đẩy thiết kế của bạn theo hướng chính xác và linh hoạt.

Ý tưởng chính là các miền thay đổi chậm hơn nhiều so với phần mềm. Giả sử bạn có phần mềm để theo dõi danh sách tạp hóa của bạn. Trong những năm 80, phần mềm này sẽ hoạt động chống lại một dòng lệnh và một số tệp phẳng trên đĩa mềm. Sau đó, bạn đã có một giao diện người dùng. Sau đó, bạn có thể đặt danh sách trong cơ sở dữ liệu. Sau đó, nó có thể chuyển sang đám mây hoặc điện thoại di động hoặc tích hợp facebook.

Nếu bạn thiết kế mã của mình cụ thể xung quanh việc triển khai (đĩa mềm và dòng lệnh), bạn sẽ không sẵn sàng cho các thay đổi. Nếu bạn đã thiết kế mã của mình xung quanh giao diện (thao tác danh sách tạp hóa) thì việc triển khai có thể tự do thay đổi.


Cảm ơn câu trả lời. Đánh giá theo những gì bạn viết tôi nghĩ tôi hiểu "lập trình cho giao diện" nghĩa là gì và lợi ích của nó là gì. Nhưng tôi có một câu hỏi - Ví dụ cụ thể phổ biến nhất cho khái niệm này là: Khi tạo tham chiếu đến một đối tượng, hãy tạo kiểu tham chiếu kiểu giao diện mà đối tượng này thực hiện (hoặc siêu lớp đối tượng này kế thừa), thay vì tạo kiểu tham chiếu loại đối tượng. (Aka, List myList = new ArrayList()thay vì ArrayList myList = new ArrayList(). (Câu hỏi trong bình luận tiếp theo)
Manila Cohn

Câu hỏi của tôi là: Bạn có thể cho tôi thêm ví dụ về các địa điểm trong mã thế giới thực nơi nguyên tắc "lập trình cho giao diện" diễn ra không? Khác với ví dụ phổ biến tôi mô tả trong bình luận cuối cùng?
Manila Cohn

6
NO . List/ ArrayListkhông phải là những gì tôi đang nói về tất cả. Nó giống như việc cung cấp một Employeeđối tượng hơn là tập hợp các bảng được liên kết mà bạn sử dụng để lưu trữ một bản ghi nhân viên. Hoặc cung cấp giao diện để lặp qua các bài hát và không quan tâm nếu những bài hát đó bị xáo trộn, hoặc trên đĩa CD hoặc phát trực tuyến từ internet. Chúng chỉ là một chuỗi các bài hát.
Telastyn

Bạn có nói rằng "lập trình cho một giao diện, không phải để thực hiện" là một nguyên tắc thể hiện nguyên tắc OO trừu tượng?
Manila Cohn

1
@AvivCohn Tôi muốn đề xuất SpringFramework là một ví dụ tốt. Mọi thứ trong Spring có thể được tăng cường hoặc tùy chỉnh bằng cách tạo ra các giao diện của riêng họ và ứng dụng chính của Springs, một tính năng sẽ tiếp tục hoạt động như mong đợi ... Hoặc trong trường hợp tùy chỉnh, như bạn mong đợi. Lập trình cho một giao diện cũng là chiến lược tốt nhất để thiết kế các khung tích hợp . Một lần nữa mùa xuân làm với sự hội nhập mùa xuân của nó. Tại thời điểm thiết kế, lập trình đến một giao diện là những gì chúng ta làm với UML. Hoặc đó là những gì tôi sẽ làm. Tóm tắt bản thân từ cách nó làm và tập trung vào những việc cần làm.
Laiv

18

Sự hiểu biết của tôi về "lập trình cho một giao diện" khác với những gì câu hỏi hoặc các câu trả lời khác gợi ý. Điều đó không có nghĩa là sự hiểu biết của tôi là chính xác, hoặc những điều trong các câu trả lời khác không phải là ý tưởng hay, chỉ là chúng không phải là những gì tôi nghĩ khi tôi nghe thấy thuật ngữ đó.

Lập trình cho một giao diện có nghĩa là khi bạn được trình bày với một số giao diện lập trình (có thể là thư viện lớp, tập hợp các chức năng, giao thức mạng hoặc bất cứ thứ gì khác) mà bạn chỉ sử dụng những thứ được giao diện đảm bảo. Bạn có thể có kiến ​​thức về việc triển khai cơ bản (bạn có thể đã viết nó), nhưng bạn không nên sử dụng kiến ​​thức đó.

Ví dụ: giả sử API cung cấp cho bạn một số giá trị mờ đục là "xử lý" đối với nội dung nào đó. Kiến thức của bạn có thể cho bạn biết rằng tay cầm này thực sự là một con trỏ và bạn có thể hủy bỏ nó và truy cập một số giá trị, điều này có thể cho phép bạn dễ dàng thực hiện một số nhiệm vụ bạn muốn làm. Nhưng giao diện không cung cấp cho bạn tùy chọn đó; đó là kiến ​​thức của bạn về việc thực hiện cụ thể.

Vấn đề với điều này là nó tạo ra sự kết hợp mạnh mẽ giữa mã của bạn và việc triển khai, chính xác là những gì giao diện được cho là ngăn chặn. Tùy thuộc vào chính trị, điều đó có thể có nghĩa là việc triển khai không thể thay đổi được nữa, vì điều đó sẽ phá vỡ mã của bạn hoặc mã của bạn rất dễ hỏng và tiếp tục phá vỡ mọi nâng cấp hoặc thay đổi của việc triển khai cơ bản.

Một ví dụ lớn về điều này là các chương trình được viết cho Windows. WinAPI là một giao diện, nhưng nhiều người đã sử dụng các thủ thuật hoạt động do triển khai cụ thể trong Windows 95. Những thủ thuật này có thể làm cho chương trình của họ nhanh hơn hoặc cho phép họ thực hiện mọi thứ với ít mã hơn mức cần thiết. Nhưng những thủ thuật này cũng có nghĩa là chương trình sẽ bị sập trên Windows 2000, vì API được triển khai khác ở đó. Nếu chương trình đủ quan trọng, Microsoft thực sự có thể tiếp tục và thêm một số hack vào việc triển khai để chương trình tiếp tục hoạt động, nhưng chi phí cho điều đó là sự phức tạp gia tăng (với tất cả các vấn đề tiếp theo) của mã Windows. Nó cũng làm cho cuộc sống của người Wine trở nên cực kỳ khó khăn, vì họ cũng cố gắng thực hiện WinAPI, nhưng họ chỉ có thể tham khảo tài liệu về cách thực hiện việc này,


Đó là một điểm tốt, và tôi nghe thấy điều này rất nhiều trong những bối cảnh nhất định.
Telastyn

Tôi thấy điểm của bạn. Vì vậy, hãy để tôi xem liệu tôi có thể điều chỉnh những gì bạn đang nói với lập trình chung không: giả sử tôi có một lớp (lớp A) sử dụng chức năng của lớp trừu tượng B. Lớp C và D kế thừa lớp B - chúng cung cấp triển khai cụ thể cho những gì Lớp được cho là làm. Nếu lớp A sử dụng trực tiếp lớp C hoặc D, thì đó gọi là 'lập trình để thực hiện', đó không phải là một giải pháp rất linh hoạt. Nhưng nếu lớp A sử dụng một tham chiếu đến lớp B, sau đó có thể được đặt thành triển khai C hoặc thực hiện D, thì nó làm cho mọi thứ linh hoạt hơn và có thể duy trì. Điều này có đúng không?
Manila Cohn

Nếu điều này đúng, hơn câu hỏi của tôi là - Có nhiều ví dụ cụ thể hơn để 'lập trình cho giao diện', ngoài 'sử dụng tham chiếu giao diện thay vì tham chiếu lớp cụ thể' không?
Manila Cohn

2
@AvivCohn Một chút muộn trong câu trả lời này, nhưng một ví dụ cụ thể là web trên toàn thế giới. Trong các cuộc chiến trình duyệt (thời đại IE 4), các trang web không được viết theo bất kỳ đặc điểm kỹ thuật nào, mà là những điều kỳ quặc của một số trình duyệt (Netscape hoặc IE). Điều này về cơ bản là lập trình để thực hiện thay vì giao diện.
Sebastian Redl

9

Tôi chỉ có thể nói về trải nghiệm cá nhân của mình, vì điều này cũng chưa bao giờ được dạy chính thức cho tôi.

Điểm đầu tiên của bạn là chính xác. Tính linh hoạt thu được đến từ việc không thể vô tình gọi các chi tiết triển khai của lớp cụ thể nơi chúng không nên được gọi.

Ví dụ, hãy xem xét một ILoggergiao diện hiện đang được triển khai như một LogToEmailLoggerlớp cụ thể . Các LogToEmailLoggerlớp cho thấy tất cả các ILoggerphương pháp và tài sản, mà còn xảy ra để có một tài sản thực hiện cụ thể sysAdminEmail.

Khi logger của bạn được sử dụng trong ứng dụng của bạn, nó không phải là mối quan tâm của mã tiêu thụ để đặt sysAdminEmail. Thuộc tính này nên được đặt trong quá trình thiết lập logger và nên được che giấu khỏi thế giới.

Nếu bạn đang mã hóa việc triển khai cụ thể, thì bạn có thể vô tình đặt thuộc tính triển khai khi sử dụng bộ ghi. Bây giờ, mã ứng dụng của bạn được liên kết chặt chẽ với logger của bạn và chuyển sang logger khác sẽ yêu cầu trước tiên tách mã của bạn khỏi mã gốc.

Theo nghĩa này, mã hóa đến một giao diện làm mất kết nối .

Về điểm thứ hai của bạn: Một lý do khác mà tôi đã thấy để mã hóa giao diện là để giảm độ phức tạp của mã.

Ví dụ, hãy tưởng tượng tôi có một trò chơi với các giao diện sau I2DRenderable, I3DRenderable, IUpdateable. Không có gì lạ khi một thành phần trò chơi có cả nội dung hiển thị 2D và 3D. Các thành phần khác có thể chỉ là 2D và các thành phần khác chỉ là 3D.

Nếu kết xuất 2D được thực hiện bởi một mô-đun, thì nó có ý nghĩa cho nó để duy trì một bộ sưu tập I2DRenderables. Sẽ không có vấn đề gì nếu các đối tượng trong bộ sưu tập của nó cũng I3DRenderablehoặc IUpdateblevì các mô-đun khác sẽ chịu trách nhiệm xử lý các khía cạnh đó của các đối tượng.

Lưu trữ các đối tượng có thể kết xuất dưới dạng một danh sách I2DRenderablegiữ cho độ phức tạp của lớp kết xuất ở mức thấp. Logic kết xuất và cập nhật 3D không phải là mối quan tâm của nó và vì vậy những khía cạnh của các đối tượng con của nó có thể và nên được bỏ qua.

Theo nghĩa này, mã hóa cho một giao diện giữ độ phức tạp thấp bằng cách cô lập các mối quan tâm .


4

Có lẽ có hai cách sử dụng giao diện từ đang được sử dụng ở đây. Giao diện mà bạn chủ yếu đề cập đến trong câu hỏi của bạn là Giao diện Java . Đó cụ thể là một khái niệm Java, nói chung là giao diện ngôn ngữ lập trình.

Tôi muốn nói rằng lập trình cho một giao diện là một khái niệm rộng hơn. Các API REST phổ biến hiện có sẵn cho nhiều trang web là một ví dụ khác về khái niệm lập trình rộng hơn cho giao diện ở mức cao hơn. Bằng cách tạo một lớp ở giữa hoạt động bên trong mã của bạn và thế giới bên ngoài (mọi người trên internet, các chương trình khác, thậm chí các phần khác của cùng một chương trình), bạn có thể thay đổi bất cứ điều gì bên trong mã của mình miễn là bạn không thay đổi thế giới bên ngoài đang mong đợi, nơi được xác định bởi một giao diện hoặc hợp đồng mà bạn dự định tôn vinh.

Điều đó sau đó cung cấp cho bạn sự linh hoạt để cấu trúc lại mã nội bộ của bạn trong khi không phải nói tất cả những điều khác phụ thuộc vào giao diện của nó.

Nó cũng có nghĩa là mã của bạn sẽ ổn định hơn. Bằng cách bám vào giao diện thì bạn không nên phá mã của người khác. Khi bạn thực sự phải thay đổi giao diện thì bạn có thể phát hành phiên bản chính mới (1.abc thành 2.xyz) của API báo hiệu rằng có những thay đổi vi phạm trong giao diện của phiên bản mới.

Như @Doval chỉ ra trong các bình luận về câu trả lời này, cũng có lỗi địa phương. Những điều này tôi nghĩ rằng tất cả sôi sục để đóng gói. Giống như bạn sẽ sử dụng nó cho các đối tượng trong thiết kế Hướng đối tượng, vì vậy khái niệm này cũng hữu ích ở mức cao hơn.


1
Một lợi ích thường bị bỏ qua là lỗi cục bộ. Giả sử bạn cần một Bản đồ và bạn triển khai nó bằng cây nhị phân. Để làm việc này, các khóa cần phải có một số thứ tự và bạn cần duy trì bất biến rằng các khóa "nhỏ hơn" khóa của nút hiện tại nằm ở cây con bên trái, trong khi các phím "lớn hơn" đang bật cây con bên phải. Khi bạn ẩn việc triển khai Bản đồ phía sau một giao diện, nếu tra cứu Bản đồ bị lỗi, bạn biết lỗi phải nằm trong mô-đun Bản đồ. Nếu nó bị lộ, lỗi có thể ở bất cứ đâu trong chương trình. Đối với tôi, đây là lợi ích chính.
Doval

4

Một sự tương tự trong thế giới thực có thể giúp:

Một phích cắm điện Mains trong một Giao diện.
Vâng; thứ ba ghim ở đầu dây nguồn từ TV, radio, máy hút bụi, máy giặt, v.v.

Bất kỳ thiết bị có một plug chính (tức là cụ là "có một phích cắm" giao diện) có thể được điều trị tại một cách chính xác theo cùng một cách; tất cả chúng có thể được cắm vào ổ cắm trên tường và có thể rút điện từ ổ cắm đó.

Những gì mỗi thiết bị cá nhân làm là hoàn toàn khác nhau. Bạn sẽ không nhận được rất xa việc làm sạch thảm của bạn với TV và hầu hết mọi người không xem máy giặt của họ để giải trí. Nhưng tất cả các thiết bị này đều có chung hành vi là có thể cắm vào ổ cắm trên tường.

Đó là những gì Giao diện giúp bạn.
Các hành vi thống nhất có thể được thực hiện bởi nhiều Lớp đối tượng khác nhau, mà không cần đến các biến chứng của Kế thừa.


1

Thuật ngữ "lập trình cho một giao diện" là mở cho nhiều giải thích. Giao diện trong phát triển phần mềm là một từ rất phổ biến. Dưới đây là cách tôi giải thích khái niệm này cho các nhà phát triển cơ sở mà tôi đã đào tạo qua nhiều năm.

Trong kiến ​​trúc phần mềm có một loạt các ranh giới tự nhiên. Ví dụ phổ biến bao gồm

  • Ranh giới mạng giữa các quá trình máy khách và máy chủ
  • Ranh giới API giữa ứng dụng và thư viện bên thứ ba
  • Ranh giới mã nội bộ giữa các lĩnh vực kinh doanh khác nhau trong chương trình

Vấn đề là, khi những ranh giới tự nhiên này tồn tại, chúng được xác định và hợp đồng về cách thức hành vi của ranh giới đó được chỉ định. Bạn kiểm tra phần mềm của mình không phải bằng cách "phía bên kia" có hoạt động hay không, nhưng liệu các tương tác của bạn có phù hợp với đặc điểm kỹ thuật hay không.

Hậu quả của việc này là:

  • Các thành phần bên ngoài có thể được trao đổi miễn là chúng thực hiện các đặc điểm kỹ thuật
  • Điểm tự nhiên cho các bài kiểm tra đơn vị để xác nhận hành vi chính xác
  • Kiểm tra tích hợp trở nên quan trọng - là đặc tả mơ hồ?
  • Là một nhà phát triển, bạn có một thế giới quan tâm nhỏ hơn khi thực hiện một nhiệm vụ cụ thể

Trong khi phần lớn điều này có thể liên quan đến các lớp và giao diện, điều quan trọng là phải nhận ra rằng nó cũng liên quan đến các mô hình dữ liệu, giao thức mạng và theo cách tổng quát hơn làm việc với nhiều nhà phát triển

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.