Có phải thực tế xấu để vượt qua các trường hợp thông qua một số lớp?


60

Trong thiết kế chương trình của tôi, tôi thường đi đến điểm mà tôi phải truyền các thể hiện đối tượng thông qua một số lớp. Ví dụ: nếu tôi có một bộ điều khiển tải một tệp âm thanh, sau đó chuyển nó cho một trình phát và trình phát đó chuyển nó đến trình phátRunnable, nó sẽ chuyển nó trở lại ở một nơi khác, v.v. biết cách tránh nó Hoặc nó là OK để làm điều này?

EDIT: Có thể ví dụ về trình phát không phải là tốt nhất vì tôi có thể tải tệp sau, nhưng trong các trường hợp khác không hoạt động.

Câu trả lời:


54

Như những người khác đã đề cập, đây không hẳn là một thực tiễn tồi, nhưng bạn nên chú ý rằng bạn sẽ không phá vỡ mối quan tâm của các lớp và chuyển các trường hợp cụ thể của lớp giữa các lớp. Ví dụ:

  • Các đối tượng cơ sở dữ liệu không bao giờ được chuyển lên các lớp cao hơn. Tôi đã thấy các chương trình sử dụng lớp DataAd CHƯƠNG của .NET, lớp truy cập DB và chuyển nó lên lớp UI, thay vì sử dụng DataAd CHƯƠNG trong DAL, tạo DTO hoặc tập dữ liệu và chuyển nó lên. Truy cập DB là miền của DAL.
  • Tất nhiên, các đối tượng UI nên được giới hạn trong lớp UI. Một lần nữa, tôi đã thấy điều này bị vi phạm, cả với ListBox được điền dữ liệu người dùng được chuyển đến lớp BL, thay vì một mảng / DTO nội dung của nó và (một mục yêu thích cụ thể của tôi), một lớp DAL lấy dữ liệu phân cấp từ DB, thay vì trả về cấu trúc dữ liệu phân cấp, nó chỉ tạo và điền vào một đối tượng TreeView và chuyển nó trở lại UI để được thêm động vào một biểu mẫu.

Tuy nhiên, nếu các trường hợp bạn vượt qua là DTOs hoặc chính các thực thể, có lẽ nó ổn.


1
Điều này nghe có vẻ gây sốc, nhưng trong những ngày đầu đen tối của .NET, đó là cách làm thường được khuyến nghị và có lẽ tốt hơn so với hầu hết các ngăn xếp khác đang làm.
Wyatt Barnett

1
Tôi không đồng ý. Đúng là Microsoft đã chứng thực việc áp dụng các ứng dụng một lớp trong đó ứng dụng khách Winforms cũng truy cập DB và DataAd CHƯƠNG được thêm trực tiếp vào biểu mẫu dưới dạng điều khiển vô hình, nhưng đó chỉ là một kiến ​​trúc cụ thể, khác với thiết lập tầng N của OP . Nhưng trong một kiến ​​trúc đa lớp, và điều này đúng với VB6 / DNA ngay cả trước .NET, các đối tượng DB vẫn ở trong lớp DB.
Avner Shahar-Kashtan

Để làm rõ: bạn đã thấy mọi người truy cập UI trực tiếp (trong ví dụ của bạn, hộp danh sách) từ "Lớp dữ liệu" của họ chưa? Tôi không nghĩ rằng tôi đã gặp phải một vi phạm như vậy trong mã sản xuất .. wow ..
Simon Whitehead

7
@SimonWhitehead Chính xác. Những người mờ nhạt về sự khác biệt tạo ra một ListBox và một mảng và sử dụng ListBox như một DTO. Đó là một khoảnh khắc giúp tôi nhận ra có bao nhiêu giả định vô hình mà tôi đưa ra mà không trực quan với người khác.
Avner Shahar-Kashtan

1
@SimonWhitehead - Vâng, chắc chắn tôi đã thấy rằng trong các chương trình VB6 và VB.NET Framework 1.1 và 2.0 và đã được giao nhiệm vụ duy trì những con quái vật như vậy. Nó trở nên rất xấu xí, rất nhanh.
jfrankcarr

15

Điều thú vị là chưa có ai nói về các đối tượng Bất biến . Tôi sẽ lập luận rằng việc đưa một vật thể bất biến xung quanh qua tất cả các lớp khác nhau thực sự là một điều tốt hơn là tạo ra nhiều vật thể sống ngắn cho mỗi lớp.

Có một số cuộc thảo luận tuyệt vời về sự bất biến của Eric Lippert trên blog của mình

Mặt khác, tôi cho rằng việc chuyển các vật thể đột biến giữa các lớp là thiết kế tồi . Về cơ bản, bạn đang xây dựng một lớp với lời hứa rằng các lớp xung quanh sẽ không thay đổi nó theo cách phá vỡ mã của bạn.


13

Vượt qua các đối tượng xung quanh là một điều bình thường để làm. Nó giảm nhu cầu giữ trạng thái (tức là các biến thể hiện) và tách mã khỏi bối cảnh thực thi của nó.

