it-swarm-vi.com

Do sửa đổi truy cập có vấn đề?

Lý thuyết là các công cụ sửa đổi truy cập cải thiện an toàn mã bởi vì chúng hỗ trợ đóng gói trạng thái bên trong. Khi thực hiện OOP, mọi ngôn ngữ tôi đã sử dụng đều thực hiện một số loại hạn chế truy cập. Tôi thích một số mô hình truy cập tốt hơn so với những người khác.

Tôi thuộc nhóm Java nhà phát triển. Các dự án của chúng tôi dành thời gian để đánh giá mã xem xét các công cụ sửa đổi truy cập, tính phù hợp của chúng và việc sử dụng những thứ như @VisibleForTesting (a Java chú thích). Các dự án của chúng tôi đôi khi cũng dành thời gian để hoàn thiện hoặc khử riêng tư một cái gì đó trong thư viện bên thứ 3 nếu thay đổi mã nguồn là không khả thi.

Tôi đã tìm kiếm nghiên cứu cho thấy việc sử dụng các công cụ sửa đổi truy cập ảnh hưởng đến mật độ lỗi hoặc sự xuất hiện của các lỗi thời gian chạy. Tôi không thể tìm thấy bất kỳ nghiên cứu về nó. Có lẽ Google-Fu của tôi yếu. Bằng chứng cho thấy các công cụ sửa đổi truy cập thực sự cung cấp những lợi ích mà chúng ta cho rằng chúng làm là gì? Đâu là nghiên cứu định lượng các vấn đề với cách sử dụng công cụ sửa đổi truy cập?

39
ahoffer

Để bổ sung cho các câu trả lời xuất sắc hiện có từ Doc Browntừ Gort the Robot , một cách khác để xem xét các hạn chế truy cập là một trong nhiều cách chúng tôi viết chương trình mô đun .

Trong kiến ​​trúc đơn giản nhất, tất cả các biến và dòng mã có thể truy cập được từ tất cả các phạm vi. Điều này sẽ cho phép bạn tập hợp mã theo bất kỳ kết hợp nào bạn muốn và khi chương trình phát triển, số lượng kết hợp tăng theo cấp số nhân, khiến bạn khó có thể suy luận về những gì chương trình sẽ làm.

Để cải thiện điều này, chúng tôi chia các chương trình thành các mô đun có thể tái sử dụng thuộc nhiều loại khác nhau. Mỗi mô-đun có hợp đồng xác định cách thức chương trình có thể tương tác với nó; và triển khai được ẩn khỏi mã khác.

Loại mô-đun đơn giản nhất là một hàm: chữ ký hàm (tên, tham số và kiểu trả về) xác định hợp đồng; và việc thực hiện có thể bao gồm các biến cục bộ và các dòng mã không thể nhảy vào từ bên ngoài hàm. Số lượng kết hợp có thể sau đó giảm từ tất cả các tương tác có thể có của các biến và dòng mã, đến tất cả các tương tác có thể có của các hàm.

Công cụ sửa đổi khả năng hiển thị chỉ đơn giản lặp lại cùng một lợi thế: bằng cách nhóm các hàm và biến thành các lớp hoặc gói, chúng ta có thể cung cấp cho mỗi nhóm một hợp đồng và thực hiện. Số lượng kết hợp có thể giảm hơn nữa, đến các tương tác hợp lệ của các lớp hoặc gói.

Tất cả điều này có thể được thực hiện theo quy ước chứ không phải là một phần của ngôn ngữ - bạn có thể đặt tên cho các biến toàn cục một cách cẩn thận thay vì có phạm vi cục bộ và bạn có thể đặt tên cho các chức năng một cách cẩn thận thay vì có khả năng hiển thị. Các quy ước đó càng phổ biến thì càng có nhiều công cụ hỗ trợ chúng - ví dụ: người kiểm tra ngoại tuyến có thể cho bạn biết nếu bạn đang xác thực hợp đồng và IDE chỉ có thể đề xuất các thành viên "hiển thị".

Để thực sự phổ quát, quy ước cần phải được đưa vào ngôn ngữ và được thi hành bằng công cụ mặc định . Điều này quan trọng nhất khi chia sẻ hoặc bàn giao mã: if most công cụ đọc dấu gạch dưới hàng đầu có nghĩa là "riêng tư cho lớp này", nhưng trình biên dịch/thời gian chạy mặc định không thực thi điều đó, bạn phải đưa ra phán quyết cho dù mã của người khác sử dụng "chính xác". Nếu bạn thấy private trong chương trình Java, bạn có thể đưa ra các giả định khá mạnh mẽ về hợp đồng.

