Bạn sử dụng chiến lược nào để đặt tên gói trong các dự án Java và tại sao? [đóng cửa]


88

Tôi đã nghĩ về điều này một lúc trước và gần đây nó đã nổi lên trở lại khi cửa hàng của tôi đang làm ứng dụng web Java thực đầu tiên.

Trong phần giới thiệu, tôi thấy có hai chiến lược đặt tên gói chính. (Để rõ ràng, tôi không đề cập đến toàn bộ phần 'domain.company.project' của điều này, tôi đang nói về quy ước gói bên dưới nó.) Dù sao, các quy ước đặt tên gói mà tôi thấy như sau:

  1. Chức năng: Đặt tên cho các gói của bạn theo chức năng của chúng về mặt kiến ​​trúc thay vì nhận dạng của chúng theo lĩnh vực kinh doanh. Một thuật ngữ khác cho điều này có thể được đặt tên theo 'lớp'. Vì vậy, bạn sẽ có một gói * .ui và một gói * .domain và một gói * .orm. Các gói của bạn là các lát ngang chứ không phải dọc.

    Đây là nhiều phổ biến hơn đặt tên logic. Trên thực tế, tôi không tin rằng mình đã từng thấy hoặc nghe nói về một dự án làm được điều này. Tất nhiên, điều này khiến tôi cảm thấy khó chịu (giống như nghĩ rằng bạn đã nghĩ ra một giải pháp cho một vấn đề NP) vì tôi không thông minh cho lắm và tôi cho rằng mọi người phải có lý do tuyệt vời để làm điều đó theo cách họ làm. Mặt khác, tôi không phản đối việc mọi người bỏ lỡ con voi trong phòng tôi chưa bao giờ nghe thấy một lập luận thực tế nào về việc đặt tên gói theo cách này. Nó chỉ có vẻ là tiêu chuẩn trên thực tế.

  2. Hợp lý: Đặt tên cho các gói của bạn theo danh tính miền doanh nghiệp của chúng và đưa mọi lớp liên quan đến phần chức năng theo chiều dọc đó vào gói đó.

    Tôi chưa bao giờ nhìn thấy hoặc nghe nói về điều này, như tôi đã đề cập trước đây, nhưng nó rất có ý nghĩa đối với tôi.

    1. Tôi có xu hướng tiếp cận các hệ thống theo chiều dọc hơn là chiều ngang. Tôi muốn đi vào và phát triển hệ thống Xử lý Đơn hàng, không phải lớp truy cập dữ liệu. Rõ ràng, có nhiều khả năng tôi sẽ chạm vào lớp truy cập dữ liệu trong quá trình phát triển hệ thống đó, nhưng vấn đề là tôi không nghĩ về nó theo cách đó. Tất nhiên, điều này có nghĩa là khi tôi nhận được lệnh thay đổi hoặc muốn triển khai một số tính năng mới, sẽ rất tuyệt nếu tôi không phải đi đánh cá trong một loạt các gói để tìm tất cả các lớp liên quan. Thay vào đó, tôi chỉ xem gói X vì những gì tôi đang làm liên quan đến X.

    2. Từ quan điểm phát triển, tôi thấy việc các gói của bạn ghi lại tên miền doanh nghiệp của bạn hơn là kiến ​​trúc của bạn là một chiến thắng lớn. Tôi cảm thấy rằng miền hầu như luôn là một phần của hệ thống khó tìm hiểu hơn vì kiến ​​trúc của hệ thống, đặc biệt là tại thời điểm này, gần như trở nên nhàm chán trong việc triển khai nó. Thực tế là tôi có thể tìm thấy một hệ thống với loại quy ước đặt tên này và ngay lập tức từ việc đặt tên cho các gói hàng biết rằng nó xử lý các đơn đặt hàng, khách hàng, doanh nghiệp, sản phẩm, v.v. có vẻ khá tiện dụng.

    3. Có vẻ như điều này sẽ cho phép bạn tận dụng tốt hơn nhiều công cụ sửa đổi quyền truy cập của Java. Điều này cho phép bạn xác định rõ ràng hơn nhiều giao diện thành các hệ thống con thay vì thành các lớp của hệ thống. Vì vậy, nếu bạn có một hệ thống con đơn đặt hàng mà bạn muốn duy trì một cách minh bạch, về lý thuyết, bạn có thể không bao giờ cho bất cứ điều gì khác biết rằng nó liên tục bằng cách không phải tạo giao diện công khai cho các lớp bền vững của nó trong lớp dao và thay vào đó đóng gói lớp dao trong chỉ với các lớp mà nó xử lý. Rõ ràng, nếu bạn muốn hiển thị chức năng này, bạn có thể cung cấp một giao diện cho nó hoặc đặt nó ở chế độ công khai. Có vẻ như bạn mất rất nhiều điều này bằng cách chia nhỏ các tính năng của hệ thống thành nhiều gói.

    4. Tôi cho rằng một nhược điểm mà tôi có thể thấy là nó làm cho việc tách các lớp khó khăn hơn một chút. Thay vì chỉ xóa hoặc đổi tên một gói và sau đó thả một gói mới vào vị trí bằng công nghệ thay thế, bạn phải vào và thay đổi tất cả các lớp trong tất cả các gói. Tuy nhiên, tôi không thấy đây là một vấn đề lớn. Có thể là do thiếu kinh nghiệm, nhưng tôi phải tưởng tượng rằng số lần bạn hoán đổi các công nghệ so với số lần bạn truy cập và chỉnh sửa các lát tính năng dọc trong hệ thống của mình.