Một vấn đề mà bạn có thể gặp phải là tái cấu trúc, khi bạn phải thay đổi chữ ký của nhiều phương thức dọc theo chuỗi gọi để đáp ứng yêu cầu thay đổi tham số của một phương thức gần cuối chuỗi đó. Tuy nhiên, nó có thể được giảm nhẹ bằng việc sử dụng các công cụ phát triển phần mềm hiện đại giúp tái cấu trúc.


6
Tôi muốn nói một vấn đề khác mà bạn có thể phải đối mặt liên quan đến sự bất biến. Tôi có thể nhớ một lỗi rất khó hiểu trong một dự án mà tôi đã làm việc trong đó một nhà phát triển đã sửa đổi DTO mà không nghĩ về thực tế rằng lớp cụ thể của anh ta không phải là người duy nhất có tham chiếu đến đối tượng đó.
Phil

8

Có thể là nhỏ, nhưng có nguy cơ chỉ định tham chiếu này ở đâu đó trong một trong các lớp có khả năng gây ra tham chiếu lơ lửng hoặc rò rỉ bộ nhớ sau này ..


Quan điểm của bạn là đúng, nhưng từ thuật ngữ của OP ("trường hợp đối tượng truyền") tôi có cảm giác rằng anh ta đang truyền các giá trị (không phải con trỏ) hoặc anh ta đang nói về một môi trường được thu gom rác (Java, C #, Python, Go ,. ..).
Mohammad Deh Afghanistan

7

Nếu bạn chuyển các đối tượng xung quanh đơn giản chỉ vì nó cần ở một vùng xa của mã của bạn, sử dụng các mẫu thiết kế tiêm điều khiển và phụ thuộc cùng với tùy chọn một bộ chứa IoC thích hợp có thể giải quyết tốt các vấn đề liên quan đến việc mang các đối tượng đi khắp nơi. Tôi đã sử dụng nó cho một dự án cỡ trung bình và sẽ không bao giờ xem xét việc viết một đoạn mã máy chủ lớn mà không sử dụng nó.


Nghe có vẻ thú vị, tôi đã sử dụng phương thức tiêm constructor và tôi nghĩ các thành phần cấp cao của tôi kiểm soát các thành phần cấp thấp. Làm thế nào để bạn sử dụng một thùng chứa IOC để tránh mang theo các trường hợp?
Puckl

Tôi nghĩ rằng tôi đã tìm thấy câu trả lời ở đây: martinfowler.com/articles/injection.html
Puckl

1
Lưu ý bên cạnh, nếu bạn đang làm việc trong Java, Guice thực sự rất hay và bạn có thể phạm vi các ràng buộc của mình với những thứ như yêu cầu để thành phần cấp cao đang tạo phạm vi và ràng buộc các thể hiện với các lớp chính xác tại thời điểm đó.
Dave

4

Truyền dữ liệu qua một loạt các lớp không phải là một điều xấu, đó thực sự là cách duy nhất để một hệ thống lớp có thể hoạt động mà không vi phạm cấu trúc lớp. Dấu hiệu có vấn đề là khi bạn truyền dữ liệu của mình cho một số đối tượng trong cùng một lớp để đạt được mục tiêu của mình.


3

Trả lời nhanh: Không có gì sai trong việc truyền các đối tượng. Như đã đề cập, điểm là bỏ qua việc gán tham chiếu này trong tất cả các lớp có khả năng gây ra rò rỉ tham chiếu hoặc bộ nhớ.

Trong các dự án của chúng tôi, chúng tôi sử dụng thực tiễn này để chuyển DTO (đối tượng truyền dữ liệu) giữa các lớp và đó là cách thực hành rất hữu ích. Chúng tôi cũng sử dụng lại các đối tượng dto của mình để xây dựng phức tạp hơn một lần, như thông tin tóm tắt.


3

Tôi chủ yếu là một nhà phát triển giao diện người dùng web nhưng đối với tôi có vẻ như sự khó chịu trực quan của bạn có thể ít hơn về việc truyền qua cá thể và nhiều hơn về thực tế là bạn sẽ có một chút thủ tục với bộ điều khiển đó. Bộ điều khiển của bạn có nên đổ mồ hôi tất cả các chi tiết này? Tại sao nó thậm chí còn tham chiếu nhiều hơn một tên đối tượng khác để phát âm thanh?

Trong thiết kế OOP, tôi có xu hướng suy nghĩ về những gì thường xanh và những gì có nhiều khả năng có thể thay đổi. Chủ đề để thay đổi công cụ là những gì bạn sẽ muốn có xu hướng đặt vào các hộp đối tượng lớn hơn để bạn có thể duy trì giao diện nhất quán ngay cả khi người chơi thay đổi hoặc tùy chọn mới được thêm vào. Hoặc bạn thấy mình muốn trao đổi các đối tượng âm thanh hoặc các thành phần trong đó bán buôn.

Trong trường hợp này, bộ điều khiển của bạn cần xác định rằng cần phải phát tệp âm thanh và sau đó có một cách nhất quán / thường xanh để phát nó. Mặt khác, công cụ phát âm thanh có thể dễ dàng thay đổi khi công nghệ và nền tảng bị thay đổi hoặc có thêm lựa chọn mới. Tất cả các chi tiết đó phải nằm bên dưới giao diện của một đối tượng tổng hợp lớn hơn, IMO và bạn không cần phải viết lại bộ điều khiển của mình khi các chi tiết về cách âm thanh được phát thay đổi. Sau đó, khi bạn chuyển một thể hiện đối tượng với các chi tiết như vị trí tệp vào đối tượng lớn hơn, tất cả những gì hoán đổi xung quanh được thực hiện trong phần bối cảnh thích hợp, nơi ai đó ít có khả năng làm điều gì đó ngớ ngẩn với nó.

Vì vậy, trong trường hợp này, tôi không nghĩ rằng đối tượng đó bị ném xung quanh có thể làm phiền bạn. Đó là thuyền trưởng Picard đang chạy xuống phòng máy để bật lõi dọc, chạy ngược lên cây cầu để vẽ tọa độ, rồi nhấn nút "punch-it" sau khi bật tấm khiên thay vì chỉ nói "Lấy chúng ta đến hành tinh X tại Warp 9. Làm cho nó trở nên như vậy. " và để cho phi hành đoàn của mình sắp xếp các chi tiết. Bởi vì khi anh ta xử lý theo cách đó, anh ta có thể làm thuyền trưởng cho bất kỳ con tàu nào trong hạm đội mà không cần biết cách bố trí của mọi con tàu và cách mọi thứ hoạt động. Và cuối cùng, đó là thiết kế OOP lớn nhất giành chiến thắng, IMO.


2

Đây là một thiết kế khá phổ biến, kết thúc xảy ra mặc dù bạn có thể gặp vấn đề về độ trễ nếu ứng dụng của bạn nhạy cảm với loại điều đó.


2

Vấn đề này có thể được giải quyết bằng các biến có phạm vi động, nếu ngôn ngữ của bạn có chúng hoặc lưu trữ luồng cục bộ. Các cơ chế này cho phép chúng tôi liên kết một số biến tùy chỉnh với chuỗi kích hoạt hoặc chuỗi điều khiển để chúng tôi không phải chuyển các giá trị này thành mã không liên quan đến chúng để chúng có thể được truyền xuống một số mã khác cái nào cần chúng


2

Như các câu trả lời khác đã chỉ ra, đây không phải là một thiết kế nghèo nàn. Nó có thể tạo khớp nối chặt chẽ giữa các lớp lồng nhau và các lớp lồng chúng, nhưng nới lỏng khớp nối có thể không phải là một lựa chọn hợp lệ nếu lồng các tham chiếu cung cấp một giá trị cho thiết kế.

Một giải pháp khả thi là "làm phẳng" các tham chiếu lồng nhau trong lớp trình điều khiển.

Thay vì truyền tham số nhiều lần qua các đối tượng lồng nhau, bạn có thể duy trì trong tham chiếu lớp trình điều khiển đến tất cả các đối tượng lồng nhau.

Làm thế nào chính xác điều này được thực hiện (hoặc nếu nó thậm chí là một giải pháp hợp lệ) phụ thuộc vào thiết kế hiện tại của hệ thống, chẳng hạn như:

  • Bạn có thể duy trì một số loại bản đồ của các đối tượng lồng nhau trong bộ điều khiển mà không quá phức tạp không?
  • Khi bạn truyền tham số cho đối tượng lồng nhau thích hợp, đối tượng lồng nhau có thể nhận ra tham số ngay lập tức không, hoặc có chức năng bổ sung nào xảy ra trong khi chuyển nó qua các đối tượng lồng nhau không?
  • Vân vân.

Đây là một vấn đề mà tôi gặp phải trong mẫu thiết kế MVC cho máy khách GXT. Các thành phần GUI của chúng tôi chứa các thành phần GUI lồng nhau cho một số lớp. Khi dữ liệu mô hình được cập nhật, cuối cùng chúng tôi chuyển qua nhiều lớp cho đến khi đạt được (các) thành phần thích hợp. Nó tạo ra sự ghép nối không mong muốn giữa các thành phần GUI vì nếu chúng ta muốn một lớp thành phần GUI mới chấp nhận dữ liệu mô hình, chúng ta phải tạo các phương thức để cập nhật dữ liệu mô hình trong tất cả các thành phần GUI có chứa lớp mới.

Để khắc phục, chúng tôi đã duy trì trong lớp View một bản đồ tham chiếu đến tất cả các thành phần GUI lồng nhau để bất cứ khi nào dữ liệu mô hình được cập nhật, View có thể gửi trực tiếp dữ liệu mô hình đến các thành phần GUI cần nó, kết thúc câu chuyện . Điều này hoạt động tốt vì chỉ có các phiên bản duy nhất của mỗi thành phần GUI. Tôi có thể thấy nó không hoạt động tốt nếu có nhiều phiên bản của một số thành phần GUI, khiến cho việc xác định bản sao nào cần được cập nhật là khó khăn.


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.