Đối tượng CLR cũ và đối tượng truyền dữ liệu


405

Đối tượng POCO = Plain Old CLR (hoặc tốt hơn: Class)

DTO = Đối tượng truyền dữ liệu

Trong bài đăng này có một sự khác biệt, nhưng thật lòng mà nói hầu hết các blog tôi đọc mô tả POCO theo cách định nghĩa DTO: DTO là các thùng chứa dữ liệu đơn giản được sử dụng để di chuyển dữ liệu giữa các lớp của một ứng dụng.

POCO và DTO có giống nhau không?


5
"POCO = Đối tượng CLR cũ (hoặc tốt hơn: Lớp)". Do đó, các đối tượng có tính chất này trong VB.NET cũng sẽ là POCO chứ không phải POVO.
J. Polfer

Câu trả lời:


568

Một POCO tuân theo các quy tắc của OOP. Nó nên (nhưng không phải) có trạng thái hành vi. POCO đến từ POJO, được đặt ra bởi Martin Fowler [ giai thoại ở đây ]. Ông đã sử dụng thuật ngữ POJO như một cách để làm cho nó gợi cảm hơn để từ chối các triển khai EJB nặng nề của khung. POCO nên được sử dụng trong cùng bối cảnh trong .Net. Đừng để các khuôn khổ ra lệnh cho thiết kế của đối tượng của bạn.

Mục đích duy nhất của DTO là chuyển trạng thái và không nên có hành vi. Xem giải thích về DTO của Martin Fowler để biết ví dụ về việc sử dụng mẫu này.

Đây là điểm khác biệt: POCO mô tả cách tiếp cận lập trình ( lập trình hướng đối tượng lỗi thời), trong đó DTO là một mẫu được sử dụng để "truyền dữ liệu" bằng cách sử dụng các đối tượng.

Mặc dù bạn có thể đối xử với các POCO như DTO, bạn có nguy cơ tạo ra một mô hình miền thiếu máu nếu bạn làm như vậy. Ngoài ra, có cấu trúc không khớp, vì các DTO phải được thiết kế để truyền dữ liệu, không thể hiện cấu trúc thực sự của miền doanh nghiệp. Kết quả của việc này là các DTO có xu hướng phẳng hơn miền thực của bạn.

Trong một miền có độ phức tạp hợp lý, hầu như bạn luôn luôn tốt hơn khi tạo các POCO tên miền riêng biệt và dịch chúng sang DTO. DDD (thiết kế hướng tên miền) định nghĩa lớp chống tham nhũng (một liên kết khác ở đây , nhưng điều tốt nhất nên làm là mua sách ), đây là một cấu trúc tốt giúp phân biệt rõ ràng.


Tôi biết tôi đã tham khảo Martin Fowler rất nhiều ở đây, nhưng anh ta đặt ra thuật ngữ POJO, và đã viết cuốn sách PoEAA là tài liệu tham khảo dứt khoát cho DTO.
Michael Meadows

Tôi không chắc chắn nếu một DTO không nên có hành vi. Theo sơ đồ của Martin Fowler, DTO có thể có các hành vi.
Beatles1692

39
@ Beatles1692, các phương thức được mô tả là mã tuần tự hóa. Có lẽ nó quá rộng để tuyên bố "không có hành vi." Làm thế nào về "không có logic kinh doanh." Mã tuần tự hóa và các công cụ đối tượng mức thấp như mã băm, đẳng thức và chuỗi phải được chấp nhận.
Michael Meadows

1
@PositiveGuy Một mô hình phục vụ mục đích khác với DTO. DTO nên được chuyển dữ liệu từ tên miền này sang tên miền khác (dù chúng có trong cùng thời gian chạy hay không đều không liên quan). Một mô hình "đại diện" cho một khía cạnh của một miền, như màn hình, dịch vụ hoặc nguồn dữ liệu. Mô hình bao gồm trạng thái và hành vi, đại diện cho những gì họ đang mô hình hóa.
Michael Meadows

