Java Swing의 View / Controller에서 모델을 완전히 분리하는 방법


10

Java Swing 앱의 View / Controller 클래스에서 Model 클래스를 분리하기 위해 일반적으로 합의 된 설계 지침 모음이 있습니까? View / Controller가 다른 방법으로 Model에 대해 아무것도 모르는 것에 대해 걱정하지 않습니다. javax.swing에 대한 지식이 없도록 Model을 설계하고 싶습니다. 이상적으로 CLI와 같은 기본 요소로 구동 할 수있는 간단한 API가 있어야합니다. 느슨하게 말하면 "엔진"이어야합니다.

GUI 이벤트를 모델에 전달하는 것은 그리 어렵지 않습니다. 조치 수행자는 모델의 API를 호출 할 수 있습니다. 그러나 모델이 자체 상태를 변경하여 GUI에 다시 반영해야하는 경우는 어떻습니까? 그것이 "듣기"를위한 것이지만 "듣는 것"조차 완전히 수동적 인 것은 아닙니다. 모델이 리스너 추가에 대해 알아야합니다.

내가 생각하게 한 특정 문제는 파일 대기열과 관련이 있습니다. GUI 측면에는 DefaultListModel뒤에가 JList있으며 파일 시스템에서 파일을 선택하고 JList에 추가하는 GUI 항목이 있습니다. 모델 쪽에서는이 "큐"의 맨 아래에서 파일을 가져 와서 (JList에서 사라지게 함) 어떤 식 으로든 처리하려고합니다. 실제로 Model 코드는 이미 작성되어 있습니다. 현재 ArrayList<File>는 공용 add(File)메소드를 유지 보수 하고 공개합니다 . 그러나 모델에 대한 스윙 전용 수정없이 모델을 뷰 / 컨트롤러와 함께 사용하는 방법에 대해서는 손실이 있습니다.

저는 지금까지 "배치"및 "백엔드"프로그래밍을 항상 해왔 던 Java 및 GUI 프로그래밍에 매우 익숙합니다. 따라서 가능한 경우 모델과 UI를 엄격하게 구분하는 데 관심이 있습니다. 가르치십시오.



Btw : MVC에서 모델은 기본적으로 뷰와 컨트롤러에서 분리되어야합니다. 당신은 교과서를 다시 읽어야합니다. 나는 당신이 개념을 이해하지 못했다고 생각합니다. 또한 .NET의 INotifyCollectionChanged 인터페이스와 같이 변경 시점을 알리는 사용자 지정 컬렉션을 구현할 수 있습니다.
팔콘

@ 팔콘 : 링크 주셔서 감사합니다. 그러나 귀하의 의견을 잘 모르겠습니다. "모델은 기본적으로보기와 컨트롤러에서 분리되어야합니다". 다시 말하거나 설명해 주시겠습니까?
Chap

답변:


10

MVC에 대해 일반적으로 합의 된 (즉, 사실상의 ) 설계 지침은 없습니다. 혼자서하는 것이 어렵지는 않지만 수업에 대한 계획과 많은 시간과 인내심이 필요합니다.

확실한 해결책이없는 이유는 장단점을 가지고 MVC를 수행하는 여러 가지 방법이 있기 때문입니다. 따라서 그것에 대해 현명하고 자신에게 가장 적합한 것을하십시오.

귀하의 질문에 대답하기 위해 실제로 컨트롤러를 뷰에서 분리하려고합니다 (따라서 Swing 앱과 콘솔 앱 모두에 동일한 비즈니스 규칙 논리를 사용할 수 있습니다). Swing 예제에서는 Swing의 JWindow및 어떤 위젯에서 컨트롤러를 분리하려고합니다 . (실제 프레임 워크를 사용하기 전에) 내가했던 방식은 컨트롤러가 사용하는보기에 대한 인터페이스를 만드는 것입니다.

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());
    }

}