Vì vậy, tôi đoán câu hỏi sau đó sẽ dành cho bạn, bạn đặt tên cho các gói hàng của mình như thế nào và tại sao? Xin hãy hiểu rằng tôi không nhất thiết nghĩ rằng tôi đã vấp phải con ngỗng vàng hay thứ gì đó ở đây. Tôi khá mới với tất cả những điều này với hầu hết kinh nghiệm học thuật. Tuy nhiên, tôi không thể tìm ra lỗ hổng trong lý luận của mình nên tôi hy vọng tất cả các bạn có thể để tôi có thể tiếp tục.


Những gì tôi đã nghe cho đến nay dường như chỉ ra rằng đó là một cuộc gọi phán xét. Tuy nhiên, hầu hết các câu trả lời không tập trung vào các cân nhắc thực tế. Đó là nhiều hơn những gì tôi đang tìm kiếm. Cảm ơn sự giúp đỡ cho đến nay, mặc dù!
Tim Visher

Tôi không nghĩ đó là một cuộc gọi phán xét. Đầu tiên phân chia theo lớp, sau đó theo chức năng chắc chắn là cách để đi. Tôi đang cập nhật câu trả lời của mình.
eljenso

@eljenso: nói điều đó với các bạn của Eclipse :) Trong eclipse, phân chia đầu tiên là "tính năng", là đơn vị triển khai và cũng là chức năng. Bên trong "tính năng", có các "plugin", thường được chia theo lớp - nhưng các plugin trong cùng "tính năng" phải luôn được triển khai cùng nhau. Nếu bạn chỉ có một đơn vị triển khai, thì việc phân chia theo lớp đầu tiên và chỉ sau đó theo chức năng có thể có ý nghĩa. Với nhiều đơn vị triển khai, bạn không muốn chỉ triển khai lớp trình bày - bạn muốn một phần chức năng bổ sung.
Peter Štibraný

Câu trả lời:


32

Đối với thiết kế gói, đầu tiên tôi chia theo lớp, sau đó theo một số chức năng khác.

Có một số quy tắc bổ sung:

  1. các lớp được xếp chồng lên nhau từ chung chung nhất (dưới cùng) đến cụ thể nhất (trên cùng)
  2. mỗi lớp có một giao diện công khai (trừu tượng)
  3. một lớp chỉ có thể phụ thuộc vào giao diện công khai của một lớp khác (tính đóng gói)
  4. một lớp chỉ có thể phụ thuộc vào nhiều lớp chung hơn (phụ thuộc từ trên xuống dưới)
  5. tốt nhất là một lớp phụ thuộc vào lớp ngay bên dưới nó

Vì vậy, đối với một ứng dụng web chẳng hạn, bạn có thể có các lớp sau trong tầng ứng dụng của mình (từ trên xuống dưới):

  • lớp trình bày: tạo giao diện người dùng sẽ được hiển thị trong lớp khách hàng
  • lớp ứng dụng: chứa logic cụ thể cho một ứng dụng, trạng thái
  • lớp dịch vụ: nhóm chức năng theo miền, không trạng thái
  • lớp tích hợp: cung cấp quyền truy cập vào lớp phụ trợ (db, jms, email, ...)

Đối với bố cục gói kết quả, đây là một số quy tắc bổ sung:

  • gốc của mọi tên gói là <prefix.company>.<appname>.<layer>
  • giao diện của một lớp được chia tách thêm theo chức năng: <root>.<logic>
  • việc triển khai private của một lớp được bắt đầu bằng private: <root>.private

Đây là một bố cục ví dụ.

Lớp trình bày được chia theo công nghệ chế độ xem và tùy chọn theo (nhóm) ứng dụng.

com.company.appname.presentation.internal
com.company.appname.presentation.springmvc.product
com.company.appname.presentation.servlet
...

Lớp ứng dụng được chia thành các trường hợp sử dụng.

com.company.appname.application.lookupproduct
com.company.appname.application.internal.lookupproduct
com.company.appname.application.editclient
com.company.appname.application.internal.editclient
...

Lớp dịch vụ được chia thành các miền nghiệp vụ, chịu ảnh hưởng của logic miền trong tầng phụ trợ.

com.company.appname.service.clientservice
com.company.appname.service.internal.jmsclientservice
com.company.appname.service.internal.xmlclientservice
com.company.appname.service.productservice
...

Lớp tích hợp được chia thành 'công nghệ' và các đối tượng truy cập.

com.company.appname.integration.jmsgateway
com.company.appname.integration.internal.mqjmsgateway
com.company.appname.integration.productdao
com.company.appname.integration.internal.dbproductdao
com.company.appname.integration.internal.mockproductdao
...

Ưu điểm của việc tách các gói như thế này là dễ quản lý độ phức tạp hơn, đồng thời tăng khả năng kiểm tra và khả năng tái sử dụng. Mặc dù nó có vẻ như rất nhiều chi phí, nhưng theo kinh nghiệm của tôi, nó thực sự đến rất tự nhiên và mọi người làm việc trên cấu trúc này (hoặc tương tự) sẽ thu thập nó trong vài ngày.

Tại sao tôi nghĩ rằng cách tiếp cận theo chiều dọc là không tốt như vậy?