Cuối cùng, đó là về tính linh hoạt trong giao dịch đối với khả năng suy luận và duy trì mã. Cũng giống như có những trường hợp khi sử dụng goto thay vì các hàm giúp giải quyết vấn đề, có những trường hợp truy cập một phương thức riêng tư sẽ hoàn thành công việc. Nếu ngôn ngữ không tạo ra sự đánh đổi bằng những gì nó thi hành, thì lập trình viên phải thực hiện sự đánh đổi tương tự bằng cách họ sử dụng nó.

11
IMSoP

Để tôi cho bạn một ví dụ thực tế về thời điểm các công cụ sửa đổi truy cập "quan trọng" mà tôi gặp phải:

Phần mềm của chúng tôi chủ yếu là python và một cách mà python khác với hầu hết các ngôn ngữ OO khác là không có công cụ sửa đổi truy cập rõ ràng. Thay vào đó, nó là quy ước các phương thức tiền tố và thuộc tính nên riêng tư với dấu gạch dưới.

Một ngày nọ, một nhà phát triển đang làm việc trên một tính năng cụ thể và không thể làm cho nó hoạt động với giao diện của đối tượng mà anh ta đang làm việc. Nhưng anh ta nhận thấy rằng nếu anh ta làm việc với một thuộc tính cụ thể được đánh dấu là riêng tư, anh ta có thể làm những gì anh ta muốn làm. Vì vậy, anh ấy đã làm nó, kiểm tra nó và (không may) nó trượt qua đánh giá mã, và vào nhánh chính.

Tiến nhanh hai năm. Nhà phát triển đó đã chuyển sang. Chúng tôi cập nhật lên phiên bản mới hơn của một thư viện cơ bản. Mã đáng tin cậy đột nhiên ngừng hoạt động. Điều này dẫn đến rất nhiều thông báo gỡ lỗi và gửi lại với một nhóm khác ở múi giờ khác.

Cuối cùng, chúng tôi đã tìm ra vấn đề: các nhà phát triển sở hữu đối tượng cơ bản đó đã thay đổi cách thức hoạt động theo cách rất tinh tế. Đủ tinh tế để không có ngoại lệ được ném, không có lỗi khác xảy ra. Thư viện trở nên dễ vỡ. Điều này xảy ra bởi vì các nhà phát triển của thư viện đó không biết rằng họ đang làm bất cứ điều gì sẽ gây ra bất kỳ rắc rối nào cho bất cứ ai. Họ đã thay đổi một cái gì đó nội bộ, không phải giao diện.

Vì vậy, sau thực tế, chúng tôi đã làm những gì đáng lẽ phải được thực hiện ban đầu: chúng tôi đã yêu cầu các nhà phát triển thư viện thêm một phương thức công khai để giải quyết vấn đề của chúng tôi thay vì lẩm bẩm với nội bộ của các đối tượng của họ.

Vì vậy, đó là những gì sửa đổi truy cập ngăn chặn. Họ đảm bảo rằng sự tách biệt của giao diện và thực hiện là rõ ràng. Nó cho phép người dùng biết chính xác những gì họ có thể làm với lớp một cách an toàn và cho phép các nhà phát triển của lớp thay đổi nội bộ mà không phá vỡ phần mềm của người dùng.

Bạn có thể làm tất cả điều này với quy ước, không bắt buộc, như python cho thấy, nhưng ngay cả khi đó chỉ là quy ước, việc tách biệt công khai/riêng tư là một lợi ích lớn cho khả năng duy trì.

68
Gort the Robot

Theo kinh nghiệm của tôi, lợi ích chính của các công cụ sửa đổi truy cập không phải là số lỗi mà chúng ngăn chặn, vì vậy sẽ không có ý nghĩa gì khi cố gắng thống kê về điều này.

Lợi ích thực sự là, đặc biệt là trong các cơ sở mã lớn, chúng tạo điều kiện cho phân tích tác động thay đổi đối với cơ sở mã theo một số bậc độ lớn. Và đó là điều tôi trải nghiệm hàng ngày. Để thực hiện thay đổi chỉ ảnh hưởng đến các công cụ riêng tư, hậu quả thường có thể được thu hẹp xuống một phần tương đối nhỏ của cơ sở mã. Để thực hiện thay đổi cho các bộ phận công cộng, việc phân tích thường khó hơn, vì thay đổi chức năng công khai trong một mô-đun về mặt lý thuyết có thể ảnh hưởng đến tất cả các mô-đun phụ thuộc.

Tất nhiên, ngay cả một thay đổi đối với một số chức năng riêng tư cũng có thể có hậu quả ở quy mô lớn hơn và không phải mọi thay đổi đối với phương thức công khai đều ảnh hưởng đến toàn bộ cơ sở mã. Nhưng công cụ sửa đổi truy cập cho phép chúng tôi suy luận tốt hơn về những gì và nơi mà phần lớn các thay đổi sẽ có (không có) hiệu ứng mong muốn hoặc không mong muốn, và vì vậy chúng ta hãy thực hiện chính xác những thay đổi đó theo cách hiệu quả hơn.

