Thành viên: Sử dụng ID duy nhất so với đối tượng miền


10

Sau một vài câu trả lời hữu ích về việc tôi nên sử dụng đối tượng miền hay id duy nhất làm tham số phương thức / hàm ở đây Định danh so với đối tượng miền làm tham số phương thức , tôi có một câu hỏi tương tự lại: các thành viên (các câu hỏi trước đó không quản lý được bao gồm điều này). Những ưu và nhược điểm của việc sử dụng ID duy nhất làm thành viên so với đối tượng là thành viên. Tôi đang hỏi về các ngôn ngữ được gõ mạnh, như Scala / C # / Java. Tôi có nên (1)

User( id: Int, CurrentlyReadingBooksId: List[Int])
Book( id: Int, LoanedToId: Int )

hoặc (2), ưu tiên cho (1) Sau khi trải qua: Chúng ta có nên xác định loại cho mọi thứ không?

User( id: UserId, CurrentlyReadingBooksId: List[ BookId] )
Book( id: BookId, LoanedToId: UserId )

hoặc (3)

User( id: Int, CurrentlyReadingBooks: List[Book]) 
Book( id: Int, LoanedTo: User)

Mặc dù tôi không thể nghĩ đến lợi ích của việc có đối tượng (3), nhưng một lợi ích khi có ID (2) & (1) là khi tôi tạo đối tượng Người dùng từ DB, tôi không phải tạo đối tượng Sách, mà lần lượt có thể phụ thuộc vào chính đối tượng Người dùng, tạo ra một chuỗi vô tận. Có một giải pháp chung cho vấn đề này cho cả RDBMS và No-SQL (nếu chúng khác nhau) không?

Dựa trên một số câu trả lời cho đến nay, hãy đọc lại câu hỏi của tôi: (với việc sử dụng ID được cho là thuộc loại được bọc) 1) Luôn sử dụng ID? 2) Luôn sử dụng Đối tượng? 3) Sử dụng ID khi có nguy cơ đệ quy trong tuần tự hóa và giải tuần tự hóa, nhưng sử dụng các đối tượng khác? 4) Còn gì nữa không?

EDIT: Nếu bạn trả lời rằng các đối tượng nên được sử dụng luôn hoặc trong một số trường hợp, vui lòng đảm bảo trả lời mối quan tâm lớn nhất mà những người trả lời khác đã đăng => Cách lấy dữ liệu từ DB


1
Cảm ơn câu hỏi hay, mong muốn theo dõi điều này với sự quan tâm. Một chút xấu hổ khi tên người dùng của bạn là "user18151", những người có loại tên người dùng này bị một số người bỏ qua :)
bjfletcher

@bjfletcher Cảm ơn bạn. Tôi đã có nhận thức dai dẳng đó, nhưng nó không bao giờ xảy ra với tôi tại sao!
0fnt

Câu trả lời:


7

Các đối tượng miền dưới dạng id tạo ra một số vấn đề phức tạp / tinh tế:

Tuần tự hóa / giải trừ

Nếu bạn lưu trữ các đối tượng dưới dạng các khóa, nó sẽ làm cho việc tuần tự hóa biểu đồ đối tượng cực kỳ phức tạp. Bạn sẽ gặp stackoverflowlỗi khi thực hiện tuần tự hóa ngây thơ sang JSON hoặc XML do đệ quy. Sau đó, bạn sẽ phải viết một bộ tuần tự tùy chỉnh để chuyển đổi các đối tượng thực tế để sử dụng id của chúng thay vì tuần tự hóa đối tượng và tạo đệ quy.

Truyền vào các đối tượng để đảm bảo an toàn kiểu nhưng chỉ lưu trữ id, sau đó bạn có thể có một phương thức truy cập lười biếng tải thực thể liên quan khi nó được gọi. Bộ nhớ đệm cấp hai sẽ đảm nhiệm các cuộc gọi tiếp theo.

Rò rỉ tham chiếu tinh tế:

