Cách tách hoàn toàn Mô hình khỏi Chế độ xem / Trình điều khiển trong Java Swing


10

Có một tập hợp các hướng dẫn thiết kế thường được thống nhất để tách các lớp Model khỏi các lớp View / Controller trong ứng dụng Java Swing không? Tôi không quan tâm đến việc Chế độ xem / Trình điều khiển không biết gì về Mô hình theo cách khác: Tôi muốn thiết kế Mô hình của mình để không có kiến ​​thức về bất cứ điều gì trong javax.swing. Lý tưởng nhất là nó phải có một API đơn giản cho phép nó được điều khiển bởi một thứ nguyên thủy như CLI. Nó nên được, nói một cách lỏng lẻo, một "động cơ."

Truyền đạt các sự kiện GUI đến mô hình không quá khó - Người thực hiện hành động có thể gọi API của mô hình. Nhưng những gì về khi mô hình thực hiện các thay đổi trạng thái của chính nó cần được phản ánh trở lại GUI? Đó là những gì "lắng nghe" là để làm, nhưng ngay cả "được lắng nghe" không hoàn toàn thụ động; nó đòi hỏi mô hình phải biết về việc thêm một người nghe.

Vấn đề cụ thể khiến tôi suy nghĩ liên quan đến một hàng tập tin. Về phía GUI có một DefaultListModelđằng sau JListvà có một số nội dung GUI để chọn tệp từ hệ thống tệp và thêm chúng vào JList. Về phía Model, nó muốn kéo các tệp ra khỏi phần cuối của "hàng đợi" này (khiến chúng biến mất khỏi JList) và xử lý chúng theo một cách nào đó. Trong thực tế, mã Model đã được viết - nó hiện đang duy trì ArrayList<File>và hiển thị một add(File)phương thức công khai . Nhưng tôi không biết làm cách nào để Mô hình của mình hoạt động với Chế độ xem / Trình điều khiển mà không có một số sửa đổi nặng, cụ thể đối với Mô hình.

Tôi rất mới đối với cả lập trình Java và GUI, luôn luôn thực hiện lập trình "bó" và "back-end" - do đó tôi quan tâm đến việc duy trì sự phân chia chặt chẽ giữa mô hình và giao diện người dùng, nếu có thể và nếu có thể được dạy.



Btw: Trong MVC, mô hình nên được tách rời khỏi chế độ xem và bộ điều khiển theo mặc định. Bạn nên đọc lại sách giáo khoa của bạn, tôi nghĩ bạn chưa hiểu rõ về khái niệm này. Và bạn có thể triển khai một bộ sưu tập tùy chỉnh để thông báo khi nó thay đổi, giống như giao diện INotifyCollectionChanged của .NET.
Falcon

@Falcon: cảm ơn các liên kết. Không chắc chắn tôi hiểu nhận xét của bạn, tuy nhiên .. "mô hình nên được tách rời khỏi chế độ xem và bộ điều khiển theo mặc định". Bạn có thể viết lại hoặc giải thích?
Chương

Câu trả lời:


10

Không có hướng dẫn thiết kế thường được thống nhất (ví dụ: defacto ) cho MVC. Nó không hoàn toàn khó để tự làm điều đó nhưng đòi hỏi một số kế hoạch về các lớp học của bạn và rất nhiều thời gian và sự kiên nhẫn.

Lý do không có giải pháp xác định là vì có nhiều cách để làm MVC, tất cả đều có ưu và nhược điểm của chúng. Vì vậy, hãy thông minh về nó và làm những gì phù hợp với bạn nhất.

Để trả lời câu hỏi của bạn, bạn thực sự cũng muốn tách bộ điều khiển khỏi chế độ xem (vì vậy bạn có thể sử dụng cùng logic quy tắc kinh doanh cho cả ứng dụng Swing và trong ứng dụng bảng điều khiển). Trong ví dụ Xoay, bạn muốn tách bộ điều khiển khỏi JWindowvà bất kỳ tiện ích nào trong Xoay. Cách tôi thường làm (trước khi sử dụng khung thực tế) là tạo giao diện cho chế độ xem mà bộ điều khiển sử dụng:

public interface PersonView {
    void setPersons(Collection<Person> persons);
}

public class PersonController {

    private PersonView view;
    private PersonModel model;

    public PersonController(PersonView view, PersonModel model) {
        this.view = view;
        this.model = model;
    }
    // ... methods to affect the model etc. 
    // such as refreshing and sort:

    public void refresh() {
        this.view.setPersons(model.getAsList());
    }

    public void sortByName(boolean descending) {
       // do your sorting through the model.
       this.view.setPersons(model.getSortedByName());
    }

}

Đối với giải pháp này trong quá trình khởi động, bạn cần đăng ký bộ điều khiển vào khung nhìn.

public class PersonWindow extends JWindow implements PersonView {

    PersonController controller;
    Model model;

    // ... Constructor etc.

    public void initialize() {
        this.controller = new PersonController(this, this.model);

        // do all the other swing stuff

        this.controller.refresh();
    }

    public void setPersons(Collection<Person> persons) {
        // TODO: set the JList (in case that's you are using) 
        // to use the given parameter
    }

}

Thay vào đó, có thể là một ý tưởng tốt để tạo một IoC-container để thực hiện tất cả các thiết lập cho bạn.

Dù sao đi nữa, bằng cách này bạn có thể thực hiện các chế độ xem chỉ dành cho bảng điều khiển, sử dụng cùng các bộ điều khiển:

public class PersonConsole implements PersonView {

    PersonController controller;
    Model model;

    public static void main(String[] args) {
        new PersonConsole().run();
    }

