Tại sao tôi nên cô lập các thực thể miền của mình khỏi lớp bản trình bày?


85

Một phần của thiết kế theo hướng miền mà dường như không có nhiều chi tiết, đó là cách thức và lý do bạn nên tách biệt mô hình miền khỏi giao diện của mình. Tôi đang cố gắng thuyết phục các đồng nghiệp của mình rằng đây là một phương pháp hay, nhưng dường như tôi không đạt được nhiều tiến bộ ...

Họ sử dụng các thực thể miền ở bất kỳ nơi nào họ muốn trong các lớp giao diện và bản trình bày. Khi tôi tranh luận với họ rằng họ nên sử dụng các mô hình hiển thị hoặc DTO để cách ly lớp Miền khỏi lớp giao diện, họ phản bác rằng họ không thấy giá trị kinh doanh khi làm điều gì đó như vậy, bởi vì bây giờ bạn có một đối tượng UI để duy trì cũng như đối tượng miền gốc.

Vì vậy, tôi đang tìm một số lý do cụ thể mà tôi có thể sử dụng để sao lưu điều này. Đặc biệt:

  1. Tại sao chúng ta không nên sử dụng các đối tượng miền trong lớp trình bày của mình?
    (nếu câu trả lời là câu trả lời rõ ràng, 'decoupling', thì vui lòng giải thích tại sao điều này lại quan trọng trong ngữ cảnh này)
  2. Chúng ta có nên sử dụng các đối tượng hoặc cấu trúc bổ sung để cô lập các đối tượng miền của chúng ta khỏi giao diện không?

câu hỏi này nên có trong wiki.
Syed Tayyab Ali

@ m4bwav - Nó phải là một wiki vì nó được diễn đạt theo cách mời gọi cuộc thảo luận hơn là một câu trả lời đúng.
Rob Allen

1
@ m4bwav: Tôi nghĩ câu hỏi của bạn giống như một mẩu ý kiến ​​hơn là một câu hỏi thực sự ... Tôi đã cố gắng sửa điều đó (bạn có thể muốn chỉnh sửa thêm), nhưng lưu ý rằng nếu không cẩn thận, điều này có thể xuất hiện được trolling.
Shog9

5
Ok, sao lưu, tôi đang hỏi một câu hỏi chính đáng, điều này sẽ xúc phạm bất kỳ ai? Tôi đang nhắm mục tiêu ai?
Mark Rogers

@ m4bwav: bạn đang nhắm mục tiêu người đàn ông rơm của mình. "Số lượng lớn người" mà bạn thảo luận về vấn đề này trong câu hỏi của bạn.
Shog9

Câu trả lời:


48

Rất đơn giản, lý do là một trong những thực hiện và trôi dạt. Có, lớp trình bày của bạn cần biết về các đối tượng kinh doanh của bạn để có thể trình bày chúng một cách chính xác. Có, ban đầu có vẻ như có rất nhiều chồng chéo giữa việc thực hiện hai loại đối tượng. Vấn đề là, khi thời gian trôi qua, mọi thứ được bổ sung cho cả hai phía. Bản trình bày thay đổi và nhu cầu của lớp trình bày phát triển để bao gồm những thứ hoàn toàn độc lập với lớp nghiệp vụ của bạn (ví dụ: màu sắc). Trong khi đó, các đối tượng miền của bạn thay đổi theo thời gian và nếu bạn không có cách tách phù hợp khỏi giao diện của mình, bạn có nguy cơ làm hỏng lớp giao diện của mình bằng cách thực hiện các thay đổi có vẻ lành tính đối với các đối tượng kinh doanh của mình.

Cá nhân tôi tin rằng cách tốt nhất để tiếp cận mọi thứ là thông qua mô hình giao diện được thực thi nghiêm ngặt; nghĩa là, lớp đối tượng nghiệp vụ của bạn để lộ một giao diện là cách duy nhất để giao tiếp với nó; không có chi tiết triển khai (nghĩa là các đối tượng miền) về giao diện được tiết lộ. Có, điều này có nghĩa là bạn phải triển khai các đối tượng miền của mình ở hai vị trí; lớp giao diện của bạn và trong lớp BO của bạn. Nhưng việc thực hiện lại đó, mặc dù ban đầu có vẻ như là một công việc bổ sung, nhưng lại giúp thực thi việc tách rời sẽ giúp tiết kiệm HÀNG TẤN công việc tại một số thời điểm trong tương lai.