Hãy để tôi thêm điều đó vào kinh nghiệm của tôi, đối với các chương trình nhỏ với ít hơn, giả sử, 2K dòng mã có hiệu lực nhỏ, nhưng khi bạn làm việc tại một hệ thống có> 200k dòng mã, mọi thứ sẽ khác.

26
Doc Brown

Công cụ sửa đổi truy cập là một kỹ thuật để thực hiện đóng gói. Sử dụng chúng nếu bạn tìm kiếm những lợi ích mà đóng gói cung cấp.

Nói chung, các thực tiễn tốt nhất trong Kỹ thuật phần mềm không bị quyết định bởi các nghiên cứu học thuật hoặc các thí nghiệm mù đôi, đánh giá ngang hàng; họ được quyết định bởi chủ nghĩa thực dụng và kinh nghiệm. Bằng chứng là "nó có hoạt động trong cuộc sống thực không?" Hoặc (thực dụng) "làm lợi ích lớn hơn chi phí?"

Nó cũng phụ thuộc vào mục tiêu của bạn là gì. Nếu bạn đến từ một nền tảng mà sự ngắn gọn có giá trị trên nhân chứng, bạn có thể thấy rằng các công cụ sửa đổi truy cập hoàn toàn không có lợi cho bạn. Nhưng đôi khi bạn không có được sự lựa chọn đó. Khi bạn làm việc với một nhóm các nhà phát triển Java, những việc cần làm thường là những điều phù hợp với sự nhạy cảm của Java.

Khi ở Rome ...

17
Robert Harvey

An toàn và bảo trì sang một bên, nó giữ cho bạn lành mạnh.

Hãy tạo ra thế giới thực này, không có mumbo jumbo lạ mắt. Giả sử bạn có một thư viện lớp để hỗ trợ bạn thực hiện công việc của mình. Bạn biết những gì nó phải làm, bạn chưa làm việc với nó. Vì vậy, bạn thêm nó vào môi trường phát triển của bạn và xem xét những gì trong đó.

Ồ 1200 lớp! Những ... những kẻ này đang nghĩ gì? Tôi chỉ cần làm bất cứ điều gì hợp lý để tôi có thể có được bằng 5, 10 lớp đứng đầu. Và họ cho tôi tất cả những thứ này. Tôi cá là 99% trong số này hoàn toàn không liên quan đến tôi, tại sao họ không chỉ cho tôi thấy những lớp học thực sự quan trọng với tôi?!

Vì vậy, bạn duyệt qua các không gian tên và phát hiện ra một số tên lớp có vẻ đầy hứa hẹn. Nó được gọi là ThisIsTheOneYouActualWantToUse. Tiện như thế nào. Bạn gõ:

ThisIsTheOneYouActuallyWantToUse instance = new ThisIsTheOneYouActuallyWantToUse();
instance.

và IntelliSense cho bạn thấy tất cả các phương thức và thuộc tính mà lớp này cung cấp. Ồ 200 thành viên! Những kẻ này đang nghĩ gì vậy?! Tôi có cần tất cả những thứ đó không? Tôi cá là không. Tại sao họ không chỉ cho tôi thấy những người quan trọng với tôi như một người dùng?

Vân vân. Bạn phàn nàn trong phần còn lại của ngày và bạn về nhà mệt mỏi và bất mãn.

[Biên tập]

Điều này minh họa trải nghiệm của một nhà phát triển ứng dụng sử dụng thư viện bên thứ ba, cố gắng tìm một phương thức hữu ích trong một lớp có liên quan. Lưu ý rằng vấn đề này diễn ra trên tất cả các cấp. Nó có thể là một vấn đề khi một nhà phát triển trong một nhóm đánh giá công việc của người khác. Hãy nghĩ về một người dùng cuối nhận được một menu với một vài trăm mặt hàng tất cả được làm phẳng đến một cấp độ. Hoặc một tài liệu không có chương hoặc đoạn văn, chỉ có dấu câu. Bản đồ của một thành phố có một tên đường (nếu bạn sử dụng số nguyên cho số nhà, điều đó sẽ đơn giản hóa mọi thứ một cách triệt để, ai cần tất cả các khu vực và tên đường ngu ngốc này?).

Là người dùng của người khác làm việc, bạn sẽ luôn tìm kiếm thứ bậc, cho các khối chính đại diện cho một cái gì đó. Bạn cần nhìn thấy khu rừng từ những cái cây để hiểu bất cứ điều gì và phóng to những gì bạn cần trong một vài bước. Công cụ sửa đổi truy cập chỉ đại diện cho một cấp trong chuỗi các tổ chức, hệ thống, ứng dụng, mô-đun, không gian tên, lớp và thành viên.