Trong mô hình phân lớp, một số mô-đun cấp cao khác nhau có thể sử dụng cùng một mô-đun cấp thấp hơn. Ví dụ: bạn có thể xây dựng nhiều khung nhìn cho cùng một ứng dụng, nhiều ứng dụng có thể sử dụng cùng một dịch vụ, nhiều dịch vụ có thể sử dụng cùng một cổng. Bí quyết ở đây là khi di chuyển qua các lớp, mức độ chức năng sẽ thay đổi. Các mô-đun trong các lớp cụ thể hơn không ánh xạ 1-1 trên các mô-đun từ lớp chung hơn, bởi vì các mức chức năng mà chúng thể hiện không ánh xạ 1-1.

Khi bạn sử dụng phương pháp tiếp cận theo chiều dọc cho thiết kế gói, tức là trước tiên bạn phân chia theo chức năng, sau đó bạn buộc tất cả các khối xây dựng với các mức chức năng khác nhau vào cùng một 'áo khoác chức năng'. Bạn có thể thiết kế mô-đun chung của mình cho mô-đun cụ thể hơn. Nhưng điều này vi phạm nguyên tắc quan trọng rằng lớp tổng quát hơn không được biết về các lớp cụ thể hơn. Ví dụ, lớp dịch vụ không nên được mô hình hóa theo các khái niệm từ lớp ứng dụng.


"Ưu điểm của việc tách các gói như thế này là dễ quản lý độ phức tạp hơn và nó làm tăng khả năng kiểm tra và khả năng tái sử dụng." Bạn thực sự không đi sâu vào lý do tại sao lại như vậy.
xoàiDrunk

1
Bạn không đi vào lý do tại sao nó không phải như vậy. Nhưng dù sao thì ... các quy tắc tổ chức khá rõ ràng và đơn giản, vì vậy nó giảm bớt sự phức tạp. Nó có thể kiểm tra và tái sử dụng nhiều hơn vì chính tổ chức. Mọi người có thể thử và sau đó chúng ta có thể đồng ý hoặc không đồng ý. Tôi giải thích ngắn gọn một số nhược điểm của phương pháp tiếp cận theo chiều dọc, ngầm đưa ra lý do tại sao tôi nghĩ rằng cách tiếp cận theo chiều ngang dễ dàng hơn.
eljenso

8
Bài viết này giải thích khá dễ hiểu lý do tại sao tốt hơn nên sử dụng gói theo tính năng thay vì gói theo lớp .
Tom

@eljenso "Bạn không tìm hiểu lý do tại sao nó không phải như vậy", đang cố gắng chuyển gánh nặng bằng chứng? Tôi nghĩ rằng bài viết được đăng bởi Tom giải thích rất tuyệt vời tại sao gói theo tính năng lại tốt hơn và tôi đang gặp khó khăn trong việc "Tôi không nghĩ đó là lời kêu gọi phán xét. Đầu tiên chia theo lớp, sau đó theo chức năng chắc chắn là cách để đi ”một cách nghiêm túc.
pka

18

Tôi thấy mình luôn tuân thủ các nguyên tắc thiết kế bao bì của Uncle Bob . Nói tóm lại, các lớp sẽ được sử dụng lại cùng nhau và thay đổi cùng nhau (vì cùng một lý do, ví dụ như thay đổi phụ thuộc hoặc thay đổi khung) nên được đặt trong cùng một gói. IMO, phân tích chức năng sẽ có cơ hội đạt được những mục tiêu này tốt hơn phân tích theo ngành dọc / doanh nghiệp cụ thể trong hầu hết các ứng dụng.

Ví dụ: một phần ngang của các đối tượng miền có thể được sử dụng lại bởi các loại giao diện người dùng hoặc thậm chí các ứng dụng khác nhau và một phần ngang của giao diện người dùng web có thể thay đổi cùng nhau khi khung web bên dưới cần được thay đổi. Mặt khác, thật dễ dàng để hình dung hiệu ứng gợn sóng của những thay đổi này trên nhiều gói nếu các lớp trên các khu vực chức năng khác nhau được nhóm lại trong các gói đó.

Rõ ràng, không phải tất cả các loại phần mềm đều giống nhau và việc phân tích theo chiều dọc có thể có ý nghĩa (về mặt đạt được các mục tiêu về khả năng tái sử dụng và khả năng đóng cửa để thay đổi) trong một số dự án nhất định.


Cảm ơn, điều này rất rõ ràng. Như tôi đã nói trong câu hỏi, tôi có cảm giác như số lần bạn hoán đổi công nghệ ít hơn nhiều so với việc bạn chuyển đổi theo chiều dọc của chức năng. Đây không phải là kinh nghiệm của bạn?
Tim Visher

Nó không chỉ là về công nghệ. Điểm số 1 của bài đăng ban đầu của bạn chỉ có ý nghĩa nếu các phần dọc là các ứng dụng / dịch vụ độc lập giao tiếp với nhau thông qua một số giao diện (SOA, nếu bạn muốn). thêm bên dưới ...
Buu Nguyen

Bây giờ khi bạn đi vào chi tiết của từng ứng dụng / dịch vụ chi tiết có gui / doanh nghiệp / dữ liệu riêng, tôi khó có thể tưởng tượng rằng những thay đổi theo chiều dọc, có thể là về công nghệ, sự phụ thuộc, quy tắc / quy trình làm việc, ghi nhật ký. , bảo mật, phong cách giao diện người dùng, có thể được cách ly hoàn toàn với các lát khác.
Buu Nguyen