2
Ý bạn là gì khi "triển khai các đối tượng miền của bạn ở hai vị trí?"
jlembke

10
Điều này chỉ có vẻ ngớ ngẩn đối với tôi. Tại sao việc làm thêm bây giờ CÓ THỂ tiết kiệm công việc trong tương lai? 9 lần trong số 10 lần bạn sẽ không bao giờ cần thực hiện thay đổi sẽ tiết kiệm được "TẤN" công việc.
Bíp bíp

13
@LuckyLindy: 99 lần trong số 100 (thực tế là nhiều hơn), việc thắt dây an toàn là không cần thiết để giúp tôi không bị thương. Tuy nhiên, trong một trường hợp khi tôi thực sự cần nó, nó sẽ (có khả năng) giúp tôi không bị giết hoặc bị thương nặng. Một ounce phòng ngừa có giá trị một pound chữa bệnh. Tôi nghi ngờ ý kiến ​​của bạn về điều này sẽ thay đổi sau khi bạn có nhiều kinh nghiệm hơn.
Paul Sonier

19

Tôi đã đấu tranh với điều này bản thân mình. Có những trường hợp DTO có ý nghĩa khi sử dụng trong trình chiếu. Giả sử tôi muốn hiển thị danh sách các Công ty trong hệ thống của mình và tôi cần id của họ để ràng buộc giá trị.

Thay vì tải một CompanyObject có thể có tham chiếu đến đăng ký hoặc ai biết gì khác, tôi có thể gửi lại DTO với tên và id. Đây là một IMHO sử dụng tốt.

Bây giờ hãy lấy một ví dụ khác. Tôi có một đối tượng đại diện cho một Ước tính, ước tính này có thể được tạo thành lao động, thiết bị, v.v., nó có thể có rất nhiều phép tính được xác định bởi người dùng lấy tất cả các mục này và tổng hợp chúng (Mỗi ước tính có thể khác nhau với các loại khác nhau tính toán). Tại sao tôi phải mô hình đối tượng này hai lần? Tại sao tôi không thể liệt kê giao diện người dùng của mình qua các phép tính và hiển thị chúng?

Tôi thường không sử dụng DTO để tách lớp miền của mình khỏi giao diện người dùng. Tôi sử dụng chúng để cô lập lớp miền của mình khỏi ranh giới nằm ngoài tầm kiểm soát của tôi. Ý tưởng rằng ai đó sẽ đưa thông tin điều hướng vào đối tượng kinh doanh của họ là vô lý, đừng làm ô nhiễm đối tượng kinh doanh của bạn.

Ý tưởng rằng ai đó sẽ đặt xác nhận vào đối tượng kinh doanh của họ? Tôi nói rằng đây là một điều tốt. Giao diện người dùng của bạn không nên có trách nhiệm duy nhất để xác thực các đối tượng kinh doanh của bạn. Lớp kinh doanh của bạn PHẢI thực hiện xác nhận của riêng nó.

Tại sao bạn lại đặt mã tạo giao diện người dùng trong một đối tượng busienss? Trong trường hợp của tôi, tôi có các đối tượng riêng biệt tạo ra mã giao diện người dùng riêng biệt từ giao diện người dùng. Tôi đã nói các đối tượng hiển thị các đối tượng kinh doanh của tôi thành Xml, ý tưởng rằng bạn phải tách các lớp của mình để ngăn chặn loại ô nhiễm này rất xa lạ với tôi vì tại sao bạn lại đặt mã tạo HTML vào một đối tượng kinh doanh ...

Chỉnh sửa Theo tôi nghĩ thêm một chút, có những trường hợp thông tin giao diện người dùng có thể thuộc về lớp miền. Và điều này có thể làm đám mây cái mà bạn gọi là lớp miền nhưng tôi đã làm việc trên một ứng dụng nhiều người thuê, ứng dụng này có hành vi rất khác nhau cả về giao diện người dùng và quy trình làm việc chức năng. Tùy thuộc vào các yếu tố khác nhau. Trong trường hợp này, chúng tôi có một mô hình miền đại diện cho người thuê và cấu hình của họ. Cấu hình của chúng tình cờ bao gồm thông tin giao diện người dùng (Ví dụ: nhãn cho các trường chung).