Nếu bạn sử dụng các đối tượng miền trong các hàm tạo như bạn có, bạn sẽ tạo các tham chiếu vòng sẽ rất khó cho phép thu hồi bộ nhớ cho các đối tượng không được sử dụng tích cực.

Tình huống lý tưởng:

Id mờ đục so với int / long:

Một idnên là một định danh hoàn toàn mờ đục không mang thông tin về những gì nó xác định. Nhưng nó sẽ cung cấp một số xác minh rằng nó là một định danh hợp lệ trong hệ thống của nó.

Các loại nguyên liệu phá vỡ điều này:

int, longStringlà các loại nguyên liệu phổ biến nhất được sử dụng để định danh trong hệ thống RDBMS. Có một lịch sử lâu dài về những lý do thực tế có từ hàng thập kỷ trước và tất cả chúng đều là những thỏa hiệp phù hợp với việc tiết kiệm spacehoặc tiết kiệm timehoặc cả hai.

Id tuần tự là những kẻ phạm tội tồi tệ nhất:

Khi bạn sử dụng id tuần tự, bạn sẽ đóng gói thông tin ngữ nghĩa tạm thời vào id theo mặc định. Điều này không tệ cho đến khi nó được sử dụng. Khi mọi người bắt đầu viết logic kinh doanh sắp xếp hoặc lọc theo chất lượng ngữ nghĩa của id, thì họ đang thiết lập một thế giới đau khổ cho những người duy trì trong tương lai.

String Các lĩnh vực có vấn đề vì các nhà thiết kế ngây thơ sẽ đóng gói thông tin vào nội dung, thường là ngữ nghĩa tạm thời.

Những điều này làm cho không thể tạo ra một hệ thống dữ liệu phân tán, bởi vì 12437379123không phải là duy nhất trên toàn cầu. Cơ hội mà một nút khác trong một hệ thống phân tán sẽ tạo ra một bản ghi có cùng số được đảm bảo khá nhiều khi bạn có đủ dữ liệu trong một hệ thống.

Sau đó, hack bắt đầu hoạt động xung quanh nó và toàn bộ điều bị phá hủy thành một đống hỗn độn hấp.

Bỏ qua các hệ thống phân tán lớn ( cụm ) nó sẽ trở thành một cơn ác mộng hoàn toàn khi bạn bắt đầu cố gắng chia sẻ dữ liệu với các hệ thống khác. Đặc biệt là khi hệ thống khác không nằm trong tầm kiểm soát của bạn.

Bạn kết thúc với cùng một vấn đề, làm thế nào để làm cho id của bạn trở nên độc nhất trên toàn cầu.

UUID được tạo và chuẩn hóa vì một lý do:

UUIDcó thể bị tất cả các vấn đề được liệt kê ở trên tùy thuộc vào việc Versionbạn sử dụng.

Version 1sử dụng địa chỉ MAC và thời gian để tạo một id duy nhất. Điều này là xấu vì nó mang thông tin ngữ nghĩa về địa điểm và thời gian. Đó không phải là vấn đề, đó là khi các nhà phát triển ngây thơ bắt đầu dựa vào thông tin đó cho logic kinh doanh. Điều này cũng rò rỉ thông tin có thể bị khai thác trong bất kỳ nỗ lực xâm nhập nào.

Version 2sử dụng một người dùng UIDhoặc GIDvà domian UIDhoặc GUIthay vì thời gian Version 1này cũng tệ như Version 1rò rỉ dữ liệu và mạo hiểm thông tin này được sử dụng trong logic kinh doanh.

Version 3là tương tự nhưng thay thế địa chỉ MAC và thời gian bằng một MD5hàm băm của một số mảng byte[]từ một cái gì đó chắc chắn có ý nghĩa ngữ nghĩa. Không có rò rỉ dữ liệu để lo lắng, byte[]không thể được phục hồi từ UUID. Điều này cung cấp cho bạn một cách tốt để tạo một cách xác định UUIDmẫu biểu mẫu và khóa ngoài của một số loại.

Version 4 chỉ dựa trên các số ngẫu nhiên là một giải pháp tốt, nó hoàn toàn không có thông tin ngữ nghĩa, nhưng nó không thể tái tạo một cách xác định.