2
Xin lưu ý rằng các mô hình miền thiếu máu không nhất thiết là xấu, đặc biệt nếu ứng dụng của bạn chủ yếu là CRUD. Ưu tiên sự đơn giản hơn Martin Fowler.
Mariusz Jamro

50

Nó có thể là dư thừa để tôi đóng góp vì tôi đã nêu vị trí của mình trong bài viết trên blog của mình, nhưng đoạn cuối cùng của bài viết đó đã tóm gọn mọi thứ:

Vì vậy, để kết luận, hãy học cách yêu POCO và đảm bảo rằng bạn không lan truyền bất kỳ thông tin sai lệch nào về việc nó giống với DTO. DTO là các thùng chứa dữ liệu đơn giản được sử dụng để di chuyển dữ liệu giữa các lớp của một ứng dụng. POCO là các đối tượng kinh doanh chính thức với một yêu cầu là chúng không biết gì về tính bền vững (không có phương thức get hoặc save). Cuối cùng, nếu bạn chưa xem cuốn sách của Jimmy Nilsson, hãy chọn nó từ ngăn xếp trường đại học địa phương của bạn. Nó có các ví dụ trong C # và nó là một bài đọc tuyệt vời.

BTW, Patrick Tôi đã đọc POCO như một bài viết Phong cách sống, và tôi hoàn toàn đồng ý, đó là một bài viết tuyệt vời. Đó thực sự là một phần trong cuốn sách của Jimmy Nilsson mà tôi đề xuất. Tôi không có ý tưởng rằng nó có sẵn trực tuyến. Cuốn sách của ông thực sự là nguồn thông tin tốt nhất tôi tìm thấy trên POCO / DTO / Kho lưu trữ / và các thực tiễn phát triển DDD khác.


4
Liên kết đến bài viết trên blog: rlacovara.blogspot.com/2009/03/ Kẻ
Jamie Ide

28

POCO chỉ đơn giản là một đối tượng không phụ thuộc vào khung bên ngoài. Nó là đồng bằng.

Cho dù POCO có hành vi hay không thì đó là phi vật chất.

Một DTO có thể là POCO như một đối tượng miền (thường có nhiều hành vi).

Thông thường, các DTO có nhiều khả năng nhận phụ thuộc vào các khung bên ngoài (ví dụ: các thuộc tính) cho các mục đích tuần tự hóa như thông thường chúng thoát ra ở ranh giới của một hệ thống.

Trong các kiến ​​trúc kiểu Onion điển hình (thường được sử dụng theo cách tiếp cận DDD rộng rãi), lớp miền được đặt ở trung tâm và vì vậy, tại thời điểm này, các đối tượng của nó không nên có các phụ thuộc bên ngoài lớp đó.



6

Tôi nghĩ rằng một DTO có thể là một POCO. DTO thiên về cách sử dụng đối tượng trong khi POCO thiên về phong cách của đối tượng (tách rời khỏi các khái niệm kiến ​​trúc).

Một ví dụ trong đó POCO là một cái gì đó khác với DTO là khi bạn nói về POCO bên trong mô hình logic / mô hình logic kinh doanh của bạn, đây là một đại diện OO tuyệt vời cho miền vấn đề của bạn. Bạn có thể sử dụng POCO trong toàn bộ ứng dụng, nhưng điều này có thể có một số tác dụng phụ không mong muốn như rò rỉ kiến ​​thức. Ví dụ, các DTO được sử dụng từ Lớp dịch vụ mà UI giao tiếp với, các DTO là biểu diễn dữ liệu phẳng và chỉ được sử dụng để cung cấp dữ liệu cho UI và truyền các thay đổi trở lại lớp dịch vụ. Lớp dịch vụ chịu trách nhiệm ánh xạ cả hai cách của DTO tới các đối tượng miền POCO.