Tôi sẽ quan tâm đến phản hồi của bạn về câu trả lời của tôi
i3ensays

@BuuNguyen Liên kết đến các nguyên tắc thiết kế gói bị hỏng.
johnnieb

5

Thường có cả hai mức độ phân chia hiện tại. Từ trên xuống có đơn vị triển khai. Chúng được đặt tên 'một cách hợp lý' (theo thuật ngữ của bạn, hãy nghĩ đến các tính năng của Eclipse). Bên trong đơn vị triển khai, bạn có phân chia chức năng của các gói (hãy nghĩ đến các plugin Eclipse).

Ví dụ, tính năng là com.feature, và nó bao gồm com.feature.client, com.feature.corecom.feature.uiplugin. Bên trong các plugin, tôi có rất ít sự phân chia cho các gói khác, mặc dù điều đó cũng không bất thường.

Cập nhật: Btw, có một bài nói chuyện tuyệt vời của Juergen Hoeller về tổ chức mã tại InfoQ: http://www.infoq.com/presentations/code-organization-large-projects . Juergen là một trong những kiến ​​trúc sư của Spring, và biết rất nhiều về công cụ này.


Tôi không hoàn toàn làm theo. Thông thường, bạn có thể thấy com.apache.wicket.x trong đó x là hàm hoặc logic. Tôi thường không thấy com.x. Tôi đoán bạn đang nói rằng bạn sẽ sử dụng cấu trúc com.company.project.feature.layer? Bạn có lý do?
Tim Visher 10/02/09

Lý do là "com.company.project.feature" là đơn vị triển khai. Ở cấp độ này, một số tính năng là tùy chọn và có thể bị bỏ qua (tức là không được triển khai). Tuy nhiên, tính năng bên trong, mọi thứ không phải là tùy chọn và bạn thường muốn có tất cả. Ở đây, nó có ý nghĩa hơn khi phân chia theo từng lớp.
Peter Štibraný

4

Hầu hết các dự án java tôi đã làm việc trên các gói java theo chức năng trước, sau đó về mặt logic.

Thông thường, các bộ phận đủ lớn để chúng được chia thành các tạo tác xây dựng riêng biệt, nơi bạn có thể đặt chức năng cốt lõi vào một lọ, apis vào một lọ khác, nội dung giao diện người dùng web vào một tệp chiến tranh, v.v.


Nó trông như thế nào? domain.company.project. Chức năng.logicalSlice?
Tim Visher

khá nhiều! vddcpweb.profiles, dcpweb.registration, dcpapis, dcppersistence, v.v ... thường hoạt động khá tốt, số dặm của bạn có thể thay đổi. cũng phụ thuộc nếu bạn đang sử dụng mô hình miền - nếu bạn có nhiều miền, trước tiên bạn có thể muốn chia theo miền.
Ben Hardy

Cá nhân tôi thích ngược lại, hợp lý sau đó chức năng. táo.rpc, apple.model và sau đó là banana.model, banana.store
mP.

Có nhiều giá trị hơn khi có thể nhóm tất cả nội dung táo với nhau hơn là nhóm tất cả nội dung web lại với nhau.
mP.

3

Các gói phải được biên dịch và phân phối như một đơn vị. Khi xem xét những lớp nào thuộc về một gói, một trong những tiêu chí quan trọng là sự phụ thuộc của nó. Lớp này phụ thuộc vào những gói nào khác (bao gồm cả thư viện của bên thứ ba). Một hệ thống được tổ chức tốt sẽ tập hợp các lớp có sự phụ thuộc tương tự vào một gói. Điều này hạn chế tác động của một thay đổi trong một thư viện, vì chỉ một số gói được xác định rõ sẽ phụ thuộc vào nó.

Có vẻ như hệ thống dọc, logic của bạn có thể có xu hướng "bôi nhọ" sự phụ thuộc trên hầu hết các gói. Có nghĩa là, nếu mọi tính năng được đóng gói dưới dạng lát dọc, thì mọi gói sẽ phụ thuộc vào mọi thư viện của bên thứ ba mà bạn sử dụng. Bất kỳ thay đổi nào đối với thư viện đều có thể xảy ra trong toàn bộ hệ thống của bạn.


Ah. Vì vậy, một điều mà thực hiện các lát cắt ngang là bảo vệ bạn khỏi các nâng cấp thực tế trong các thư viện bạn đang sử dụng. Tôi nghĩ bạn nói đúng rằng các lát cắt dọc sẽ bôi nhọ mọi phụ thuộc mà hệ thống của bạn có trên mọi gói. Tại sao điều này lại là một vấn đề lớn như vậy?
Tim Visher

Đối với một "tính năng", nó không phải là một vấn đề lớn. Họ có xu hướng dễ bay hơi hơn và có nhiều phụ thuộc hơn. Mặt khác, mã "khách hàng" này có xu hướng có khả năng tái sử dụng thấp. Đối với một thứ mà bạn có ý định trở thành một thư viện có thể tái sử dụng, việc cô lập các khách hàng khỏi mọi thay đổi nhỏ là một khoản đầu tư tuyệt vời.
erickson

3

Cá nhân tôi thích nhóm các lớp một cách hợp lý sau đó trong đó bao gồm một gói con cho mỗi sự tham gia chức năng.