Nếu tôi phải thiết kế các đối tượng của mình để làm cho chúng có thể tồn tại được, thì tôi có nên nhân bản các đối tượng không? Hãy nhớ rằng nếu bạn muốn thêm một trường mới, bây giờ bạn có hai nơi để thêm nó. Có lẽ điều này đặt ra một câu hỏi khác nếu bạn đang sử dụng DDD, có phải tất cả các đối tượng miền của thực thể lâu dài không? Tôi biết trong ví dụ của tôi là họ.


Các nhãn khác nhau đối với những người thuê khác nhau sẽ chỉ ra một ngôn ngữ phổ biến khác nhau cho mỗi người thuê sao? Tôi nghĩ rằng cần phải có khái niệm về mô hình siêu mẫu trong đó miền được chia sẻ giữa những người thuê với một lớp dịch để diễn giải mô hình siêu mẫu của họ.
Kell

16

Bạn làm điều đó vì lý do giống như bạn giữ SQL khỏi các trang ASP / JSP của mình.

Nếu bạn chỉ giữ một đối tượng miền để sử dụng trong lớp trình bày VÀ miền miền, thì một đối tượng đó sẽ sớm trở thành nguyên khối. Nó bắt đầu bao gồm mã xác thực giao diện người dùng, mã điều hướng giao diện người dùng và mã tạo giao diện người dùng. Sau đó, bạn sẽ sớm thêm tất cả các phương thức lớp nghiệp vụ lên trên đó. Giờ đây, lớp nghiệp vụ và giao diện người dùng của bạn đã được trộn lẫn và tất cả chúng đang lộn xộn ở lớp thực thể miền.

Bạn muốn sử dụng lại tiện ích giao diện người dùng tiện lợi đó trong một ứng dụng khác? Bạn phải tạo một cơ sở dữ liệu với tên này, hai lược đồ này và 18 bảng này. Bạn cũng phải định cấu hình Hibernate và Spring (hoặc các khuôn khổ bạn chọn) để thực hiện xác thực nghiệp vụ. Ồ, bạn cũng phải bao gồm 85 lớp khác không liên quan này vì chúng được tham chiếu trong lớp nghiệp vụ, chỉ xảy ra trong cùng một tệp.


13

Tôi không đồng ý.

Tôi nghĩ rằng cách tốt nhất là bắt đầu với các đối tượng miền trong lớp bản trình bày của bạn CHO ĐẾN KHI NÓ CÓ SENSE ĐỂ LÀM VIỆC KHÁC.

Trái với suy nghĩ thông thường, "Đối tượng miền" và "Đối tượng giá trị" có thể cùng tồn tại một cách vui vẻ trong lớp trình bày. Và đây là cách tốt nhất để làm điều đó - bạn nhận được lợi ích của cả hai thế giới, giảm sự trùng lặp (và mã soạn sẵn) với các đối tượng miền; và điều chỉnh và đơn giản hóa khái niệm của việc sử dụng các đối tượng giá trị trên các yêu cầu.


Cảm ơn bạn đã đóng góp ý kiến, tôi biết bạn đến từ đâu. Mặc dù tôi không nói rằng đây không phải là một trong những cách vô hạn để tạo ra một dự án thành công, nhưng nó dường như đi ngược lại với phong cách "Thiết kế theo hướng miền", dành cho các dự án lớn hơn và phức tạp hơn khó duy trì về lâu dài.
Mark Rogers

Không, điều này là sai, và chính xác là tại sao rất nhiều trang web lại dễ bị tiêm sql.
Remi

7

Câu trả lời tùy thuộc vào quy mô ứng dụng của bạn.


Ứng dụng CRUD (Tạo, Đọc, Cập nhật, Xóa) đơn giản

Đối với các ứng dụng thô sơ cơ bản, bạn không có bất kỳ chức năng nào. Thêm DTO lên trên các thực thể sẽ rất lãng phí thời gian. Nó sẽ làm tăng độ phức tạp mà không tăng khả năng mở rộng.

nhập mô tả hình ảnh ở đây


Ứng dụng Non-CRUD phức tạp vừa phải

Trong kích thước ứng dụng này, bạn sẽ có ít thực thể có vòng đời thực sự và một số logic nghiệp vụ được liên kết với chúng.