Cập nhật Martin Fowler nói rằng cách tiếp cận này là một con đường nặng nề và chỉ nên được thực hiện nếu có sự không phù hợp đáng kể giữa lớp miền và giao diện người dùng.


2
@David Landman, liên kết bạn đưa vào là dành cho mẫu DTO cục bộ, đó là khi DTO được sử dụng cho trạng thái chuyển trong phạm vi hệ thống của bạn. Trong những trường hợp này, bạn nên rất cẩn thận, vì trong hệ thống của bạn, bạn đã có một tên miền được xác định rõ có thể được chia sẻ. Khi chuyển trạng thái qua các ranh giới hệ thống, DTO khó tránh và khá phù hợp trong mọi trường hợp.
Michael Meadows

@Michal Meadows, vâng, liên kết thực sự nói về một tập hợp con khác nhau của các vấn đề. Nhưng tôi nghĩ trong trường hợp chuyển trạng thái qua ranh giới hệ thống, bạn nên sử dụng dịch vụ dịch thuật để ánh xạ POCO từ một bối cảnh sang POCO từ bối cảnh khác. Hay bạn đang nói về các ràng buộc ở cấp độ hệ thống?
Davy Landman

1

Trường hợp sử dụng chính cho DTO là trả lại dữ liệu từ dịch vụ web. Trong trường hợp này, POCO và DTO là tương đương. Bất kỳ hành vi nào trong POCO sẽ bị xóa khi được trả về từ một dịch vụ web, vì vậy nó không thực sự quan trọng cho dù nó có hành vi hay không.


5
Tôi nghĩ rằng câu trả lời của bạn nói sai những gì xảy ra một chút. Trong trường hợp dịch vụ web, proxy được tạo dựa trên trạng thái tiếp xúc của đối tượng. Điều này có nghĩa là một DTO được tạo ra tách biệt với POCO, tình cờ có cùng trạng thái công khai như POCO. Nó có vẻ tinh tế, nhưng nó quan trọng. Lý do là ngay cả khi proxy giống hệt với bản gốc, nó không thực sự được xây dựng từ cùng một lớp.
Michael Meadows

À, không. Người ta sử dụng DTO để trả về / nhận dữ liệu giữa các tầng, trong trường hợp này là dịch vụ web. Người ta chọn DTO vì nó chỉ có dữ liệu và không có hành vi. Đúng là lớp proxy cũng có khả năng là DTO và nếu bạn đã sử dụng lớp POCO thay thế, thì proxy sẽ được tạo. Nhưng trong trường hợp này, lớp POCO thực sự là một DTO, vì hành vi của nó sẽ không dịch. Tôi vẫn nói sử dụng DTO vì bạn sẽ không bỏ lỡ hành vi chưa từng tồn tại.
John Saunders

5
** Về mặt ngữ nghĩa: Các dịch vụ web hiển thị các túi trạng thái đối tượng bằng WSDL. Proxy được tạo ra từ những cái này Chúng không thể bao gồm hành vi. Nếu sử dụng một dịch vụ web, mối quan hệ duy nhất giữa đối tượng của bạn và đối tượng miền bị lộ là nó có cùng trạng thái công khai được tạo dựa trên kiểm tra.
Michael Meadows

7
@ John, tôi nghĩ bạn đang phản ứng thái quá. Tôi đang nói bạn đúng, nhưng từ ngữ của bạn là sai lệch. "Trong trường hợp này, POCO và DTO là tương đương." Về mặt ngữ nghĩa, điều đó không đúng. POCO có thể được sử dụng làm DTO và ngược lại, nhưng điều đó không có nghĩa là chúng tương đương ... không nhiều hơn một chiếc xe hơi và xe bán tải là tương đương, mặc dù cả hai đều có thể được sử dụng để đưa bạn đến cửa hàng tạp hóa. Chúng có chức năng chồng lấp, nhưng bạn sẽ khó tìm được ai đó để nói với bạn một cái nhìn sâu sắc tương đương với một chiếc F350, ngay cả trong bối cảnh của chuyến đi tạp hóa.
Michael Meadows