Mục tiêu của bao bì

Các gói sau cùng là để nhóm mọi thứ lại với nhau - ý tưởng là các lớp có liên quan sống gần nhau. Nếu họ sống trong cùng một gói, họ có thể tận dụng gói riêng tư để hạn chế khả năng hiển thị. Vấn đề là việc gộp tất cả nội dung về chế độ xem và tính năng của bạn vào một gói có thể dẫn đến nhiều lớp bị trộn lẫn vào một gói duy nhất. Điều hợp lý tiếp theo cần làm là tạo chế độ xem, tính bền bỉ, sử dụng các gói con và lớp cấu trúc lại cho phù hợp. Không may, phạm vi bảo vệ và gói riêng tư gói không hỗ trợ khái niệm về gói và gói phụ hiện tại vì điều này sẽ hỗ trợ việc thực thi các quy tắc hiển thị như vậy.

Bây giờ tôi thấy giá trị trong việc phân tách thông qua chức năng cho biết giá trị nào có để nhóm tất cả các nội dung liên quan đến chế độ xem. Những thứ trong chiến lược đặt tên này trở nên ngắt kết nối với một số lớp trong chế độ xem trong khi những lớp khác vẫn tồn tại, v.v.

Một ví dụ về cấu trúc đóng gói hợp lý của tôi

Đối với mục đích minh họa, hãy đặt tên cho hai mô-đun - hãy sử dụng mô-đun tên như một khái niệm nhóm các lớp dưới một nhánh cụ thể của cây pacckage.

apple.model apple.store banana.model banana.store

Ưu điểm

Một khách hàng sử dụng Banana.store.BananaStore chỉ được tiếp xúc với chức năng mà chúng tôi muốn cung cấp. Phiên bản ngủ đông là một chi tiết triển khai mà họ không cần biết cũng như không nên nhìn thấy các lớp này vì chúng thêm lộn xộn vào các hoạt động lưu trữ.

Các ưu điểm khác về logic v Chức năng

Càng về phía gốc, phạm vi càng rộng và những thứ thuộc về một gói bắt đầu thể hiện ngày càng nhiều phụ thuộc vào những thứ thuộc về mô-đun toher. Nếu người ta kiểm tra mô-đun "chuối" chẳng hạn, hầu hết các phụ thuộc sẽ bị giới hạn trong mô-đun đó. Trên thực tế, hầu hết các trợ giúp trong "banana" sẽ không được tham chiếu ở bên ngoài phạm vi gói này.

Tại sao chức năng?

Giá trị nào mà người ta đạt được bằng cách gộp mọi thứ lại dựa trên chức năng. Hầu hết các lớp trong trường hợp này là độc lập với nhau mà ít hoặc không cần tận dụng các phương thức hoặc lớp riêng của gói. Việc tái cấu trúc chúng thành các gói con của riêng chúng chỉ thu được ít lợi ích nhưng sẽ giúp giảm bớt sự lộn xộn.

Nhà phát triển thay đổi hệ thống

Khi các nhà phát triển được giao nhiệm vụ thực hiện những thay đổi lớn hơn một chút thì có vẻ ngớ ngẩn rằng họ có thể có những thay đổi bao gồm các tệp từ tất cả các khu vực của cây gói. Với cách tiếp cận có cấu trúc logic, các thay đổi của chúng mang tính cục bộ hơn trong cùng một phần của cây gói, điều này có vẻ đúng.


"Khi các nhà phát triển [...] tệp từ tất cả các khu vực của cây gói." Bạn đúng khi nói điều này có vẻ ngớ ngẩn. Bởi vì đây chính xác là điểm của việc phân lớp thích hợp: các thay đổi không xuất hiện trong toàn bộ cấu trúc của ứng dụng.
eljenso

Cảm ơn vì lời khuyên. Tôi không chắc mình đang hiểu bạn rõ ràng. Tôi tin rằng bạn đang cố gắng tranh luận cho các gói Logic, tôi không rõ lý do chính xác. Bạn có thể cố gắng làm cho câu trả lời của mình rõ ràng hơn một chút, có thể là ghi lại? Cảm ơn!
Tim Visher 19/02/09

3

Cả cách tiếp cận chức năng (kiến trúc) và logic (tính năng) đối với bao bì đều có một vị trí. Nhiều ứng dụng ví dụ (những ứng dụng được tìm thấy trong sách văn bản, v.v.) tuân theo cách tiếp cận chức năng là đặt bản trình bày, dịch vụ kinh doanh, ánh xạ dữ liệu và các lớp kiến ​​trúc khác thành các gói riêng biệt. Trong các ứng dụng ví dụ, mỗi gói thường chỉ có một vài hoặc chỉ một lớp.

Cách tiếp cận ban đầu này là tốt vì một ví dụ có sẵn thường phục vụ: 1) phác thảo khái niệm kiến ​​trúc của khung được trình bày, 2) được thực hiện với một mục đích logic duy nhất (ví dụ: thêm / xóa / cập nhật / xóa vật nuôi khỏi phòng khám) . Vấn đề là nhiều độc giả coi đây là tiêu chuẩn không có giới hạn.