Thêm DTO vào trường hợp này là một ý tưởng hay vì một số lý do:

  • Lớp trình bày chỉ có thể xem tập hợp con của các trường mà thực thể có. Bạn đóng gói các thực thể
  • Không có khớp nối giữa phụ trợ và biên giới
  • Nếu bạn có các phương thức kinh doanh bên trong các thực thể, nhưng không có trong DTO thì việc thêm các DTO có nghĩa là mã bên ngoài không thể làm hỏng trạng thái của thực thể của bạn.

nhập mô tả hình ảnh ở đây


Ứng dụng Doanh nghiệp phức tạp

Một thực thể có thể cần nhiều cách trình bày. Mỗi người trong số họ sẽ cần tập hợp các trường khác nhau. Trong trường hợp này, bạn gặp phải các vấn đề tương tự như trong ví dụ trước cộng với việc cần phải kiểm soát số lượng trường hiển thị cho mỗi máy khách. Có DTO riêng biệt cho từng khách hàng sẽ giúp bạn chọn những gì nên hiển thị.

nhập mô tả hình ảnh ở đây


4

Chúng tôi đang sử dụng cùng một mô hình trong máy chủ và trên ui. Và đó là một nỗi đau. Chúng tôi phải cấu trúc lại nó vào một ngày nào đó.

Các vấn đề chủ yếu là do mô hình miền cần được cắt thành nhiều phần nhỏ hơn để có thể tuần tự hóa nó mà không cần tham chiếu toàn bộ cơ sở dữ liệu. Điều này làm cho nó khó sử dụng hơn trên máy chủ. Các liên kết quan trọng bị thiếu. Một số loại cũng không thể tuần tự hóa và không thể gửi đến máy khách. Ví dụ: 'Loại' hoặc bất kỳ lớp chung nào. Chúng cần phải không chung chung và Loại cần được chuyển dưới dạng chuỗi. Điều này tạo ra các thuộc tính bổ sung cho tuần tự hóa, chúng thừa và khó hiểu.

Một vấn đề khác là các thực thể trên giao diện người dùng không thực sự phù hợp. Chúng tôi đang sử dụng databinding và nhiều thực thể có rất nhiều thuộc tính thừa chỉ cho mục đích ui. Ngoài ra, có nhiều 'BrowsableAttribute' và những thứ khác trong mô hình thực thể. Điều này thật sự tệ.

Cuối cùng, tôi nghĩ vấn đề là cách nào dễ hơn. Có thể có các dự án mà nó chỉ hoạt động tốt và không cần viết mô hình DTO khác.


2
Nếu bạn định sử dụng databinding, hãy chạy một truy vấn linq và liên kết với một kiểu ẩn danh. Điều này cho phép bạn san bằng và thay đổi thứ bậc. Bạn cũng có thể thực hiện lọc và sắp xếp rất độc đáo với điều này.
JoshBerke

@Josh: Cảm ơn vì lời khuyên. Điều này có thể một phần hoạt động. Bản thân tôi không phải là một lập trình viên GUI và không liên quan nhiều đến các khái niệm GUI. Vấn đề sẽ là trong trường hợp dữ liệu được thao tác và gửi trở lại máy chủ.
Stefan Steinegger

3

Đó là về sự phụ thuộc cho hầu hết các phần. Cấu trúc chức năng cốt lõi của tổ chức có các yêu cầu chức năng riêng và giao diện người dùng phải cho phép mọi người sửa đổi và xem cốt lõi; nhưng bản thân lõi không được yêu cầu để đáp ứng giao diện người dùng. (Nếu nó cần phải xảy ra, đó thường là một dấu hiệu cho thấy lõi không phải là thuộc tính được thiết kế.)

Hệ thống kế toán của tôi có cấu trúc và nội dung (và dữ liệu) được cho là mô hình hóa hoạt động của công ty tôi. Cấu trúc đó là có thật và tồn tại bất kể tôi sử dụng phần mềm kế toán nào. (Chắc chắn một gói phần mềm nhất định chứa cấu trúc và nội dung vì lợi ích riêng của nó, nhưng một phần của thách thức là giảm thiểu chi phí này.)