Version 5chỉ là thích Version 4nhưng sử dụng sha1thay vì md5.

Khóa miền và khóa dữ liệu giao dịch

Sở thích của tôi đối với id đối tượng miền, là sử dụng Version 5hoặc Version 3nếu bị hạn chế sử dụng Version 5vì một số lý do kỹ thuật.

Version 3 là tuyệt vời cho dữ liệu giao dịch có thể được trải rộng trên nhiều máy.

Trừ khi bạn bị giới hạn bởi không gian, hãy sử dụng UUID:

Chúng được đảm bảo duy nhất, kết xuất dữ liệu từ một cơ sở dữ liệu và tải lại vào cơ sở dữ liệu khác mà bạn không bao giờ phải lo lắng về các id trùng lặp thực sự tham chiếu dữ liệu miền khác nhau.

Version 3,4,5 là hoàn toàn mờ đục và đó là cách họ nên được.

Bạn có thể có một cột duy nhất làm khóa chính với a UUIDvà sau đó bạn có thể có các chỉ mục duy nhất ghép cho những gì sẽ là một khóa chính tổng hợp tự nhiên.

Lưu trữ không phảiCHAR(36)một trong hai. Bạn có thể lưu trữ UUIDtrong trường byte / bit / số riêng cho cơ sở dữ liệu đã cho miễn là nó vẫn có thể lập chỉ mục được.

Di sản

Nếu bạn có các kiểu thô và không thể thay đổi chúng, bạn vẫn có thể trừu tượng hóa chúng trong mã của mình.

Sử dụng một Version 3/5trong số UUIDbạn có thể chuyển vào Class.getName()+ String.valueOf(int)dưới dạng a byte[]và có khóa tham chiếu mờ có thể tái tạo và xác định.


Tôi rất xin lỗi nếu tôi không rõ ràng trong câu hỏi của mình và tôi cảm thấy tất cả tồi tệ hơn (hoặc thực sự tốt) bởi vì đây là một câu trả lời tuyệt vời và được suy nghĩ kỹ và bạn rõ ràng đã dành thời gian cho nó. Thật không may, nó không phù hợp với câu hỏi của tôi, có lẽ nó xứng đáng là một câu hỏi của riêng mình? "Tôi nên ghi nhớ điều gì khi tạo trường id cho đối tượng miền của mình"?
0fnt

Tôi đã thêm một lời giải thích rõ ràng.

Giờ đã hiểu. Cảm ơn đã dành thời gian cho câu trả lời.
0fnt

1
Những người thu gom rác thế hệ Btw, AFAIK (mà tôi tin là hệ thống GC thống trị ngày nay) không nên gặp quá nhiều khó khăn trong việc tham khảo thông tư của GC.
0fnt

1
nếu C-> A -> B -> ABđược đưa vào Collectionsau đó Avà tất cả trẻ em của nó vẫn có thể truy cập được, những điều này không hoàn toàn rõ ràng và có thể dẫn đến rò rỉ tinh tế . GClà ít nhất trong các vấn đề, tuần tự hóa và giải tuần tự hóa đồ thị là một cơn ác mộng của sự phức tạp.

2

Vâng, có những lợi ích cho cả hai cách, và cũng có một sự thỏa hiệp.

List<int>:

  • Lưu bộ nhớ
  • Khởi tạo kiểu nhanh hơn User
  • Nếu dữ liệu của bạn đến từ cơ sở dữ liệu quan hệ (SQL), bạn không phải truy cập hai bảng để có được người dùng, chỉ có Usersbảng

List<Book>:

  • Truy cập sách nhanh hơn từ người dùng, sách đã được tải sẵn vào bộ nhớ. Điều này là tốt nếu bạn có đủ khả năng để có một khởi động lâu hơn để có được các hoạt động tiếp theo nhanh hơn.
  • Nếu dữ liệu của bạn đến từ cơ sở dữ liệu lưu trữ tài liệu như HBase hoặc Cassandra thì các giá trị của sách được đọc có thể có trong Hồ sơ người dùng, do đó bạn có thể dễ dàng lấy được sách "trong khi bạn ở đó lấy người dùng".

