Phạm vi mức gói Java có hữu ích không?


11

Tôi hiểu ý tưởng về phạm vi gói, và đôi khi thậm chí đã nghĩ rằng tôi muốn nó. Tuy nhiên, mỗi lần tôi cố gắng sử dụng nó, tôi phát hiện ra nó không phù hợp với nhu cầu mà tôi nghĩ nó sẽ phục vụ.

Vấn đề chính của tôi dường như luôn luôn là những điều mà tôi muốn giới hạn phạm vi không bao giờ nằm ​​trong cùng một gói. Về mặt khái niệm, tất cả chúng có thể được liên kết, nhưng việc phân chia dữ liệu hợp lý trong ứng dụng có chúng như các gói con riêng biệt của một gói lớn hơn.

Chẳng hạn, tôi có thể có một mô hình Mission và tôi chỉ muốn các công cụ Mission khác, như MissionService của tôi, sử dụng một số phương thức. Tuy nhiên, tôi kết thúc với Missions.models và Missions.service dưới dạng các gói của mình, vì vậy MissionModel và MissionService không cùng phạm vi gói. Dường như không có trường hợp nào các gói chứa một cách thích hợp những thứ mà tôi muốn có quyền nâng cao mà không bao gồm nhiều thứ tôi không muốn có những quyền đó; và hiếm khi tôi cảm thấy lợi thế của gói phạm vi một phương pháp biện minh cho việc sửa đổi kiến ​​trúc dự án của tôi để đặt mọi thứ trong cùng một gói. Thường thì các khía cạnh hoặc một số loại đảo ngược của điều khiển hóa ra lại là cách tiếp cận tốt hơn cho bất kỳ vấn đề nào mà tôi đã xem xét ngắn gọn về phạm vi gói.

Tôi tò mò hơn là điều này thường được coi là đúng đối với tất cả các nhà phát triển Java hoặc chỉ là một công việc tôi làm. Là phạm vi gói được sử dụng nhiều trong thế giới thực? Có nhiều trường hợp nó được coi là hình thức tốt để sử dụng, hoặc nó được xem chủ yếu là một hành vi di sản hiếm khi được khai thác trong phát triển hiện đại?

Tôi không hỏi bất cứ điều gì về lý do tại sao phạm vi riêng tư gói là mặc định, tôi hỏi khi nào nên sử dụng nó bất kể mặc định. Hầu hết các cuộc thảo luận về lý do tại sao nó không thực sự được đưa vào khi phạm vi gói thực sự hữu ích, thay vào đó chỉ tranh luận đơn giản là tại sao hai phạm vi thường được sử dụng khác không nên mặc định để gói thắng theo quá trình loại bỏ. Ngoài ra, câu hỏi của tôi là về tình trạng phát triển hiện tại . Cụ thể, chúng tôi đã phát triển đến mức các công cụ và mô hình khác làm cho phạm vi gói trở nên ít hữu ích hơn khi nó trở lại khi quyết định làm cho nó mặc định có ý nghĩa.


Thử nghiệm suy nghĩ: các chương trình Java sẽ trông như thế nào nếu bạn không có các gói? Có thể là bạn chưa xem hoặc làm việc trên bất kỳ chương trình Java lớn nào .
Robert Harvey

Nhìn vào mã cho java.util.Stringjava.util.Integer.


2
Câu trả lời được bình chọn hàng đầu cho câu hỏi trùng lặp bao gồm việc sử dụng quan trọng nhất cho gói riêng tư, hoàn toàn trả lời tại sao nó hữu ích.

2
Tôi hoàn toàn không bị thuyết phục bởi bản sao. Câu hỏi này là "Phạm vi gói là gì?" trong khi người kia đang hỏi (trong số những thứ khác) "Phạm vi gói được đưa ra là mặc định, phạm vi riêng là gì?" Cộng với câu trả lời được chấp nhận trên bản dupe và câu trả lời được chấp nhận ở đây đang tạo ra những điểm hoàn toàn khác nhau.
Ixrec

Câu trả lời:


2

Trong suốt java.util.*gói có các trường hợp mã được viết với bảo vệ mức gói. Ví dụ: bit này của java.util.String- một hàm tạo trong Java 6 :

