Các kiến trúc sạch gợi ý để cho một trường hợp sử dụng tương tác bằng gọi việc thực hiện thực tế của người dẫn chương trình (được tiêm, sau khi DIP) để xử lý các phản ứng / hiển thị. Tuy nhiên, tôi thấy mọi người thực hiện kiến trúc này, trả lại dữ liệu đầu ra từ trình tương tác và sau đó để bộ điều khiển (trong lớp bộ điều hợp) quyết định cách xử lý nó. Là giải pháp thứ hai rò rỉ trách nhiệm ứng dụng ra khỏi lớp ứng dụng, ngoài việc không xác định rõ ràng các cổng đầu vào và đầu ra cho bộ tương tác?
Cổng đầu vào và đầu ra
Xem xét định nghĩa Kiến trúc sạch và đặc biệt là sơ đồ dòng nhỏ mô tả mối quan hệ giữa bộ điều khiển, bộ tương tác ca sử dụng và người trình bày, tôi không chắc liệu tôi có hiểu chính xác "Cổng đầu ra trường hợp sử dụng" là gì không.
Kiến trúc sạch, giống như kiến trúc lục giác, phân biệt giữa các cổng chính (phương thức) và cổng phụ (giao diện được thực hiện bởi bộ điều hợp). Theo luồng truyền thông, tôi hy vọng "Cổng đầu vào ca sử dụng" là cổng chính (do đó, chỉ là một phương thức) và "Cổng đầu ra trường hợp sử dụng" một giao diện sẽ được triển khai, có lẽ là một đối số của hàm tạo lấy bộ điều hợp thực tế, để người tương tác có thể sử dụng nó.
Mã ví dụ
Để tạo một ví dụ mã, đây có thể là mã điều khiển:
Presenter presenter = new Presenter();
Repository repository = new Repository();
UseCase useCase = new UseCase(presenter, repository);
useCase->doSomething();
Giao diện người trình bày:
// Use Case Output Port
interface Presenter
{
public void present(Data data);
}
Cuối cùng, chính trình tương tác:
class UseCase
{
private Repository repository;
private Presenter presenter;
public UseCase(Repository repository, Presenter presenter)
{
this.repository = repository;
this.presenter = presenter;
}
// Use Case Input Port
public void doSomething()
{
Data data = this.repository.getData();
this.presenter.present(data);
}
}
Trên trình tương tác gọi người trình bày
Việc giải thích trước đó dường như được xác nhận bởi chính sơ đồ đã nói ở trên, trong đó mối quan hệ giữa bộ điều khiển và cổng đầu vào được biểu thị bằng một mũi tên đặc với đầu "nhọn" (UML cho "liên kết", nghĩa là "có", trong đó bộ điều khiển "có" trường hợp sử dụng), trong khi mối quan hệ giữa người trình bày và cổng đầu ra được biểu thị bằng một mũi tên đặc với đầu "trắng" (UML cho "thừa kế", không phải là "để thực hiện", nhưng có lẽ dù sao đó cũng là ý nghĩa).
Hơn nữa, trong câu trả lời này cho một câu hỏi khác , Robert Martin mô tả chính xác trường hợp sử dụng trong đó người tương tác gọi người trình bày theo yêu cầu đọc:
Nhấp vào bản đồ sẽ khiến cho PlacePinControll được gọi. Nó tập hợp vị trí của nhấp chuột và bất kỳ dữ liệu theo ngữ cảnh nào khác, xây dựng cấu trúc dữ liệu của PlacePinRequest và chuyển nó đến PlacePinInteractor để kiểm tra vị trí của pin, xác thực nó nếu cần, tạo một thực thể Place để ghi lại mã pin, xây dựng một EditPlaceReponse đối tượng và chuyển nó đến EditPlacePresenter, màn hình sẽ hiển thị.
Để làm cho điều này hoạt động tốt với MVC, tôi có thể nghĩ rằng logic ứng dụng mà theo truyền thống sẽ đi vào bộ điều khiển, ở đây được chuyển sang trình tương tác, vì chúng tôi không muốn bất kỳ logic ứng dụng nào bị rò rỉ bên ngoài lớp ứng dụng. Bộ điều khiển trong lớp bộ điều hợp sẽ chỉ gọi trình tương tác và có thể thực hiện một số chuyển đổi định dạng dữ liệu nhỏ trong quy trình:
Phần mềm trong lớp này là một bộ các bộ điều hợp chuyển đổi dữ liệu từ định dạng thuận tiện nhất cho các trường hợp sử dụng và thực thể, sang định dạng thuận tiện nhất cho một số cơ quan bên ngoài như Cơ sở dữ liệu hoặc Web.
từ bài viết gốc, nói về Bộ điều hợp giao diện.
Trên dữ liệu tương tác trả về
Tuy nhiên, vấn đề của tôi với phương pháp này là trường hợp sử dụng phải quan tâm đến bản trình bày. Bây giờ, tôi thấy rằng mục đích của Presenter
giao diện là đủ trừu tượng để đại diện cho một số loại trình bày khác nhau (GUI, Web, CLI, v.v.) và nó thực sự chỉ có nghĩa là "đầu ra", đó là một trường hợp sử dụng có thể rất tốt có, nhưng tôi vẫn không hoàn toàn tự tin với nó.
Bây giờ, tìm kiếm trên Web các ứng dụng của kiến trúc sạch, tôi dường như chỉ thấy mọi người diễn giải cổng đầu ra như một phương thức trả về một số DTO. Đây sẽ là một cái gì đó như:
Repository repository = new Repository();
UseCase useCase = new UseCase(repository);
Data data = useCase.getData();
Presenter presenter = new Presenter();
presenter.present(data);
// I'm omitting the changes to the classes, which are fairly obvious
Điều này hấp dẫn bởi vì chúng tôi chuyển trách nhiệm "gọi" bản trình bày ra khỏi trường hợp sử dụng, vì vậy trường hợp sử dụng không quan tâm đến việc biết phải làm gì với dữ liệu nữa, thay vì chỉ cung cấp dữ liệu. Ngoài ra, trong trường hợp này, chúng tôi vẫn không vi phạm quy tắc phụ thuộc, vì trường hợp sử dụng vẫn không biết gì về lớp bên ngoài.
Tuy nhiên, trường hợp sử dụng không kiểm soát thời điểm khi bản trình bày thực tế được thực hiện nữa (có thể hữu ích, ví dụ để thực hiện các công cụ bổ sung tại thời điểm đó, như ghi nhật ký hoặc hủy bỏ hoàn toàn nếu cần thiết). Ngoài ra, lưu ý rằng chúng tôi đã mất Cổng đầu vào ca sử dụng, vì bây giờ bộ điều khiển chỉ sử dụng getData()
phương thức (là cổng đầu ra mới của chúng tôi). Hơn nữa, theo tôi, chúng tôi đang phá vỡ nguyên tắc "nói, đừng hỏi" ở đây, bởi vì chúng tôi đang yêu cầu người tương tác cho một số dữ liệu để làm gì đó với nó, thay vì bảo nó làm điều thực tế trong địa điểm đầu tiên.
Đến điểm
Vì vậy, có bất kỳ một trong hai lựa chọn thay thế này là cách giải thích "chính xác" của Cổng đầu ra ca sử dụng theo Kiến trúc sạch không? Cả hai đều khả thi?