Java Swingでモデルをビュー/コントローラーから完全に分離する方法


10

Java Swingアプリで、ModelクラスをView / Controllerクラスから分離するための、よく合意された設計ガイドラインのコレクションはありますか?ビュー/コントローラーがモデルについて何も知らないことをそれほど心配しているわけではありません。逆に、モデルを設計して、javax.swingで何も認識しないようにしたいと考えています。理想的には、CLIのようなプリミティブによって駆動できるようにする単純なAPIが必要です。大まかに言えば「エンジン」である必要があります。

モデルへのGUIイベントの伝達はそれほど難しくありません。アクション実行者はモデルのAPIを呼び出すことができます。しかし、モデルがGUIに反映する必要がある独自の状態変更を行う場合はどうでしょうか?それが「聞く」ことの目的ですが、「聞く」ことさえ完全に受動的ではありません。モデルがリスナーの追加について知っている必要があります。

考えさせられた特定の問題は、ファイルのキューに関係しています。GUI側にはのDefaultListModel後ろにありJList、ファイルシステムからファイルを選択してJListに追加するためのGUIがいくつかあります。モデル側では、この「キュー」の最下部からファイルをプルし(JListからファイルを非表示にします)、何らかの方法でそれらを処理します。実際、Modelコードは既に作成されています-現在、を維持しArrayList<File>、パブリックadd(File)メソッドを公​​開しています。しかし、モデルにSwing固有の大きな変更を加えずに、ビュー/コントローラーでモデルを機能させる方法について、私は途方に暮れています。

私はJavaとGUIプログラミングの両方に非常に慣れていないため、これまで「バッチ」プログラミングと「バックエンド」プログラミングを常に行ってきました。したがって、可能であれば、モデルとUIの間の厳密な分割を維持することに関心があります。教わります。



ところで:MVCでは、モデルはデフォルトでビューとコントローラーから切り離されている必要があります。教科書をもう一度読んでください。コンセプトがよくわかりません。また、.NETのINotifyCollectionChangedインターフェイスのように、変更時に通知するカスタムコレクションを実装することもできます。
ファルコン

@ファルコン:リンクをありがとう。しかし、あなたのコメントを理解できているかどうかはわかりませんが、「モデルはデフォルトでビューとコントローラから切り離されている必要があります」。言い換えたり、詳しく説明してもらえますか?

回答:


10

MVCには一般に合意された(つまり、事実上の)設計ガイドラインはありません。自分で行うのはそれほど難しいことではありませんが、クラスの計画と多くの時間と忍耐が必要です。

明確な解決策がない理由は、MVCを実行する方法が複数あり、すべて長所と短所があるためです。だから、それについて賢くなり、あなたに最も適したことをしてください。

あなたの質問に答えるために、実際にはコントローラーもビューから切り離したいと考えています(そのため、Swingアプリとコンソールアプリの両方に同じビジネスルールロジックを使用できます)。Swingの例では、コントローラーJWindowをSwingのウィジェットから切り離します。(実際のフレームワークを使用する前に)使用した方法は、コントローラーが使用するビューのインターフェースを作成することです。

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)がいくつかあります。

私の頭から考えることができるカップルがいくつかあります:EclipseNetbeans RCPです。

自分でコントローラーとモデルを開発する必要がありますが、それがORMを使用する理由です。例はHibernateです。

IoCコンテナーはすべてがクールですが、これのためのフレームワークもあります。以下のような(また、データは他のものの中だけでなくとして扱うん)。


長い回答を書いてくれてありがとう。この時点ではほとんどの部分が頭上にありますが、ウィキペディアが役立つと確信しています。私 EclipseとNetbeansの両方を使用しており、後者に頼っています。コントローラとビューを区別する方法はわかりませんが、上部のフレームワーク前の例が役立ちます。
2011

0

私の見解では、時々妥協する必要があります

あなたが言うように、観測されたオブジェクトがこれのための明示的なインフラストラクチャを持たずに、変更通知を暗黙的に伝達できたら素晴らしいでしょう。Java、C#、C ++のような一般的な命令型言語の場合、ランタイムアーキテクチャは今のところとにかく軽量すぎます。つまり、現時点では言語仕様の一部ではありません。

あなたの特定のケースでは、INotifyPropertyChanged(c#のような)のようないくつかの汎用インターフェイスを定義/使用することは、決して悪いことではないと思います。教えます

繰り返しになりますが、変更通知を自分で定義する必要がない場合はすばらしいと思いますが、すべてのクラスで暗黙的な場合はオーバーヘッドが発生する可能性があります。


より多くの私はそれについて考え、私はあなたにしている権利を実現-モデルオブジェクトが持っているに関与していると、いくつかの変更が発生したことをGUIへの通知を開始することに程度; そうでない場合、GUIは変更を検出するためにモデルをポーリングする必要があります-それは悪いことです。
Chap
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.