Khi một ứng dụng "kinh doanh" mở rộng để bao gồm ngày càng nhiều tính năng, việc tuân theo cách tiếp cận chức năng trở thành một gánh nặng. Mặc dù tôi biết nơi để tìm kiếm các loại dựa trên lớp kiến ​​trúc (ví dụ: bộ điều khiển web trong gói "web" hoặc "ui", v.v.), việc phát triển một tính năng logic bắt đầu yêu cầu nhảy qua lại giữa nhiều gói. Điều này ít nhất là cồng kềnh, nhưng nó còn tệ hơn thế.

Vì các loại liên quan về mặt logic không được đóng gói cùng nhau, nên API được công khai quá mức; sự tương tác giữa các kiểu liên quan đến logic buộc phải ở chế độ 'công khai' để các kiểu có thể nhập và tương tác với nhau (khả năng giảm thiểu đến mức mặc định / khả năng hiển thị gói bị mất).

Nếu tôi đang xây dựng một thư viện khung, bằng mọi cách, các gói của tôi sẽ tuân theo cách tiếp cận đóng gói chức năng / kiến ​​trúc. Người tiêu dùng API của tôi thậm chí có thể đánh giá cao rằng các câu lệnh nhập của họ chứa gói trực quan được đặt tên theo kiến ​​trúc.

Ngược lại, khi xây dựng một ứng dụng kinh doanh tôi sẽ đóng gói theo tính năng. Tôi không gặp vấn đề gì khi đặt Widget, WidgetService và WidgetController trong cùng một gói " com.myorg.widget. " Và sau đó tận dụng khả năng hiển thị mặc định (và có ít câu lệnh nhập hơn cũng như các phụ thuộc giữa các gói).

Tuy nhiên, vẫn có những trường hợp chéo. Nếu WidgetService của tôi được nhiều miền logic (tính năng) sử dụng, tôi có thể tạo gói " com.myorg.common.service. ". Cũng có một cơ hội tốt là tôi tạo các lớp với ý định có thể sử dụng lại trên các tính năng và kết thúc bằng các gói như " com.myorg.common.ui.helpers. " Và " com.myorg.common.util. ". Tôi thậm chí có thể kết thúc việc di chuyển tất cả các lớp "chung" sau này trong một dự án riêng biệt và đưa chúng vào ứng dụng kinh doanh của tôi dưới dạng phụ thuộc myorg-commons.jar.


2

Nó phụ thuộc vào mức độ chi tiết của các quy trình logic của bạn?

Nếu chúng độc lập, bạn thường có một dự án mới cho chúng trong quyền kiểm soát nguồn, thay vì một gói mới.

Dự án mà tôi đang thực hiện hiện tại đang gặp lỗi trong việc phân tách hợp lý, có một gói cho khía cạnh jython, một gói cho công cụ quy tắc, các gói cho foo, bar, binglewozzle, v.v. Tôi đang xem xét có các trình phân tích cú pháp XML cụ thể / write cho mỗi mô-đun trong gói đó, thay vì có một gói XML (mà tôi đã làm trước đó), mặc dù vẫn sẽ có một gói XML cốt lõi để chia sẻ logic. Tuy nhiên, một lý do cho điều này là nó có thể có thể mở rộng (plugin) và do đó mỗi plugin sẽ cần xác định mã XML (hoặc cơ sở dữ liệu, v.v.) của nó, vì vậy việc tập trung hóa điều này có thể gây ra các vấn đề sau này.

Cuối cùng, đó có vẻ là cách nó có vẻ hợp lý nhất đối với dự án cụ thể. Tuy nhiên, tôi nghĩ thật dễ dàng để đóng gói theo các dòng của sơ đồ phân lớp dự án điển hình. Bạn sẽ kết hợp với sự kết hợp của bao bì hợp lý và chức năng.

Điều cần thiết là không gian tên được gắn thẻ. Một trình phân tích cú pháp XML cho một số chức năng của Jython có thể được gắn thẻ cả Jython và XML, thay vì phải chọn cái này hay cái kia.

Hoặc có thể tôi đang lúng túng.


Tôi không hoàn toàn hiểu quan điểm của bạn. Tôi nghĩ những gì bạn đang nói là bạn chỉ nên làm những gì có ý nghĩa nhất cho dự án của bạn và đối với bạn điều đó hợp lý với một chút chức năng được bổ sung. Có lý do thực tế cụ thể nào cho việc này không?
Tim Visher

Như tôi đã nói, tôi sẽ có các plugin để tăng cường chức năng tích hợp. Một plugin sẽ phải có khả năng phân tích cú pháp và viết XML của nó (và cuối cùng là vào DB) cũng như cung cấp chức năng. Do đó, tôi có com.xx.feature.xml hoặc com.xx.xml.feature? Trước đây có vẻ gọn gàng hơn.
JeeBee 11/02/09

2

Tôi cố gắng thiết kế các cấu trúc gói theo cách mà nếu tôi vẽ một biểu đồ phụ thuộc, thì sẽ dễ dàng theo dõi và sử dụng một mẫu nhất quán, với càng ít tham chiếu vòng tròn càng tốt.

Đối với tôi, điều này dễ dàng hơn nhiều để duy trì và hình dung trong một hệ thống đặt tên dọc hơn là theo chiều ngang. nếu component1.display có tham chiếu đến component2.dataaccess, điều này sẽ tạo ra nhiều chuông cảnh báo hơn nếu display.component1 có tham chiếu đến dataaccess. thành phần2.

Tất nhiên, các thành phần được chia sẻ bởi cả hai đều đi trong gói riêng của chúng.