시작하는 동안이 솔루션을 사용하려면 컨트롤러를보기에 등록해야합니다.

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
    }

}

대신 모든 설정을 수행하기 위해 IoC 컨테이너를 생성하는 것이 좋습니다.

어쨌든 동일한 컨트롤러를 사용하여 콘솔 전용보기를 구현할 수 있습니다.

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

}    

재미있는 부분은 이벤트 처리 방법입니다. 인터페이스를 사용하여 뷰를 컨트롤러에 등록하도록하여 이것을 구현했습니다. 이것은 Observer 패턴을 사용하여 수행됩니다 (.NET을 사용하는 경우 대신 이벤트 핸들러를 사용합니다). 다음은 문서를 저장하거나로드 할 때 신호를 보내는 간단한 "문서 관찰자"의 예입니다.

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);
    }        
}

이렇게하면 뷰가 문서 업데이트를 구독하고 있으므로 뷰 자체가 제대로 업데이트 될 수 있습니다. DocumentObserver인터페이스 를 구현하기 만하면됩니다.

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
    }

    // ...

}

이러한 동기 부여 예제를 통해 직접 수행하는 방법에 대한 아이디어를 얻을 수 있기를 바랍니다. 그러나 Java에서 대부분의 작업을 수행하는 프레임 워크를 사용하는 것이 좋습니다. 그렇지 않으면 많은 시간이 걸리는 상용구 코드가 생길 수 있습니다. 응용 프로그램 전체의 문서 처리 및 많은 기본 이벤트 처리와 같이 가장 필요한 몇 가지 기본 기능을 구현하는 데 사용할 수있는 RCP (Rich Client Platforms)가 있습니다.

내 머리에서 생각할 수있는 몇 가지가있다 : EclipseNetbeans RCP.

여전히 스스로 컨트롤러와 모델을 개발해야하지만, ORM을 사용하는 이유입니다. 예는 최대 절전 모드 입니다.

IoC 컨테이너는 모두 멋지지만 이에 대한 프레임 워크도 있습니다. Spring 과 같은 (데이터 처리뿐만 아니라 다른 것들도 수행).


이렇게 긴 답변을 보내 주셔서 감사합니다. 이 시점에서 대부분은 내 머리 위에 약간 있지만 Wikipedia가 도움이 될 것이라고 확신합니다. 내가 하고 이클립스와 넷빈즈를 모두 사용하고, 후자에 기대어. Controller와 View를 구별하는 방법을 잘 모르겠지만 상단의 사전 프레임 워크 예제가 도움이됩니다.
Chap

0

이것에 대한 나의 취지는 때때로 우리는 타협해야한다

당신이 말했듯이, 관찰 된 객체가 명시적인 인프라를 갖지 않고 변경 알림을 암시 적으로 전파 할 수 있다면 좋을 것입니다. Java, C #, C ++와 같은 일반적인 명령 언어의 경우 런타임 아키텍처는 현재로서는 너무 가볍습니다. 즉, 현재로서는 언어 사양의 일부가 아닙니다.

특정 경우에, 나는 정확히 / 정의 같은 몇 가지 일반적인 인터페이스를 사용하는 것이 나쁜 일이라고 생각하지 않습니다 에서 INotifyPropertyChanged를 그 자동 어쨌든보기에 연결되지 않기 때문에, (C #에서로) - 그냥 말한다 나는 변화하는 경우, 나는 당신을 말할 것이다 .

다시 한 번, 변경 알림을 직접 정의하지 않아도된다면 좋겠지 만 모든 클래스에 대해 암시 적이면 오버 헤드가 발생할 수 있습니다.


더 나는 그것에 대해 생각, 난 당신이 맞아요을 실현 - 모델 객체가 가지고 에 참여하는 몇 가지 변화가 발생하는 GUI에 통지를 시작의 정도; 그렇지 않으면 GUI는 변경 사항을 발견하기 위해 모델을 폴링해야합니다.
Chap
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.