Về cơ bản một người có một công việc để làm. DDD phải phù hợp với quy trình và nội dung của công việc. DDD là về việc làm rõ ràng tất cả các công việc cần được thực hiện quảng cáo hoàn toàn và độc lập nhất có thể. Sau đó, giao diện người dùng hy vọng sẽ tạo điều kiện để hoàn thành công việc một cách minh bạch, hiệu quả nhất có thể.

Các giao diện là về các đầu vào và khung nhìn được cung cấp cho lõi chức năng bất biến và được mô hình hóa đúng cách.


3

Chết tiệt, tôi thề rằng điều này đã nói lên sự kiên trì.

Dù sao, đó là một trường hợp tương tự nữa: Luật Parnas nói rằng một mô-đun nên giữ bí mật và bí mật là một yêu cầu có thể thay đổi. (Bob Martin có một quy tắc là một phiên bản khác của điều này.) Trong một hệ thống như thế này, bản trình bày có thể thay đổi độc lập với miền . Chẳng hạn như, chẳng hạn, một công ty duy trì giá bằng Euro và sử dụng tiếng Pháp trong văn phòng công ty, nhưng muốn trình bày giá bằng đô la với văn bản bằng tiếng Quan Thoại. Các miền là như nhau; bản trình bày có thể thay đổi. Vì vậy, để giảm thiểu tính dễ gãy của hệ thống - tức là số lượng thứ phải thay đổi để thực hiện thay đổi về yêu cầu - bạn tách các mối quan tâm ra.


2

Bản trình bày của bạn có thể tham chiếu đến lớp miền của bạn, nhưng không có ràng buộc trực tiếp từ ui của bạn với các đối tượng miền của bạn. Các đối tượng miền không nhằm mục đích sử dụng giao diện người dùng vì chúng thường, nếu được thiết kế phù hợp, dựa trên các hành vi chứ không phải biểu diễn dữ liệu. Nên có một lớp ánh xạ giữa giao diện người dùng và miền. MVVM, hoặc MVP, là một mô hình tốt cho việc này. Nếu bạn cố gắng liên kết trực tiếp giao diện người dùng của mình với Miền, bạn sẽ tự tạo ra rất nhiều vấn đề đau đầu cho mình. Họ có hai mục đích khác nhau.


1

Có lẽ bạn không khái niệm về lớp giao diện người dùng theo các thuật ngữ đủ rộng. Suy nghĩ về nhiều hình thức phản hồi (trang web, phản hồi bằng giọng nói, thư in, v.v.) và về nhiều ngôn ngữ (tiếng Anh, tiếng Pháp, v.v.).

Bây giờ, giả sử rằng công cụ giọng nói cho hệ thống gọi điện thoại chạy trên một loại máy tính hoàn toàn khác (ví dụ: Mac) với máy tính chạy trang web (có lẽ là Windows).

Tất nhiên là rất dễ rơi vào bẫy "À trong công ty của tôi, chúng tôi chỉ quan tâm đến tiếng Anh, chạy trang web của chúng tôi trên LAMP (Linux, Apache, MySQL và PHP) và mọi người đều sử dụng cùng một phiên bản Firefox". Nhưng 5 hay 10 năm nữa thì sao?



1

Với sự trợ giúp của công cụ như ' Value Injecter ' và khái niệm 'Mappers' trong lớp trình bày khi làm việc với các khung nhìn, việc hiểu từng đoạn mã sẽ dễ dàng hơn nhiều. Nếu bạn có một chút mã, bạn sẽ không thấy lợi thế ngay lập tức nhưng khi dự án của bạn ngày càng phát triển, bạn sẽ rất vui khi làm việc với các khung nhìn để không phải nhập vào logic của các dịch vụ, kho để hiểu mô hình xem. View Model là một người bảo vệ khác trong thế giới rộng lớn của lớp chống tham nhũng và có giá trị bằng vàng trong một dự án dài hạn.

Lý do duy nhất khiến tôi không thấy lợi ích của việc sử dụng mô hình chế độ xem là nếu dự án của bạn đủ nhỏ và đơn giản để có các chế độ xem được liên kết trực tiếp với từng thuộc tính của mô hình của bạn. Nhưng nếu trong tương lai, yêu cầu thay đổi và một số điều khiển trong các chế độ xem sẽ không bị ràng buộc với mô hình và bạn không có khái niệm về mô hình chế độ xem, bạn sẽ bắt đầu thêm các bản vá ở nhiều nơi và bạn sẽ bắt đầu có mã kế thừa bạn sẽ không đánh giá cao. Chắc chắn, bạn có thể thực hiện một số cấu trúc lại để chuyển đổi mô hình chế độ xem của mình trong mô hình chế độ xem và tuân theo nguyên tắc YAGNI trong khi không thêm mã nếu bạn không cần nhưng đối với bản thân tôi, đó là một phương pháp hay nhất mà tôi phải tuân theo để thêm lớp trình bày chỉ hiển thị các đối tượng mô hình khung nhìn.


