Java Swing'de Modelden Görünüm / Denetleyiciden Tamamen Ayırma


10

Bir Java Swing uygulamasında Model sınıflarını Görünüm / Denetleyici sınıflarından ayırmak için genel olarak üzerinde anlaşılan tasarım yönergeleri koleksiyonu var mı? Görünüm / Denetleyici, Model hakkında başka bir şey bilmediği için endişelenmiyorum: Modelimi javax.swing'de hiçbir şey bilmeyecek şekilde tasarlamak istiyorum. İdeal olarak, bir CLI kadar ilkel bir şey tarafından yönlendirilmesini sağlayan basit bir API'ye sahip olması gerekir. Gevşek bir şekilde bir "motor" olmalıdır.

GUI olaylarını modele iletmek çok zor değildir - Eylem Sanatçılar modelin API'sını çağırabilir. Ancak, modelin GUI'ye geri yansıtılması gereken kendi durum değişikliklerini ne zaman yaptığı hakkında? “Dinleme” bunun için var, ama “dinlenmek” bile tamamen pasif değil; modelin dinleyici eklemeyi bilmesi gerekir.

Beni düşündüren sorun, bir Dosya sırası içeriyor. GUI tarafında bir DefaultListModelarka a JListvardır ve dosya sisteminden dosyaları seçmek ve bunları JList'e eklemek için bazı GUI öğeleri vardır. Model tarafında, dosyaları bu "kuyruğun" altından çıkarmak (JList'ten kaybolmalarına neden olmak) ve bir şekilde işlemek istiyor. Aslında, Model kodu zaten yazılmıştır - şu anda ArrayList<File>bir genel add(File)yöntemi korur ve açıklar . Ancak Modelimde, Modelde ağır, Swing'e özgü bazı değişiklikler yapılmadan Görünüm / Denetleyici ile nasıl çalışacağı konusunda kayıp yaşıyorum.

Java ve GUI programlamada çok yeniyim, şimdiye kadar her zaman "toplu" ve "arka uç" programlamayı yaptım. öğretilecek.



Btw: MVC'de model varsayılan olarak görünümden ve denetleyiciden ayrılmalıdır. Ders kitabınızı tekrar okumalısınız, sanırım konsepti tam olarak anlamadınız. Ve .NET'in INotifyCollectionChanged arayüzü gibi, değiştiğinde bunu bildiren özel bir koleksiyon uygulayabilirsiniz.
Falcon

@Falcon: bağlantılar için teşekkürler. Ancak yorumunuzu anladığımdan emin değilim .. "model varsayılan olarak görünümden ve denetleyiciden ayrılmalıdır". Yeniden ifade edebilir veya ayrıntılandırabilir misiniz?
Chap

Yanıtlar:


10

MVC için genel olarak üzerinde anlaşmaya varılmış (yani defacto ) bir tasarım kılavuzu yoktur. Bunu kendiniz yapmak o kadar da zor değil ama sınıflarınız üzerinde biraz planlama ve çok fazla zaman ve sabır gerektiriyor.

Kesin bir çözüm bulunmamasının nedeni, MVC yapmanın birden fazla yolu olması, hepsinin artıları ve eksileri ile olmasıdır. Bu yüzden akıllı olun ve size en uygun olanı yapın.

Sorunuzu cevaplamak için, aslında denetleyiciyi görünümden de ayırmak istersiniz (böylece hem Swing uygulaması hem de konsol uygulaması için aynı iş kuralı mantığını kullanabilirsiniz). Swing örneğinde, denetleyiciyi Swing'deki JWindowve herhangi bir widget'tan ayırmak istiyorsunuz . Eskiden (gerçek çerçeveleri kullanmadan önce) yaptığım yol, denetleyicinin kullandığı görünüm için bir arayüz oluşturmaktır:

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

}

Başlatma sırasında bu çözüm için denetleyiciyi görünüme kaydetmeniz gerekir.

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
    }

}