// Package private constructor which shares value array for speed.
String(int offset, int count, char value[]) {
    this.value = value;
    this.offset = offset;
    this.count = count;
}

hoặc phương thức getChars này :

/** Copy characters from this string into dst starting at dstBegin. 
    This method doesn't perform any range checking. */
void getChars(char dst[], int dstBegin) {
    System.arraycopy(value, offset, dst, dstBegin, count);
}

Lý do cho điều này là vì các nhà thiết kế mã (và java.util.*có thể được coi là một thư viện khá lớn) muốn có khả năng có hiệu suất nhanh hơn với chi phí mất an toàn khác nhau (kiểm tra phạm vi trên mảng, truy cập trực tiếp vào các trường khác có nghĩa là việc thực hiện thay vì giao diện, 'không an toàn' truy cập vào các phần của lớp được coi là bất biến thông qua các phương thức công khai).

Bằng cách hạn chế các phương thức và trường này chỉ được truy cập bởi các lớp khác trong java.utilgói, nó tạo ra sự kết hợp chặt chẽ hơn giữa chúng nhưng cũng tránh làm lộ các chi tiết triển khai này ra thế giới.

Nếu bạn đang làm việc trên một ứng dụng và không cần phải lo lắng về người dùng khác, bảo vệ cấp gói không phải là điều bạn cần lo lắng. Khác với các khía cạnh khác nhau của độ tinh khiết thiết kế, bạn có thể làm mọi thứ publicvà ổn với điều đó.

Tuy nhiên, nếu bạn đang làm việc trên một thư viện sẽ được người khác sử dụng (hoặc muốn tránh vướng vào các lớp học của bạn quá nhiều để tái cấu trúc sau này), bạn nên sử dụng mức bảo vệ nghiêm ngặt nhất dành cho bạn cho nhu cầu của bạn. Thường thì điều này có nghĩa private. Tuy nhiên, đôi khi, bạn cần tiết lộ một chút chi tiết triển khai cho các lớp khác trong cùng một gói để tránh lặp lại chính mình hoặc để thực hiện thực tế dễ dàng hơn một chút với chi phí ghép nối giữa hai lớp và việc triển khai của chúng. Như vậy, bảo vệ cấp gói.


1
Tôi đã xem qua java.util và tôi thấy điều đó. Tuy nhiên, tôi cũng nhận thấy đó là một thư viện LỚN. Ngày nay, dường như các thư viện lớn hiếm khi được viết mà không sử dụng các gói phụ; và đó là những phân chia gói phụ dẫn đến giới hạn. Tôi không nói java.util là sai, nhưng có vẻ như họ có thể thoát khỏi một gói lớn chỉ vì họ đã TUYỆT VỜI những lập trình viên giỏi mà họ có thể tin tưởng để làm mọi thứ đúng với việc đóng gói khá hạn chế trong một gói; Tôi sẽ cảm thấy trần trụi khi tin rằng nhiều tiềm năng có thể làm xấu đối với các nhà phát triển trung bình có thể làm việc trên các gói tương tự như tôi.
DSollen

1
@dsollen Nếu bạn đào sâu vào Spring và Hibernate hoặc các thư viện lớn tương tự, bạn sẽ tìm thấy những thứ tương tự. Có những lúc bạn cần khớp nối gần hơn. Một người nên có cơ hội để mã xem lại các bài nộp và đảm bảo mọi thứ đều chính xác. Nếu bạn không cần sự kết hợp chặt chẽ hơn hoặc hoàn toàn không tin tưởng các lập trình viên khác, thì các trường công khai hoặc gói hoặc các trường hoặc phương thức được bảo vệ không phải lúc nào cũng phù hợp. Tuy nhiên, điều đó không có nghĩa là nó không hữu ích.

Nói tóm lại, tôi sẽ cố gắng tạo ra các gói hạn chế hơn và không lo lắng về mức độ tối ưu hóa (điều này chỉ lãng phí trong 95% dự án) cho nhu cầu hàng ngày của tôi. Do đó, câu hỏi tiếp theo của tôi là phạm vi mức gói có tiềm năng tối ưu hóa rất cụ thể chỉ được sử dụng trong một số API lớn được sử dụng rộng rãi nhất định không? IE chỉ những người vĩ đại sẽ cần, hoặc muốn, để thư giãn bảo vệ ở mức độ đó? Có lý do nào mà các lập trình viên giỏi làm việc trên API 'tiêu chuẩn' với cơ sở người dùng nhỏ hơn sẽ tìm thấy việc sử dụng này không?
DSollen