1

Đây là một ví dụ thực tế về lý do tại sao tôi thấy cách thực hành tốt là tách các thực thể miền khỏi chế độ xem.

Một vài tháng trước, tôi đã tạo một giao diện người dùng đơn giản để hiển thị các giá trị của Nitơ, Phốt pho và Kali trong một mẫu đất thông qua một loạt 3 đồng hồ đo. Mỗi thước đo có một phần màu đỏ, xanh lá cây và đỏ, tức là bạn có thể có quá ít hoặc quá nhiều trong mỗi thành phần, nhưng có một mức xanh an toàn ở giữa.

Không cần suy nghĩ nhiều, tôi lập mô hình logic kinh doanh của mình để cung cấp dữ liệu cho 3 thành phần hóa học này và một bảng dữ liệu riêng biệt, chứa dữ liệu về các mức được chấp nhận trong mỗi trường hợp trong số 3 trường hợp (bao gồm cả đơn vị đo lường đang được sử dụng, tức là mol hoặc phần trăm). Sau đó, tôi lập mô hình giao diện người dùng của mình để sử dụng một mô hình rất khác, mô hình này quan tâm đến nhãn, giá trị, giá trị ranh giới và màu sắc.

Điều này có nghĩa là sau này khi tôi phải hiển thị 12 thành phần, tôi chỉ ánh xạ dữ liệu bổ sung vào 12 mô hình chế độ xem đồng hồ đo mới và chúng xuất hiện trên màn hình. Điều đó cũng có nghĩa là tôi có thể sử dụng lại điều khiển máy đo một cách dễ dàng và để chúng hiển thị các tập dữ liệu khác.

Nếu tôi đã ghép các đồng hồ đo này trực tiếp vào các thực thể miền của mình, tôi sẽ không có bất kỳ sự linh hoạt nào ở trên và bất kỳ sửa đổi nào trong tương lai sẽ là một vấn đề đau đầu. Tôi đã gặp những vấn đề tương tự khi lập mô hình lịch trong giao diện người dùng. Nếu có yêu cầu về một cuộc hẹn lịch chuyển sang màu đỏ khi có hơn 10 người tham dự, thì logic nghiệp vụ để xử lý điều này sẽ vẫn nằm trong lớp nghiệp vụ và tất cả lịch trong giao diện người dùng cần biết, đó là lịch đã được hướng dẫn để chuyển sang màu đỏ thì không cần biết tại sao.


-1

Lý do hợp lý duy nhất để thêm ánh xạ bổ sung giữa ngữ nghĩa tổng quát và miền cụ thể là bạn có (quyền truy cập vào) nội dung mã (và các công cụ) hiện có dựa trên ngữ nghĩa tổng quát (nhưng có thể lập bản đồ) khác biệt với ngữ nghĩa miền của bạn.

Thiết kế hướng miền hoạt động tốt nhất khi được sử dụng cùng với một tập hợp trực giao các khung miền chức năng (chẳng hạn như ORM, GUI, Dòng công việc, v.v.). Luôn nhớ rằng chỉ ở lớp bên ngoài các ngữ nghĩa của miền mới cần được bộc lộ. Điển hình đây là giao diện người dùng (GUI) và giao diện người dùng cuối (RDBM, ORM). Bất kỳ lớp can thiệp nào được thiết kế hiệu quả đều có thể và phải là miền bất biến.


đoạn 1: không tạo ra sự trừu tượng không cần thiết (ví dụ: các thành phần có thể tái sử dụng) trừ khi bạn thực sự chia sẻ chúng trên các ứng dụng riêng biệt. đoạn 2: Tôi tự hỏi làm thế nào các GUI chung hoạt động trên rất nhiều miền khác nhau. Nhận xét: Ngành công nghiệp này đã quá hỏng, nó thậm chí không còn vui nữa ...
alphazero
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.