Bunun yerine tüm ayarları yapmak için bir IoC kapsayıcısı oluşturmak iyi bir fikir olabilir.

Her neyse, bu şekilde aynı denetleyicileri kullanarak yalnızca konsol görünümlerini uygulayabilirsiniz:

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

}    

İşin eğlenceli kısmı olay işlemenin nasıl yapılacağıdır. Bunu, bir arabirim kullanarak görünümün kendisini denetleyiciye kaydettirmesine izin vererek uyguladım, bu, Gözlemci deseni kullanılarak yapılır (.NET kullanıyorsanız olay işleyicileri kullanırsınız). Burada, bir belgenin ne zaman kaydedildiğini veya yüklendiğini gösteren basit bir "belge gözlemcisi" örneği vardır.

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

Bu şekilde görünüm, belge güncellemelerine abone olduğu için kendisini düzgün bir şekilde güncelleyebilir. Tek yapması gereken DocumentObserverarayüzü uygulamaktır :

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
    }

    // ...

}

Umarım bu motive edici örnekler size bunu nasıl yapacağınız konusunda bazı fikirler verir. Bununla birlikte, Java'da sizin için çoğu şeyi yapan çerçeveler kullanmayı düşünmenizi önemle tavsiye ederim, yoksa yazmak için uzun zaman alan çok sayıda kaynak koduna sahip olursunuz. Uygulama çapında belge işleme ve çok sayıda temel olay işleme gibi, büyük olasılıkla ihtiyaç duyduğunuz bazı temel işlevleri uygulamak için kullanabileceğiniz birkaç Zengin İstemci Platformu (RCP) vardır.

Aklımdan aklıma gelen bir çift var: Eclipse ve Netbeans RCP'ler.

Hala kendiniz için kontrolör ve modeller geliştirmeniz gerekiyor, ancak bu yüzden bir ORM kullanıyorsunuz. Örnek Hazırda Bekletme olacaktır .

IoC kapları harika ve hepsi de bunun için çerçeveler var. Bu şekilde Spring (aynı zamanda yok olan veriler, diğer şeylerin yanı sıra hem de taşıma).


Bu kadar uzun bir yanıt yazmaya zaman ayırdığınız için teşekkür ederiz. Çoğu bu noktada biraz kafamın üstünde, ama eminim Wikipedia yardımcı olacaktır. Ben am Eclipse ve NetBeans ikisini de kullanarak ve ikincisi için eğilerek. Denetleyiciyi oradaki görünümden nasıl ayıracağımdan emin değilim, ancak üstteki ön çerçeve örneğiniz yardımcı olur.
Chap

0

Bunu benim almam, bazen taviz vermemiz gerektiğidir

Söylediğiniz gibi, gözlemlenen nesne bunun için açık bir altyapıya sahip olmadan değişiklik bildirimini örtülü bir şekilde yaymak harika olurdu. Java, C #, C ++ gibi yaygın zorunluluk dilleri için çalışma zamanı mimarisi şu an itibariyle çok hafif. Bununla demek istediğim, şu anda dil spesifikasyonunun bir parçası değil.

Sizin özel durumunuzda, INotifyPropertyChanged (c # 'da olduğu gibi) gibi genel bir arabirimi tanımlamanın / kullanmanın tam olarak kötü bir şey olduğunu düşünmüyorum , çünkü bu zaten otomatik olarak bir görünüme bağlı değil - sadece değiştirirsem, Size söyleyeceğim .

Yine, değişiklik bildirimini kendimiz tanımlamak zorunda kalmazsak harika olacağına katılıyorum, ancak daha sonra tüm sınıflar için örtük olsaydı, yüke neden olabilir.


daha bunu düşünmek, sana yaptıkları anda fark - Model nesne vardır edilmesi için gereken bazı bir değişimin meydana gelmiş olması GUI bildirimi başlatmadaki ölçüde; aksi halde GUI'nin değişiklikleri keşfetmek için modeli yoklaması gerekirdi - ve bu kötü.
Chap
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.