    public void run() {
        this.model = createModel();
        this.controller = new PersonController(this, this.model);

        this.controller.refresh();
    }

    public void setPersons(Collection<Person> persons) {
        // just output the collection to the console

        StringBuffer output = new StringBuffer();
        for(Person p : persons) {
            output.append(String.format("%s%n", p.getName()));
        }

        System.out.println(output);
    }

    public void createModel() {
        // TODO: create this.model
    }

    // this could be expanded with simple console menu with keyboard
    // input and other console specific stuff

}    

Phần thú vị là làm thế nào để xử lý sự kiện. Tôi đã thực hiện điều này bằng cách cho phép khung nhìn tự đăng ký với bộ điều khiển bằng giao diện, điều này được thực hiện bằng cách sử dụng mẫu Observer (nếu bạn đang sử dụng .NET, bạn sẽ sử dụng trình xử lý sự kiện thay thế). Dưới đây là một ví dụ về "trình quan sát tài liệu" đơn giản, báo hiệu khi tài liệu đã được lưu hoặc tải.

public interface DocumentObserver {
    void onDocumentSave(DocModel saved);
    void onDocumentLoad(DocModel loaded);
}

// in your controller you implement register/unregister methods
private List<DocumentObserver> observers;

// register observer in to the controller
public void addObserver(DocumentObserver o) {
    this.observers.add(o);
}

// unregisters observer from the controller
public void removeObserver(DocumentObserver o) {
    this.observers.remove(o);
}

public saveDoc() {
    DocModel model = model.save();
    for (DocumentObserver o : observers) {
        o.onDocumentSave(model);
    }
}

public loadDoc(String path) {
    DocModel model = model.load(path);
    for (DocumentObserver o : observers) {
        o.onDocumentLoad(model);
    }        
}

Bằng cách đó, chế độ xem có thể tự cập nhật chính xác vì nó đang đăng ký các bản cập nhật tài liệu. Tất cả những gì phải làm là thực hiện DocumentObservergiao diện:

public class DocumentWindow extends JWindow 
        implements DocView, DocumentObserver {

    //... all swing stuff

    public void onDocumentSave(DocModel saved) {
        // No-op
    }

    public void onDocumentLoad(DocModel loaded) {
        // do what you need with the loaded model to the
        // swing components, or let the controller do it on
        // the view interface
    }

    // ...

}

Tôi hy vọng những ví dụ thúc đẩy này cung cấp cho bạn một số ý tưởng về cách tự làm. Tuy nhiên, tôi thực sự khuyên bạn nên cân nhắc sử dụng các khung công tác trong Java để thực hiện hầu hết mọi thứ cho bạn, nếu không bạn sẽ có rất nhiều mã soạn sẵn mất nhiều thời gian để viết. Có một số Nền tảng khách hàng phong phú (RCP) mà bạn có thể sử dụng để thực hiện một số chức năng cơ bản mà bạn rất cần, chẳng hạn như xử lý tài liệu trên toàn ứng dụng và xử lý nhiều sự kiện cơ bản.

Có một cặp vợ chồng tôi có thể nghĩ ra từ đầu của mình: EclipseNetbeans RCP.

Bạn vẫn cần phát triển bộ điều khiển và mô hình cho chính mình, nhưng đó là lý do tại sao bạn sử dụng ORM. Ví dụ sẽ là Hibernate .

Các container IoC là tuyệt vời, nhưng cũng có các khung cho việc này. Chẳng hạn như Spring (cũng xử lý dữ liệu trong số những thứ khác).


Cảm ơn đã dành thời gian để viết một phản hồi dài như vậy. Hầu hết trong số đó là một chút trên đầu của tôi tại thời điểm này, nhưng tôi chắc chắn Wikipedia sẽ giúp. Tôi đang sử dụng cả Eclipse và Netbeans và nghiêng về cái sau. Tôi không chắc làm thế nào để phân biệt Trình điều khiển với Chế độ xem ở đó, nhưng ví dụ về khung trước của bạn ở trên cùng sẽ giúp ích.
Chap

0

Tôi cho rằng điều này đôi khi chúng ta cần phải thỏa hiệp

Như bạn nói, sẽ thật tuyệt nếu chúng ta có thể tuyên truyền thông báo thay đổi hoàn toàn mà không cần đối tượng quan sát có cơ sở hạ tầng rõ ràng cho việc này. Đối với các ngôn ngữ mệnh lệnh phổ biến như Java, C #, C ++, kiến ​​trúc thời gian chạy của chúng quá nhẹ, như bây giờ, dù sao đi nữa. Điều đó có nghĩa là nó không phải là một phần của đặc tả ngôn ngữ tại thời điểm này.

Trong trường hợp cụ thể của bạn, tôi không nghĩ rằng việc xác định / sử dụng một số giao diện chung chung như INotifyPropertyChanged (như trong c #) là điều xấu, vì dù sao nó không được tự động kết hợp với một chế độ xem - nó chỉ nói rằng nếu tôi thay đổi, Tôi sẽ nói với bạn .

Một lần nữa, tôi đồng ý sẽ thật tuyệt nếu chúng ta không phải tự xác định thông báo thay đổi, nhưng sau đó một lần nữa nếu nó ẩn cho tất cả các lớp thì nó có thể phải chịu chi phí.


tôi càng nghĩ về nó, tôi nhận ra rằng mình nói đúng - đối tượng mô hình được tham gia vào một số mức độ trong việc khởi xướng thông báo cho GUI rằng một sự thay đổi đã xảy ra; nếu không, GUI sẽ phải thăm dò mô hình để khám phá các thay đổi - và điều đó thật tệ.
Chap
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.