Không nhận được bài đăng tiếp theo của tôi đủ nhanh :) Tôi hiểu và thấy điểm bạn đã thực hiện, tôi không tranh luận gì cả. Nhiều hơn chỉ đặt ra một câu hỏi tiếp theo là nó hữu ích cho phần còn lại của chúng tôi, những lập trình viên giỏi, nhưng những người không làm việc với các API lớn như vậy. Cụ thể, nó chủ yếu là một tối ưu hóa với chi phí đánh đổi đóng gói chỉ được thực hiện khi bạn thực sự cần phải điều chỉnh hiệu suất.
DSollen

2
@dsollen không phải là về hiệu suất (mặc dù đó là một lý do để phơi bày việc thực hiện) mà là đóng gói. Bạn làm điều đó để tránh lặp lại mã cho Integer.toString()String.valueOf(int)có thể chia sẻ mã mà không để lộ ra thế giới. Các java.utillớp sẽ làm việc trong buổi hòa nhạc để cung cấp toàn bộ chức năng lớn hơn thay vì là các lớp riêng lẻ hoàn toàn là đảo - chúng được kết hợp trong một gói và chia sẻ chức năng thực hiện thông qua phạm vi cấp gói.

5

Thỉnh thoảng tôi leo thang khả năng hiển thị của các phương thức riêng tư hoặc được bảo vệ để đóng gói để cho phép thử nghiệm Junit truy cập một số chi tiết triển khai không nên truy cập từ bên ngoài.

do bài kiểm tra Junit có cùng gói với bài kiểm tra dưới mục, nó có thể truy cập các chi tiết triển khai này

thí dụ

  public class MyClass {
    public MyClass() {
        this(new MyDependency());
    }

    /* package level to allow junit-tests to insert mock/fake implementations */ 
    MyClass(IDependency someDepenency) {...}
  }

Thật đáng tranh luận nếu đây là một cách sử dụng "tốt" hay không nhưng nó thực dụng


2
Tôi luôn đặt các bài kiểm tra trong một gói khác, nếu không tôi thấy tôi đã để lại một số phương thức chính trong phạm vi mặc định không cho phép bất kỳ ai khác gọi nó ...
soru

Tôi không bao giờ làm điều đó với các phương thức, nhưng đó là một tính năng hay để sử dụng cho các phụ thuộc được tiêm. Ví dụ: khi kiểm tra EJB, EntityManager được tiêm có thể bị chế giễu bằng cách đặt EM cấp độ gói với đối tượng Mock trong đó máy chủ ứng dụng được đưa vào thời gian chạy.
jwenting

Tại sao nâng lên protectedphạm vi gói? protected đã cung cấp truy cập gói. Đó là một chút châm biếm kỳ lạ, nhưng tôi nghĩ họ không muốn yêu cầu khai báo phạm vi hỗn hợp như protected packagescope void doSomething()(cũng yêu cầu một từ khóa mới).
tgm1024 - Monica bị ngược đãi

2

Nó không hữu ích lắm, đặc biệt là mặc định, trong một thế giới nơi mọi người có xu hướng sử dụng tên các gói được chấm chấm như thể chúng là các gói phụ. Do Java không có thứ gọi là gói phụ, nên xyi INTERNals không có nhiều quyền truy cập vào xy hơn so với ab Tên gói giống nhau hoặc khác nhau, khớp một phần không khác gì không có gì chung.

Tôi đoán rằng các nhà phát triển ban đầu không bao giờ mong đợi rằng quy ước sẽ được sử dụng và muốn giữ mọi thứ đơn giản mà không cần thêm sự phức tạp của các gói phân cấp theo kiểu Ada.

Java 9 là loại địa chỉ này, trong đó bạn có thể đặt một số gói trong một mô-đun và chỉ xuất một số gói. Nhưng điều đó sẽ làm cho phạm vi gói thậm chí còn dư thừa hơn.

