Hi there,
TL; DR: câu trả lời cho hầu hết tất cả các câu hỏi của cậu là “it depends”.
Xử lý logic ở đây cụ thể là gì nhỉ
Cậu có 1 số loại logic ở đây:
- Application logic: validation, conversion, cách tạo câu query để gọi tới DB, cách gọi API… những logic liên quan tới kỹ thuật là application logic.
- Business logic: Nghiệp vụ của hệ thống, là lý do hệ thống của cậu được sinh ra. Đôi khi người ta có sử dụng cụm từ “Domain logic” để chỉ phần này - logic của lĩnh vực mà hệ thống của cậu phục vụ. Thường “Logic hệ thống” thường để ám chỉ loại này.
Khi người dùng nhập vào textField là dạng String thì hàm get dữ liệu từ textField đó rồi trả về Integer thì mình phải đặt hàm này ở VIEW hay CONTROLLER ?
Việc chuyển đổi dữ liệu đầu vào (từ textField) thành dữ liệu sử dụng trong hệ thống (integer) là conversion logic. Nó là 1 layer nhỏ trung gian giữa View và Controller, giúp cho model của View (dùng để hiển thị) và model của hệ thống (dùng để lưu trữ dữ liệu tính toán) không bị phụ thuộc vào nhau.
Logic này nên nằm ở nơi nào chịu trách nhiệm validate, tức là tùy vào design của hệ thống. Cậu có thể để nó ở Controller nếu cậu không muốn view biết cách chuyển đổi dữ liệu. Cậu cũng có thể để conversion logic và validation logic ở View (nếu như cậu đã implement validation ở các component trong view, điều này cũng chấp nhận được).
Các hàm xử lý tính toán ( kiểm tra delta ==> check nghiệm kép, 2 nghiệm hay vô nghiệm ==> tính nghiệm ) thì đặt ở lớp CONTROLLER hay MODEL ( giả sử app này có database ) ?
Để tớ đề cập tới phần này trước nhé!
Nhược điểm của MVC
Trong 1 application, cậu có rất nhiều thành phần và công đoạn xử lý.
- Validate
- Conversion
- Infrastructure: DB access & external service access
- Application flow
- Request model
- Response model
- Database model
- External service model
- Business model
- Business logic (hàm xử lý tính toán của cậu nằm ở đây)
…
Nhược điểm lớn nhất của MVC là chỉ có 3 tầng cho 3 đơn vị, và cậu phải đưa hết đống kể trên vào trong 3 tầng đó.
Dù cậu đưa vào đâu đi chăng nữa, sẽ có 1 vài tầng “béo” lên.
Do đó, tuy là 1 architechture phổ biến và đơn giản, nhưng MVC thường được dùng trong các ứng dụng nhỏ, khi mọi thứ còn bé. Khi ứng dụng to dần, cậu sẽ nhận ra một tầng của cậu làm nhiều nhiệm vụ hơn. Lúc đó, cậu thường sẽ buộc phải tách nhỏ MVC ra thành nhiều tầng hơn, nhiều đơn vị hơn, hoặc “lai” nó với 1 architechture khác (kết quả của việc refactor in architechture level không hoàn thiện). Các ứng dụng enterprise thường không dùng kiến trúc này, khi họ cần làm cho mọi thứ độc lập nhất có thể.
Những application implement với MVC thường cố gắng có business logic ngắn gọn nhất có thể, đẩy hết business logic phức tạp ra 3rd party, có lẽ chỉ có chút logic liên quan tới việc tổng hợp dữ liệu (từ nhiều bảng dữ liệu, hoặc từ nhiều nguồn khác nhau).
Như cậu đã liệt kê, có 2 cách để hàm xử lý tính toán (thực ra là 2 cách bố trí Business logic layer ở trong kiến trúc MVC). Tớ sẽ đưa ra phân tích trên từng loại cho cậu.
Để logic tính toán ở controller
Pros:
- Giảm độ phức tạp cho model, khi model đã chứa mô hình dữ liệu của bài toán, infrastructure, serialize & deserialize logic…
- Controller layer bao gồm 2 nhiệm vụ phân luồng (vốn chỉ delegate sang tầng khác) và business logic, được tách độc lập, nên thường thì cách này cài đặt dễ dàng hơn.
Cons:
- Nếu cậu có logic phân luồng phức tạp, Code ở controller của cậu sẽ có nguy cơ trở thành spagetty code, vì business logic vốn đã phức tạp rồi. Trừ khi cậu có 1 layer nhỏ cho việc phân luồng application flow giữa View và Controller-business logic.
Để logic tính toán ở model
Pros:
- Do gần với business model hơn, cậu có thể đóng gói toàn bộ business model trong phần model (đúng với tên của nó), và đưa ra View model (data để hiển thị, thường nó khá giống với business model, nhưng nó nên được tách ra để ứng dụng của cậu có các tầng độc lập với nhau) lên controller - view. Điều đó giúp cậu tách biệt hoàn toàn View với business model, nó sẽ có ích khi cậu chuyển đổi xử lý business logic sang việc gọi 1 API, và ngược lại, mà không cần đập phá controller - view.
Cons:
- Model của cậu sẽ khá “béo” Đi kèm với nó, model sẽ rất phức tạp.
Conclusion
Nó phụ thuộc vào sở thích của cậu mà cậu có thể để nó ở controller hay model (nó nên nằm giữa cả 2 :D). Cá nhân tớ, tớ vẫn thường để nó ở model, vì business logic nên ở chung chỗ với business model objects. However, it’s up to you
Giải phương trình bậc 2 theo MVC sử dụng 3rd party service
Tớ có viết thử 1 chương trình nhỏ (dĩ nhiên nó không chạy, mục đích chỉ là demo thôi ) để demo cho việc implement MVC để giải phương trình bậc 2.
Trong này tớ cố gắng tách ra nhiều layer nhỏ nữa, và giữ design simple nhất có thể.
public class View {
// The input field of the quadratic equation ax^2 + bx + c = 0
private TextField a;
private TextField b;
private TextField c;
private TextField resultTextField;
private Button calculateButton;
private Controller controller;
// initialize all fields and events
public void calculateButtonOnClick() {
// Normally, the `resultTextField` text field should be binded with one object
// so if you update the object, the `resultTextField` text field should be updated as well.
// However, to simplified the logic, the following implementation will be used:
try {
resultTextField.setText(controller.solve(a.getText(), b.getText(), c.getText()));
} catch (ValidationException ex) {
resultTextField.setText(ex.getMessage());
}
repaintComponent();
}
}
public class ValidatorRule {
public static boolean isDouble(String text) {
try {
Double.parseDouble(text);
return true;
} catch(NumberFormatException ex) {
return false;
}
}
}
public class Validator {
@SafeVarargs
public static <T> void validate(Predicate<T> rule, T... objects) throws ValidationException {
for(T object : objects) {
if(!rule.test(object)) {
// Fail immediately to make the implementation simple.
throw new ValidationException("Invalid parameter: " + object);
}
}
}
}
public class Controller {
private Model model;
private Validator validator;
private Conversion conversion;
// initialize all fields
/**
* Solve the quadratic equation ax^2 + bx + c, and return a Result object
*/
public String solve(String rawA, String rawB, String rawC) throws ValidationException {
validator.validate(ValidatorRule::isDouble, rawA, rawB, rawC);
// Just delegate the work to model
return conversion.fromResultToText(model.solve(
conversion.toDouble(rawA),
conversion.toDouble(rawB),
conversion.toDouble(rawC)));
}
}
public class Model {
private ThirdPartyService thirdPartyService;
private Conversion fromThirdPartyModelToResultConversion;
// initialize all fields
public Result solve(double a, double b, double c) {
// Call third party service, and converse the response to Result object
// The business logic is either in this method, or from external services.
return fromThirdPartyModelToResultConversion.converse(thirdPartyService.callSolveQuadraticEquationApi(a, b, c));
}
}
// Result, ThirdPartyService and some part of Conversion implementation are omitted.