Như tôi hiểu bạn lúc đó, bạn sẽ ủng hộ quy ước đặt tên theo ngành dọc. Tôi nghĩ đây là những gì một gói * .utils dành cho, khi bạn cần một lớp trên nhiều lát cắt.
Tim Visher

2

Tôi hoàn toàn tuân theo và đề xuất tổ chức hợp lý ("theo tính năng")! Một gói phải tuân theo khái niệm "mô-đun" càng chặt chẽ càng tốt. Tổ chức chức năng có thể trải rộng một mô-đun trên một dự án, dẫn đến ít đóng gói hơn và dễ bị thay đổi trong chi tiết triển khai.

Hãy lấy một plugin Eclipse làm ví dụ: đặt tất cả các khung nhìn hoặc hành động trong một gói sẽ là một mớ hỗn độn. Thay vào đó, mỗi thành phần của một tính năng sẽ chuyển đến gói của tính năng hoặc nếu có nhiều, thành các gói con (featureA.handlers, featureA.preferences, v.v.)

Tất nhiên, vấn đề nằm ở hệ thống gói phân cấp (mà Java có), khiến cho việc xử lý các mối quan tâm trực giao là không thể hoặc ít nhất là rất khó - mặc dù chúng xảy ra ở mọi nơi!


1

Cá nhân tôi muốn đặt tên theo chức năng. Lý do ngắn gọn: nó tránh trùng lặp mã hoặc cơn ác mộng phụ thuộc.

Hãy để tôi nói rõ hơn một chút. Điều gì xảy ra khi bạn đang sử dụng tệp jar bên ngoài, với cây gói riêng của nó? Bạn đang nhập mã (đã biên dịch) vào dự án của mình một cách hiệu quả và với nó là một cây gói (phân tách theo chức năng). Có hợp lý không nếu sử dụng hai quy ước đặt tên cùng một lúc? Không, trừ khi điều đó bị ẩn với bạn. Và nó là, nếu dự án của bạn đủ nhỏ và có một thành phần duy nhất. Nhưng nếu bạn có một số đơn vị logic, có thể bạn không muốn triển khai lại, giả sử mô-đun tải tệp dữ liệu. Bạn muốn chia sẻ nó giữa các đơn vị logic, không có sự phụ thuộc giả tạo giữa các đơn vị không liên quan đến logic và không phải chọn đơn vị nào bạn sẽ đặt công cụ được chia sẻ cụ thể đó vào.

Tôi đoán đây là lý do tại sao đặt tên theo chức năng được sử dụng nhiều nhất trong các dự án tiếp cận hoặc có nghĩa là để tiếp cận, một kích thước nhất định và đặt tên hợp lý được sử dụng trong các quy ước đặt tên lớp để theo dõi vai trò cụ thể, nếu có của mỗi lớp trong gói hàng.

Tôi sẽ cố gắng trả lời chính xác hơn từng quan điểm của bạn về cách đặt tên hợp lý.

  1. Nếu bạn phải đi đánh cá trong các lớp cũ để sửa đổi các chức năng khi bạn thay đổi kế hoạch, đó là một dấu hiệu của sự trừu tượng xấu: bạn nên xây dựng các lớp cung cấp một chức năng được xác định rõ ràng, có thể xác định trong một câu ngắn gọn. Chỉ một số lớp cấp cao nhất nên tập hợp tất cả những thứ này lại để phản ánh thông tin kinh doanh của bạn. Bằng cách này, bạn sẽ có thể sử dụng lại nhiều mã hơn, bảo trì dễ dàng hơn, tài liệu rõ ràng hơn và ít vấn đề phụ thuộc hơn.

  2. Điều đó chủ yếu phụ thuộc vào cách bạn tìm kiếm dự án của mình. Chắc chắn, quan điểm logic và chức năng là trực giao. Vì vậy, nếu bạn sử dụng một quy ước đặt tên, bạn cần áp dụng quy ước còn lại cho các tên lớp để giữ một số thứ tự hoặc phân tách từ quy ước đặt tên này sang quy ước đặt tên khác ở một số độ sâu.

  3. Các công cụ sửa đổi quyền truy cập là một cách tốt để cho phép các lớp khác hiểu quá trình xử lý của bạn truy cập vào các phần tử trong lớp của bạn. Mối quan hệ logic không có nghĩa là sự hiểu biết về các ràng buộc thuật toán hoặc đồng thời. Chức năng có thể, mặc dù nó không. Tôi rất mệt mỏi với các công cụ sửa đổi truy cập không phải là công khai và riêng tư, bởi vì chúng thường che giấu việc thiếu cấu trúc thích hợp và trừu tượng hóa lớp.

  4. Trong các dự án thương mại, lớn, việc thay đổi công nghệ xảy ra thường xuyên hơn bạn tưởng. Ví dụ, tôi đã phải thay đổi 3 lần trình phân tích cú pháp XML, 2 lần công nghệ bộ nhớ đệm và 2 lần phần mềm định vị địa lý. Thật tốt là tôi đã giấu tất cả các chi tiết phức tạp trong một gói chuyên dụng ...


1
Hãy tha thứ cho tôi nếu tôi sai, nhưng với tôi thì có vẻ như bạn đang nói nhiều hơn về việc phát triển một khuôn khổ dành cho nhiều người sử dụng. Tôi nghĩ các quy tắc thay đổi giữa kiểu phát triển đó và đang phát triển hệ thống người dùng cuối. Liệu tôi có sai?
Tim Visher