3
Câu trả lời này rất sai, một dịch vụ web không đủ chung cho một người. Quan trọng nhất, có một thực tế rõ ràng là DTO KHÔNG phải là POCO. DTO là một thùng chứa dữ liệu, trong khi POCO là các đối tượng làm thuộc tính và không biết gì về tính bền vững (không có phương thức get hoặc save).
Tom Stickel

1

đây là quy tắc chung: DTO == ác và chỉ báo của phần mềm được thiết kế quá mức. POCO == tốt. Các mô hình 'doanh nghiệp' đã phá hủy bộ não của rất nhiều người trong thế giới Java EE. xin đừng lặp lại sai lầm trong đất .NET.


7
Bạn có thể giải thích, xin vui lòng? DTO được yêu cầu khi trả lại dữ liệu từ một dịch vụ web, để tránh việc triển khai và các chi tiết cụ thể về nền tảng trong hợp đồng.
John Saunders

1
Có, John DTO được thiết kế cho những gì bạn nói và hoạt động tốt. Nhưng thật không may, chúng thường được sử dụng khi không được yêu cầu trong các ứng dụng web một lớp và có ít giá trị.
Craig

9
Tôi nghĩ, @drscroogemcduck, có thể bạn không thích DTO vì chúng được sử dụng như một phương sách đầu tiên chứ không phải là phương sách cuối cùng, nhưng chúng không phải là xấu xa ... không hơn gì so với các mẫu đơn lẻ hay nhà máy khét tiếng . Điều gì là xấu xa là các kiến ​​trúc sư đã đẩy mạnh khuôn khổ của các nhà phát triển buộc họ phải tạo ra DTO cho mọi thứ. Đối với những gì họ làm, chuyển dữ liệu, DTO (nếu được thực hiện thận trọng) là phù hợp hoàn hảo.
Michael Meadows

0

Các lớp DTO được sử dụng để tuần tự hóa / giải tuần tự dữ liệu từ các nguồn khác nhau. Khi bạn muốn giải tuần tự hóa một đối tượng từ một nguồn, không quan trọng đó là nguồn bên ngoài nào: dịch vụ, tệp, cơ sở dữ liệu, v.v. bạn có thể chỉ muốn sử dụng một phần của điều đó nhưng bạn muốn một cách dễ dàng để giải tuần tự dữ liệu đó thành một vật. sau đó bạn sao chép dữ liệu đó vào XModel mà bạn muốn sử dụng. Một serializer là một công nghệ đẹp để tải các đối tượng DTO. Tại sao? bạn chỉ cần một hàm để tải (deserialize) đối tượng.


0

TL; DR:

Một DTO mô tả mô hình chuyển giao trạng thái. Một POCO không mô tả bất cứ điều gì. Đó là một cách khác để nói "đối tượng" trong OOP. Nó xuất phát từ POJO (Java), được đặt ra bởi Martin Fowler, người thực sự chỉ mô tả nó như một cái tên lạ hơn cho 'đối tượng' vì 'đối tượng' không gợi cảm lắm.

DTO là một mẫu đối tượng được sử dụng để chuyển trạng thái giữa các lớp quan tâm. Họ có thể có hành vi (nghĩa là về mặt kỹ thuật có thể là một poco) miễn là hành vi đó không làm thay đổi trạng thái. Ví dụ, nó có thể có một phương thức nối tiếp chính nó.

POCO là một đối tượng đơn giản, nhưng 'đơn giản' có nghĩa là nó không đặc biệt. Nó chỉ có nghĩa là nó là một đối tượng CLR không có mô hình ngụ ý cho nó. Một thuật ngữ chung. Nó không được thực hiện để làm việc với một số khung khác. Vì vậy, nếu POCO của bạn có [JsonProperty]hoặc trang trí EF trên tất cả các thuộc tính của nó, chẳng hạn, thì tôi cho rằng đó không phải là POCO.