Nếu bạn không có bộ nhớ hoặc CPU mà tôi sẽ List<Book>sử dụng, mã sử dụng các Userphiên bản sẽ sạch hơn.

Thỏa hiệp:

Khi sử dụng Linq2Query, mã được tạo cho thực thể Người dùng sẽ có một mã EntitySet<Book>lười biếng được tải khi bạn truy cập. Điều này sẽ giữ cho mã của bạn sạch sẽ và cá thể Người dùng nhỏ (dấu chân bộ nhớ).


Giả sử một số loại bộ nhớ đệm, lợi ích tải trước sẽ là null. Tôi chưa sử dụng Cassandra / HBase nên không thể nói về họ nhưng Linq2Query là một trường hợp rất cụ thể (mặc dù tôi không thấy việc tải lười biếng sẽ ngăn trường hợp chuỗi vô hạn ngay cả trong trường hợp cụ thể này và trong trường hợp chung)
0fnt

Trong ví dụ Linq2SQL bạn thực sự không nhận được lợi ích hiệu năng, chỉ cần mã sạch hơn. Khi nhận được một đến nhiều thực thể từ kho lưu trữ tài liệu như Cassandra / HBase, phần lớn thời gian xử lý được dành để tìm bản ghi, vì vậy bạn cũng có thể nhận được tất cả nhiều thực thể trong khi bạn ở đó (sách, trong ví dụ này).
ytoledano

Bạn có chắc không? Ngay cả khi tôi lưu trữ Sách và Người dùng được chuẩn hóa riêng biệt? Đối với tôi có vẻ như nó chỉ nên có độ trễ mạng thêm chi phí. Trong mọi trường hợp, làm thế nào để xử lý trường hợp RDBMS một cách tổng quát? (Tôi đã chỉnh sửa câu hỏi để đề cập rõ ràng)
0fnt

1

Quy tắc ngắn và đơn giản:

ID được sử dụng trong DTO s.
Tham chiếu đối tượng thường được sử dụng trong các đối tượng lớp Logic / Business Logic và UI.

Đó là kiến ​​trúc phổ biến trong các dự án lớn hơn, đủ các dự án. Bạn sẽ có những người lập bản đồ dịch qua lại hai loại đối tượng này.


Cảm ơn bạn đã ghé qua và trả lời. Thật không may, trong khi tôi hiểu được sự khác biệt nhờ vào liên kết wiki, tôi chưa bao giờ thấy điều này trong thực tế (với điều kiện là tôi chưa bao giờ làm việc với các dự án dài hạn lớn). Bạn có thể lấy một ví dụ trong đó cùng một đối tượng được trình bày theo hai cách cho hai mục đích khác nhau không?
0fnt

đây là một câu hỏi thực tế liên quan đến ánh xạ: stackoverflow.com/questions/9770041/dto-to-entity-mapping-tool - và có những bài viết quan trọng như thế này: rogemonying.com/2013/12/01/ trộm
herzmeister

Thực sự hữu ích, cảm ơn. Thật không may, tôi vẫn không hiểu làm thế nào để tải dữ liệu với các giới thiệu vòng tròn hoạt động? ví dụ: nếu Người dùng tham khảo Sách và Sách đề cập đến cùng một người dùng, bạn sẽ tạo đối tượng này như thế nào?
0fnt

Nhìn vào mẫu Kho lưu trữ . Bạn sẽ có một BookRepositoryvà một UserRepository. Bạn sẽ luôn gọi myRepository.GetById(...)hoặc tương tự, và kho lưu trữ sẽ tạo đối tượng và tải các giá trị của nó từ kho lưu trữ dữ liệu hoặc lấy nó từ bộ đệm. Ngoài ra, các đối tượng con chủ yếu là tải lười biếng, điều này cũng ngăn chặn việc phải xử lý các tham chiếu vòng tròn trực tiếp tại thời điểm xây dựng.
herzmeister
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.