Tôi nghĩ rằng đối với các lớp phổ biến được sử dụng bởi nhiều lát dọc, việc có một cái gì đó giống như một utilsgói là hợp lý. Sau đó, bất kỳ lớp nào cần nó có thể đơn giản phụ thuộc vào gói đó.
Tim Visher

Một lần nữa, quy mô lại quan trọng: khi một dự án trở nên đủ lớn, nó cần được nhiều người hỗ trợ và một số loại chuyên môn hóa sẽ xảy ra. Để dễ dàng tương tác và ngăn ngừa sai lầm, việc phân chia dự án thành các phần nhanh chóng trở nên cần thiết. Và gói utils sẽ sớm trở nên khổng lồ!
Varkhan

1

Đó là một thử nghiệm thú vị khi không sử dụng các gói (ngoại trừ gói gốc.)

Câu hỏi đặt ra sau đó là, khi nào và tại sao việc giới thiệu các gói lại có ý nghĩa. Có lẽ, câu trả lời sẽ khác với những gì bạn đã trả lời ở phần đầu của dự án.

Tôi đoán rằng câu hỏi của bạn nảy sinh ở tất cả, bởi vì các gói giống như các danh mục và đôi khi rất khó để quyết định cho cái này hay cái kia. Đôi khi các thẻ sẽ được đánh giá cao hơn khi thông báo rằng một lớp có thể sử dụng được trong nhiều ngữ cảnh.


0

Từ quan điểm thực tế thuần túy, các cấu trúc khả năng hiển thị của java cho phép các lớp trong cùng một gói truy cập các phương thức và thuộc tính với protecteddefaultkhả năng hiển thị, cũng như các lớp public. Sử dụng các phương pháp không công khai từ một lớp mã hoàn toàn khác chắc chắn sẽ là một mùi mã lớn. Vì vậy, tôi có xu hướng đặt các lớp từ cùng một lớp vào cùng một gói.

Tôi không thường sử dụng các phương thức được bảo vệ hoặc mặc định này ở nơi khác - ngoại trừ có thể trong các bài kiểm tra đơn vị cho lớp - nhưng khi tôi làm vậy, nó luôn từ một lớp ở cùng một lớp


Nó không phải là một cái gì đó về bản chất của các hệ thống nhiều lớp luôn phụ thuộc vào lớp tiếp theo? Ví dụ: giao diện người dùng phụ thuộc vào lớp dịch vụ của bạn phụ thuộc vào lớp miền của bạn, v.v. Đóng gói các lát dọc với nhau dường như để bảo vệ chống lại sự phụ thuộc quá nhiều giữa các gói, phải không?
Tim Visher 10/02/09

Tôi gần như không sử dụng các phương pháp được bảo vệ và mặc định. 99% là công cộng hoặc tư nhân. Một số ngoại lệ: (1) khả năng hiển thị mặc định cho các phương thức chỉ được sử dụng bởi các bài kiểm tra đơn vị, (2) phương thức trừu tượng được bảo vệ chỉ được sử dụng từ lớp cơ sở trừu tượng.
Esko Luontola

1
Tim Visher, phụ thuộc giữa các gói không phải là một vấn đề, miễn là các phụ thuộc luôn hướng về cùng một hướng và không có chu trình trong đồ thị phụ thuộc.
Esko Luontola

0

Nó phụ thuộc. Trong công việc của tôi, đôi khi chúng tôi chia các gói theo chức năng (truy cập dữ liệu, phân tích) hoặc theo loại tài sản (tín dụng, cổ phiếu, lãi suất). Chỉ cần chọn cấu trúc thuận tiện nhất cho nhóm của bạn.


Có lý do gì để đi theo cả hai cách không?
Tim Visher 10/02/09

Việc phân chia theo loại nội dung (nói chung: theo lĩnh vực kinh doanh) giúp những người mới tìm đường thông qua mã dễ dàng hơn. Việc phân chia theo chức năng rất tốt cho việc đóng gói. Đối với tôi, quyền truy cập "gói" trong Java giống như một "người bạn" trong C ++.
quant_dev

@quant_dev Tôi không đồng ý với "..by function tốt cho việc đóng gói". Tôi nghĩ bạn có nghĩa là hoàn toàn trái ngược là đúng. Ngoài ra, đừng chỉ chọn từ "sự tiện lợi". Có một trường hợp cho mỗi (xem câu trả lời của tôi).
i3ensays

-3

Theo kinh nghiệm của tôi, khả năng tái sử dụng tạo ra nhiều vấn đề hơn là giải quyết. Với bộ vi xử lý và bộ nhớ mới nhất và rẻ tiền, tôi muốn sao chép mã hơn là tích hợp chặt chẽ để sử dụng lại.


5
Việc sử dụng lại mã không phải là về giá của phần cứng mà là về chi phí bảo trì mã.
user2793390

1
Đồng ý, nhưng sửa một cái gì đó trong một mô-đun đơn giản sẽ không ảnh hưởng đến toàn bộ mã. Bạn đang nói về chi phí bảo trì nào?
Raghu Kasturi

1
Bản sửa lỗi trong một mô-đun sẽ ảnh hưởng đến toàn bộ ứng dụng. Tại sao bạn muốn sửa cùng một lỗi nhiều lần?
user2793390
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.