Dưới đây là một số ví dụ về các loại mẫu đối tượng khác nhau để so sánh:

  • Mô hình xem : được sử dụng để mô hình dữ liệu cho một khung nhìn. Thường có chú thích dữ liệu để hỗ trợ ràng buộc và xác nhận. Trong MVVM, nó cũng hoạt động như một bộ điều khiển. Nó còn hơn cả một DTO
  • Đối tượng giá trị : được sử dụng để biểu diễn các giá trị
  • Root tổng hợp : được sử dụng để quản lý trạng thái và bất biến
  • Trình xử lý : được sử dụng để trả lời một sự kiện / tin nhắn
  • Thuộc tính : được sử dụng làm vật trang trí để giải quyết các mối quan tâm xuyên suốt
  • Dịch vụ : được sử dụng để thực hiện các nhiệm vụ phức tạp
  • Bộ điều khiển : được sử dụng để kiểm soát luồng yêu cầu và phản hồi
  • Factory : được sử dụng để cấu hình và / hoặc lắp ráp các đối tượng phức tạp để sử dụng khi hàm tạo không đủ tốt. Cũng được sử dụng để đưa ra quyết định về những đối tượng cần được tạo ra trong thời gian chạy.
  • Kho lưu trữ / DAO : được sử dụng để truy cập dữ liệu

Tất cả chỉ là các đối tượng, nhưng lưu ý rằng hầu hết chúng thường được gắn với một mẫu. Vì vậy, bạn có thể gọi chúng là "đối tượng" hoặc bạn có thể cụ thể hơn về ý định của nó và gọi nó bằng ý nghĩa của nó. Đây cũng là lý do tại sao chúng tôi có các mẫu thiết kế; để mô tả các khái niệm phức tạp trong một vài tác phẩm. DTO là một mô hình. Tổng hợp gốc là một mẫu, View Model là một mẫu (ví dụ: MVC & MVVM). POCO không phải là một mô hình.

Một POCO không mô tả một mô hình. Nó chỉ là một cách khác nhau để đề cập đến các lớp / đối tượng trong OOP. Hãy nghĩ về nó như một khái niệm trừu tượng; họ có thể đề cập đến bất cứ điều gì. IMO, có một mối quan hệ một chiều bởi vì một khi một đối tượng đạt đến điểm mà nó chỉ có thể phục vụ một mục đích sạch sẽ, thì đó không còn là POCO nữa. Ví dụ, một khi bạn đánh dấu lớp của mình bằng các đồ trang trí để làm cho nó hoạt động với một số khung công tác, nó không còn là POCO nữa. Vì thế:

  • Một DTO là một POCO
  • POCO không phải là DTO
  • Mô hình xem là một POCO
  • POCO không phải là Mô hình xem

Điểm quan trọng trong việc phân biệt giữa hai là về việc giữ cho các mẫu rõ ràng và nhất quán trong nỗ lực để không gây lo ngại và dẫn đến khớp nối chặt chẽ. Ví dụ: nếu bạn có một đối tượng kinh doanh có các phương thức để thay đổi trạng thái, nhưng cũng được trang trí thành địa ngục với các trang trí EF để lưu vào SQL Server VÀ JsonProperty để có thể gửi lại điểm cuối API. Đối tượng đó sẽ không dung nạp để thay đổi và có thể sẽ bị vấy bẩn bởi các biến thể của các thuộc tính (ví dụ: UserId, UserPk, UserKey, UserGuid, trong đó một số trong số chúng được đánh dấu để không được lưu vào DB và các đối tượng khác được đánh dấu để không được lưu vào JSON ở điểm cuối API).