8
Martin Maat

Tôi không biết về nghiên cứu cụ thể nhưng có một loạt các dự án phần mềm thất bại để nghiên cứu. Một số trong số chúng thất bại vì độ phức tạp của chúng. Độ phức tạp của phần mềm nguyên khối tăng theo cấp số nhân. Giống như tất cả sự tăng trưởng theo cấp số nhân, sự phức tạp cuối cùng vượt quá các nguồn lực sẵn có: Ném càng nhiều nhân lực vào một dự án sẽ không cứu được nó nếu nó không có cấu trúc và quá lớn.

Độ phức tạp giảm đi thông qua mô hình kỹ thuật phần mềm được hình thành trước khi hầu hết chúng ta được sinh ra: Divide et inva.

Đối với điều này, các phần phải là được ghép lỏng lẻo hoặc chúng không phải là các phần độc lập thực sự.

Thats tất cả để có nó. Công cụ sửa đổi truy cập ngăn chặn một cách khớp nối chặt chẽ bằng cách chỉ cung cấp một tập hợp con xác định của các thành viên của chúng ở bên ngoài. Việc thực thi điều này ở cấp độ ngôn ngữ trái ngược với việc biến nó thành quy tắc mã hóa là cần thiết bởi vì các lập trình viên khét tiếng vì thiếu kỷ luật tự giác và các quy tắc mã hóa chỉ được thực thi bởi các đánh giá nghiêm ngặt.

3

Ngoài tất cả những gì người khác đã nói, công cụ sửa đổi truy cập là hoặc có thể là tài liệu quan trọng. Họ ghi lại API dự định là gì. API dự định thường chỉ là một tập hợp con nhỏ của tất cả các phương thức được xác định. Có các phương thức được dán nhãn rõ ràng là "Không dành cho khách hàng sử dụng" giúp tránh các lỗi và tìm ra các phương pháp chính xác để sử dụng. Ngoài ra, hợp đồng đó được kiểm tra bởi trình biên dịch, thật tuyệt!

3
kutschkem

Do sửa đổi truy cập có vấn đề? Có họ làm. Tôi nghĩ rằng câu trả lời với ví dụ python là đủ minh họa. Tuy nhiên, có một số cạm bẫy về điều này, đặc biệt là xem xét cách nó thường được sử dụng trong thế giới Java :

  • Bạn phải trung thực với các sửa đổi truy cập của bạn.
  • Những gì công khai sẽ được coi là công khai bởi bất kỳ người dùng nào trong lớp của bạn. Bạn có thể không thể thay đổi hoặc xóa phương thức công khai nếu người dùng của lớp bạn không được kiểm soát
  • Càng nhiều tầm nhìn, khả năng biến đổi càng quan trọng.
  • Trong các hệ thống lớn hơn, việc tách rời thường được thực hiện bằng cách sử dụng các giao diện chỉ hiển thị các phương thức chung chứ không phải bất kỳ thành viên nào hoặc thậm chí là các triển khai cụ thể

Không có gì làm cho một lớp phức tạp hơn việc có 10 thành viên với một getter và setter tầm thường, thường được tạo tự động. Nó không hữu ích trong bất kỳ cách nào để biến biến foo thành riêng tư và cung cấp một trình thiết lập trực tiếp, không được kiểm tra như thế này

    class Foobar {
      private Foo bar;

      public void setBar(Foo bar) { this.bar=bar; }
      public Foo getBar() { return bar; }
    }

Trong trường hợp này, bạn không thu được gì trên một biến công khai nhưng đã thêm hai phương thức vào lớp của bạn. Và nếu mọi người có thể trực tiếp thiết lập (gần như) tất cả các thành viên của bạn, bạn sẽ không tách rời bằng bất kỳ cách nào. Đồng thời, biết rằng một cái gì đó là bất biến có thể làm cho cuộc sống dễ dàng hơn như một người gọi.

Vì vậy, tôi muốn giới thiệu như sau

  • Nếu bạn có một kiểu nguyên thủy và nó là bất biến, hãy làm cho nó công khai và cuối cùng
  • Đối với các phương thức, hãy cố gắng dựa vào các giao diện để ẩn thông tin, không phải trên các lớp riêng lẻ. Nó cho phép bạn trao đổi một loạt các lớp cộng tác mà không cần phụ thuộc bên ngoài
  • Đối với các biến của các loại phức tạp hãy cố gắng vượt qua chúng trong khi xây dựng (nếu có thể). Tránh các getters và setters tầm thường càng nhiều càng tốt
2
Manziel