Trong một thế giới lý tưởng, họ sẽ thay đổi phạm vi mặc định thành 'phạm vi mô-đun, nếu mô-đun chứa tồn tại, nếu không thì phạm vi gói'. Sau đó, bạn có thể sử dụng nó để chia sẻ triển khai trong một mô-đun, cho phép tái cấu trúc mà không phá vỡ các giao diện được xuất. Nhưng có lẽ có một số tinh tế tại sao điều đó sẽ phá vỡ khả năng tương thích ngược, hoặc ngược lại là xấu.


Tôi thấy bình luận java 9 thú vị. Tôi nghĩ rằng một khả năng đơn giản để nhận ra các gói dịch vụ và bằng cách nào đó xác định phạm vi gói liên quan đến một số gói cha mẹ (sử dụng chú thích?) Sẽ tốt.
DSollen

1
@dsollen, có lẽ, nhưng thiên hướng đầu tiên của tôi là chúng ta sẽ bắt đầu mạ vàng các loại lốp xe bằng cái đó. Bước đầu tiên tốt hơn nhiều để làm rõ (mang lại sự an toàn trong một chừng mực nào đó) theo ý kiến ​​của tôi sẽ là phát minh ra một từ khóa phạm vi gói thực tế. Nó thậm chí có thể là "gói" :)
tgm1024 - Monica bị ngược đãi

2

Các loại gắn kết mô tả của bạn là không thực sự là ví dụ tốt nhất về nơi bạn sẽ sử dụng truy cập gói. Gói tiện dụng để tạo các thành phần, mô-đun có thể hoán đổi cho nhau trên một giao diện.

Dưới đây là một ví dụ sơ bộ của một kịch bản. Giả sử mã của bạn được tổ chức lại để xoay quanh sự gắn kết chức năng và thành phần hóa. Có lẽ 3 lọ như:

**MissionManager.jar** - an application which uses the Java Service Provider Interface to load some implementation of missions, possible provided by a 3rd party vendor.

**missions-api.jar** - All interfaces, for services and model
    com...missions
        MissionBuilder.java   - has a createMission method
        Mission.java - interface for a mission domain object
    com...missions.strategy
        ... interfaces that attach strategies to missions ...
    com...missions.reports
        .... interfaces todo with mission reports ....

   **MCo-missionizer-5000.jar** -  provides MCo's Missionizer 5000 brand mission implementation.
       com...missions.mco
                  Mission5000.java - implements the Mission interface.
       com...missions.strategy.mco
              ... strategy stuff etc ...

Bằng cách sử dụng cấp độ gói trong Mission5000.java, bạn có thể không chắc chắn rằng không có gói hoặc thành phần nào khác như MissionManager có thể kết hợp chặt chẽ để thực hiện nhiệm vụ. Đó chỉ là thành phần được cung cấp bởi "bên thứ 3" từ "M co" thực sự tạo ra việc thực hiện một Nhiệm vụ đặc biệt mang thương hiệu riêng. Trình quản lý nhiệm vụ phải gọi MissionBuiler.createMission (...) và sử dụng giao diện đã xác định.

Bằng cách này, nếu thành phần triển khai Nhiệm vụ được thay thế, chúng tôi biết rằng những người triển khai MissionManager.jar đã không bỏ qua api và sử dụng trực tiếp triển khai của MCo.

Do đó, chúng tôi có thể trao đổi MCo-Missionizer5000.jar với một sản phẩm cạnh tranh. (Hoặc có lẽ là Mock để kiểm tra đơn vị Trình quản lý nhiệm vụ)

Ngược lại, MCo có thể che giấu một số bí mật thương mại truyền giáo độc quyền của họ.

(Điều này cũng liên quan đến việc thực thi các ý tưởng Không ổn định và Trừu tượng trong các thành phần.)


-1

Bởi vì phạm vi gói trong java không được thiết kế theo cách phân cấp nên hầu như không hữu ích. Chúng tôi hoàn toàn không sử dụng phạm vi gói và định nghĩa tất cả các lớp là công khai.

Thay vào đó, chúng tôi sử dụng các quy tắc truy cập của riêng mình dựa trên phân cấp gói và tên gói được xác định trước.

Nếu bạn quan tâm đến chi tiết google cho "CODERU-Rules".

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.