Vì vậy, nếu bạn định nói với tôi một cái gì đó là DTO, thì có lẽ tôi chắc chắn rằng nó không bao giờ được sử dụng cho bất cứ thứ gì ngoài trạng thái chuyển động xung quanh. Nếu bạn nói với tôi một cái gì đó là một mô hình xem, thì có lẽ tôi chắc chắn rằng nó sẽ không được lưu vào cơ sở dữ liệu. Nếu bạn nói với tôi một cái gì đó là Mô hình miền, thì có lẽ tôi chắc chắn rằng nó không phụ thuộc vào bất cứ thứ gì bên ngoài miền. Nhưng nếu bạn nói với tôi điều gì đó là POCO, bạn thực sự sẽ không nói với tôi nhiều điều.


-13

Thậm chí đừng gọi họ là DTOs. Chúng được gọi là Mô hình .... Thời kỳ. Người mẫu không bao giờ có hành vi. Tôi không biết ai đã đưa ra thuật ngữ DTO ngu ngốc này nhưng đó phải là một điều .NET là tất cả những gì tôi có thể hình dung. Hãy nghĩ về các mô hình xem trong MVC, cùng một điều, các mô hình được sử dụng để chuyển trạng thái giữa các lớp máy chủ hoặc trong giai đoạn dây, tất cả chúng đều là các mô hình. Thuộc tính với dữ liệu. Đây là những mô hình bạn vượt qua ove dây. Mô hình, Mô hình Mô hình. Đó là nó.

Tôi ước thuật ngữ ngu ngốc DTO sẽ biến mất khỏi vốn từ vựng của chúng tôi.


1
Tôi không biết bạn lấy ý tưởng này ở đâu mà người mẫu không bao giờ có hành vi. Làm thế nào để bạn mô hình hóa bất cứ điều gì khác ngoài CRUD mà không có mô hình hành vi? Ngay cả ViewModels cũng có hành vi trong nhiều trường hợp, đặc biệt là trong các ứng dụng MVVM. DTO là một thuật ngữ hữu ích vì nó mô tả chính xác mục đích; để truyền dữ liệu.
Gerald

9
bị hạ thấp vì thực tế không chính xác, và cho thái độ phổ biến.
joedotnot

Vô lý. Mô hình nên là container ngu ngốc. Không có DTO, đó là một thuật ngữ MS. Bạn chuyển mô hình giữa các tên miền, dịch vụ và ứng dụng. Giai đoạn = Stage. DTO là một sự lãng phí thuật ngữ không cần thiết và chỉ gây nhầm lẫn nhiều thứ hơn. Mô hình, mô hình và nhiều mô hình đó là nó. Mô hình có thể có hoặc không có hành vi. Xem mô hình không nên. Hành vi đó phải ở trong BL, không phải trong lớp Model.
positiveGuy

Tôi đồng ý rằng DTO là mô hình chức năng. ViewModels có hành vi và là những gì bạn liên kết trong MVVM. TUY NHIÊN, tôi đã viết một ứng dụng trong đó Mô hình của tôi thông minh hơn (về cơ bản là VM nhưng tôi không muốn gọi chúng hơn) và chúng "chấp nhận" một đối tượng DTO. Điều này cho phép tôi có nhiều lựa chọn hơn với khung. Vì vậy, từ CRUD (hoặc thậm chí là EF), tôi sẽ truyền đối tượng qua các dịch vụ WCF và nhận đối tượng DTO và đóng gói nó (thêm OnProp Change, v.v.). ViewModels của tôi đã thực hiện đóng gói thêm và có thể đã chấp nhận hai (hoặc một danh sách) "Mô hình". Định nghĩa cứng nhắc sẽ là VM.
SQLMason

"Bạn chuyển mô hình giữa các miền, dịch vụ và ứng dụng" Tại sao bạn nghĩ mô hình thuật ngữ phù hợp và phù hợp hơn thuật ngữ DTO cho hành vi này bạn mô tả?
caa
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.