Java Swing sınıfını ne zaman genişletmeliyim?


35

Şu anki miras uygulama anlayışım, birinin sadece bir IS-A ilişkisi mevcutsa bir sınıfı genişletmesi gerektiği yönünde . Ebeveyn sınıfı ayrıca, farklı işlevselliğe sahip daha spesifik alt tipleri içerebilir ancak ebeveynde soyutlanmış ortak unsurları paylaşacaktır.

Java profesörümün bize yapmamızı önerdiği şey yüzünden bu anlayışı sorguluyorum. JSwingSınıfta inşa ettiğimiz bir uygulama için tavsiye etti.

Bir bütün uzanmalıdır JSwingsınıfları ( JFrame, JButton, JTextBoxayrı bir özel sınıfa, vs) ve (bileşen büyüklüğü, bileşen etiket, vb gibi) onları GUI ilgili özelleştirme belirtmek

Şimdiye dek çok iyi, ancak tek ayırt edici faktör onların etiketi olmasına rağmen her JButton'un kendi özel genişletilmiş sınıfına sahip olması gerektiğini tavsiye etmeye devam ediyor.

Örneğin, GUI'de iki düğme varsa Tamam ve İptal . Aşağıdaki şekilde genişletilmelerini önerir:

class OkayButton extends JButton{
    MainUI mui;
    public OkayButton(MainUI mui) {
        setSize(80,60);
        setText("Okay");
        this.mui = mui;
        mui.add(this);        
    }
}

class CancelButton extends JButton{
    MainUI mui;
    public CancelButton(MainUI mui) {
        setSize(80,60);
        setText("Cancel");
        this.mui = mui;
        mui.add(this);        
    }
}

Gördüğünüz gibi tek fark setTextfonksiyonda.

Peki bu standart pratik mi?

Btw, bunun tartışıldığı kursa Java'daki En İyi Programlama Uygulamaları denir .

[Prof. Kullanıcısının yanıtı]

Bu yüzden sorunu profesörle tartıştım ve cevaplarda belirtilen bütün noktaları dile getirdim.

Gerekçesi, GUI tasarım standartlarını takip ederken alt sınıflamanın yeniden kullanılabilir kod sağlamasıdır. Örneğin, geliştirici bir pencerede özel Okayve Canceldüğmeler kullandıysa , aynı düğmeleri diğer Windows'a da yerleştirmek daha kolay olacaktır.

Sanırım sebebi anlıyorum, ama yine de sadece mirastan yararlanma ve kodu kırılgan hale getirme.

Daha sonra, herhangi bir geliştirici yanlışlıkla setTextbir Okaydüğmeyi arayabilir ve değiştirebilir. Bu durumda alt sınıf sadece sorun çıkarır.


3
Neden JButtonyapıcıda ortak yöntemleri genişletip çağırıp, basitçe oluşturabilir JButtonve aynı ortak yöntemleri sınıf dışında çağırabiliyorsanız?

17
eğer yapabilirsen o profesörden uzak dur. Bu, en iyi uygulamalar olmaktan oldukça uzak . Gösterdiğiniz gibi kod çoğaltma çok kötü bir koku.
njzk2

7
Sadece nedenini sor, motivasyonunu burada iletmekte tereddüt etme.
Alex,

9
Oy vermek istemiyorum çünkü bu çok kötü bir kod, ama aynı zamanda kötü olmak da istiyorum çünkü kötü bir profesörün söylediklerini körü körüne kabul etmek yerine sorgulamanız harika.
Nic Hartley,

1
@Alex Profesörün gerekçesiyle soruyu güncelledim
Paras

Yanıtlar:


21

Bu ihlal Liskov değiştirme prensibi bir nedeni OkayButtonherhangi bir yerde ikame edilemez Buttonbekleniyor. Örneğin, herhangi bir düğmenin etiketini istediğiniz gibi değiştirebilirsiniz. Ancak bunu yapmak OkayButton, içsel değişmezleri ihlal etmekle.

Bu, kod yeniden kullanımı için klasik bir miras istismarıdır. Bunun yerine bir yardımcı yöntem kullanın.

Bunu yapmamak için bir başka neden, bunun doğrusal kodun yapacağı aynı şeyi başarmanın basit bir yoludur.


Bu OkayButtondüşündüğünüz değişmez olan sürece, LSP'yi ihlal etmez . OkayButtonBu sadece bir varsayılan olarak metni düşünün ve tam metne değişiklikleri desteklemek niyetinde olabilir, herhangi bir ekstra değişmezleri almamayı talep edebilir. Hala iyi bir fikir değil, bu nedenle değil.
hvd

Bunu ihlal etmiyor çünkü sen yapabilirsin. Yani, bir yöntem activateButton(Button btn)bir düğme beklerse, kolayca bir örneğini verebilirsiniz OkayButton. Prensip sahip olması anlamına gelmez tam bu miras hemen hemen yararsız yapar gibi, aynı işlevi.
Sebb

1
@Sebb, ancak setText(button, "x")(varsayılan) değişmezliği ihlal ettiği için onu bir işlevle kullanamazsınız. Bu özel Tamam düğmesinin ilginç bir değişmezinin olamayacağı doğrudur, bu yüzden kötü bir örnek seçtim. Ama olabilir. Örneğin, tıklama işleyicisi ne diyorsa if (myText == "OK") ProcessOK(); else ProcessCancel();? O zaman metin değişmezin bir parçasıdır.
usr

@ usr Metin değişmez bir parçası olduğunu düşünmedim. Haklısın, ihlal edildi o zaman.
Sebb,

50

Mümkün olan her şekilde tamamen korkunç. At en , JButtons üretmek için fabrika işlevini kullanın. Onlardan yalnızca bazı ciddi uzatma gereksinimleriniz varsa devralmalısınız.


20
+1. Daha fazla referans için, AbstractButton'un Swing sınıfı hiyerarşisine bakın . Sonra JToggleButton hiyerarşisine bakın . Kalıtım , farklı düğme türlerini kavramsallaştırmak için kullanılır . Ancak işletme kavramlarını ayırt etmek için kullanılmaz ( Evet, Hayır, Devam Et, İptal ... ).
Laiv

2
@Laiv: bile, örneğin için farklı türde olan JButton, JCheckBox, JRadioButton, bu tip standart düz AWT gibi diğer araç, aşina olan geliştiriciler için sadece bir imtiyaz. Prensip olarak, hepsi tek bir düğme sınıfı tarafından idare edilebilir. Gerçek fark yaratan model ve UI temsilcisinin birleşimidir.
Holger

Evet, tek bir düğme ile idare edilebilirler. Ancak asıl hiyerarşi iyi uygulamalar olarak gösterilebilir. Yeni bir Düğme oluşturmak veya yalnızca olayın temsilcisini devretmek ve davranışın başka bileşenlere kontrolünü vermek tercih meselesidir. Ben olsaydım, bayım düğümü değiştirmek ve davranışlarını, eylemlerini içine almak için Swing bileşenlerini genişletirdim ... Projede uygulanmasını kolaylaştırmak ve gençler için kolaylaştırmak için. Web envs'de web bileşenlerini çok fazla olay işleme yerine tercih ederim. Neyse. Haklısın UI'yi Davranışçılardan ayırabiliriz.
Laiv

12

Bu, Swing kodu yazmanın standart olmayan bir yoludur. Genellikle, nadiren yalnızca Swing UI bileşenlerinin alt sınıflarını, çoğunlukla JFrame'i (alt pencereler ve olay işleyicileri ayarlamak için) yaparsınız, ancak bu alt sınıflama bile gereksizdir ve çoğu kişi tarafından önerilmez. Metnin düğmelere ve benzerlerine özelleştirilmesini sağlamak genellikle AbstractAction sınıfını (veya sağladığı Eylem arayüzünü ) genişleterek yapılır . Bu, metin, simgeler ve diğer gerekli görsel özelleştirmeleri sağlayabilir ve bunları temsil ettikleri gerçek koda bağlayabilir. Bu, UI kodu yazmanın gösterdiğiniz örneklerden çok daha iyi bir yoludur.

(bu arada, Google alimi alıntı yaptığınız makaleleri hiç duymamış - daha kesin bir referansınız var mı?)


Tam olarak "standart yol" nedir? Her bileşen için bir alt sınıf yazmanın muhtemelen kötü bir fikir olduğunu kabul ediyorum, ancak resmi Swing dersleri hala bu uygulamayı destekliyor. Profesörün sadece çerçevenin resmi belgelerinde gösterilen stili takip ettiğini düşünüyorum.
GELMEKTEDİR

@COMEFROM Tüm Swing dersini okumamış olduğumu itiraf edeceğim, bu yüzden belki bu tarzın nerede kullanıldığını özledim, fakat baktığım sayfalar temel sınıfları kullanma eğilimindedir ve alt sınıfları kullanmaz, örneğin docs.oracle .com / javase / tutorial / uiswing / components / button.html
Jules

@Jules karışıklık için özür dilerim, demek istediğim, profesörün öğrettiği ders / makale Java'nın En İyi Uygulamaları
Paras

1
Genel olarak doğru, fakat bir başka büyük istisna daha var - JPanel. JFrameBir panel sadece soyut bir kap olduğundan , bunu alt sınıfa koymak daha faydalı olur .
Ordous

10

IMHO, Java'daki En İyi Programlama Uygulamaları Joshua Bloch'un kitabı olan "Etkin Java" ile tanımlanır. Öğretmeninizin size OOP alıştırmaları vermesi harika ve başkalarının programlama stillerini okumayı ve yazmayı öğrenmek önemlidir. Ancak, Josh Bloch'un kitabının dışında, görüşler en iyi uygulamalar konusunda oldukça geniş bir çeşitlilik gösteriyor.

Eğer bu sınıfı genişletecekseniz, kalıtımdan da faydalanabilirsiniz. Ortak kodu yönetmek için bir MyButton sınıfı yapın ve değişken parçalar için onu alt sınıflayın:

class MyButton extends JButton{
    protected final MainUI mui;
    public MyButton(MainUI mui, String text) {
        setSize(80,60);
        setText(text);
        this.mui = mui;
        mui.add(this);        
    }
}

class OkayButton extends MyButton{
    public OkayButton(MainUI mui) {
        super(mui, "Okay");
    }
}

class CancelButton extends MyButton{
    public CancelButton(MainUI mui) {
        super(mui, "Cancel");
    }
}

Bu ne zaman iyi bir fikir? Ne zaman kullanmak türlerini Oluşturduğunuz! Örneğin, bir açılır pencere oluşturma işleviniz varsa ve imza şudur:

public void showPopUp(String text, JButton ok, JButton cancel)

Yeni yarattığın bu tipler hiçbir şekilde işe yaramıyor. Fakat:

public void showPopUp(String text, OkButton ok, CancelButton cancel)

Şimdi yararlı bir şey yarattın.

  1. Derleyici, showPopUp öğesinin bir OkButton ve CancelButton aldığını doğrular. Kodu okuyan biri bu işlevin nasıl kullanılacağını bilir, çünkü bu tür belgeler eski zamanlarında derleme zamanı hatasına neden olur. Bu bir ana parasıdır. Tip güvenliğin yararları üzerine yapılan 1 veya 2 deneysel araştırma insan kodunun anlaşılmasının tek ölçülebilir fayda olduğunu buldu.

  2. Bu, işleve ilettiğiniz düğme sırasını tersine çevirdiğiniz hataları da önler. Bu hataların tespit edilmesi zordur, ancak oldukça nadirdir, bu nedenle bu bir MINOR yararıdır. Bu, tip güvenliğini satmak için kullanıldı, ancak ampirik olarak özellikle faydalı olduğu kanıtlanmadı.

  3. İşlevin ilk şekli daha esnektir çünkü herhangi iki düğmeyi de görüntüler. Bazen bu, ne tür düğmeler alacağını kısıtlamaktan daha iyidir. Herhangi bir tuş işlevini daha fazla koşulda test etmeniz gerektiğini unutmayın - yalnızca doğru şekilde doğru düğmelere basarsanız çalışır, herhangi bir tuşa basar gibi davranarak herhangi bir iyilik yapmazsınız.

Nesneye Yönelik Programlamanın sorunu, arabayı attan önce koymasıdır. İmzanın ne olduğunu bilmek için önce işlevi yazmanızın yararı olup olmadığını bilmek için işlevi yazmanız gerekir. Ancak Java'da, türlerinizi önce işlev imzasını yazabilmeniz için yapmanız gerekir.

Bu nedenle, önce işlevlerinizi yazmanın ne kadar harika olduğunu görmek için bazı Clojure kodları yazmak isteyebilirsiniz. Olabildiğince asimptotik karmaşıklığı düşünerek hızlıca kodlayabilirsiniz ve Clojure'un değişmezlik varsayımı, Java'da türlerde olduğu gibi hataları önler.

Hala büyük projeler için statik türlerin hayranıyım ve şimdi ilk önce işlevleri yazmam ve türleriimi daha sonra Java olarak adlandırmama izin veren bazı işlevsel yardımcı programları kullanıyorum . Sadece bir düşünce.

PS muiİşaretçiyi final yaptım - değişken olması gerekmez.


6
3 puanınızdan 1 ve 3, genel JButtonlarla ve uygun bir giriş parametresi adlandırma şemasıyla ele alınabilir. 2. nokta aslında kodunuzu daha sıkı bir şekilde bağlamanızı sağlaması bakımından bir dezavantajdır: ya düğme sırasının tersine çevrilmesini istersen? Peki, Tamam / İptal düğmeleri yerine, Evet / Hayır düğmelerini kullanmak istiyorsanız? YesButton ve Nobutton alan tamamen yeni bir showPopup yöntemi mi yapacaksınız? Yalnızca varsayılan JButtons kullanıyorsanız, türlerinizi zaten var oldukları için ilk yapmanız gerekmez.
Nzall

@NateKerkhofs’un söylediklerini uzatarak, modern bir IDE, gerçek ifadeleri yazarken size resmi argüman adlarını gösterecektir.
Solomon Yavaş

8

Öğretmenin, kullanmak için her zaman bir Swing bileşenini genişletmeniz gerektiğine gerçekten inanmadığına bahse girerim. İddiaya girerim, sizi sadece dersleri uzatmaya zorlamak için bir örnek olarak kullanıyorlar. Henüz gerçek dünyadaki en iyi uygulamalar hakkında fazla endişelenmedim.

Olduğu söyleniyor, gerçek dünyada mirasa ilişkin kompozisyonu tercih ediyoruz .

"Bir IS-A ilişkisi varsa yalnızca bir sınıfı genişletmeli" kuralınız eksik. Büyük harflerle "... ve sınıfın varsayılan davranışını değiştirmeliyiz" ile bitmeli .

Örnekleriniz bu kriterlere uymuyor. Sadece metnini ayarlamak extendiçin JButtonsınıfa gerek yok . Sen gerekmez , sadece kendisine bileşenler eklemek için sınıfa. Bunları varsayılan uygulamalarını kullanarak gayet iyi yapabilirsiniz, bu yüzden miras eklemek sadece gereksiz komplikasyonlar eklemek demektir.extendJFrame

Başka bir sınıfın olduğu extendsbir sınıf görürsem , bu sınıfın neyi değiştirdiğini merak ediyorum . Eğer hiçbir şeyi değiştirmiyorsanız, beni sınıfta hiç gözden geçirmeyin.

Sorunuza dönün: bir Java sınıfını ne zaman genişletmelisiniz? Bir sınıfı genişletmek için gerçekten, gerçekten, gerçekten iyi bir nedeniniz olduğunda.

İşte size özel bir örnek: Özel boyama yapmanın bir yolu (bir oyun veya animasyon için veya sadece özel bir bileşen için) JPanelsınıfı genişletmektir . (Bu konuda daha fazla bilgiye buradan .) Sen uzatmak JPanelEğer gerektiğinden sınıfını geçersizpaintComponent() işlevini. Aslında bunu yaparak sınıfın davranışını değiştiriyorsunuz . Varsayılan JPaneluygulama ile özel boyama yapamazsınız .

Ama dediğim gibi, öğretmeniniz muhtemelen bu örnekleri, sizi genişleyen sınıflar üzerinde çalışmaya zorlamak için bir bahane olarak kullanıyor.


1
Oh bekleyin, Borderbaşka süslemeler boyayan bir tane ayarlayabilirsiniz . Ya da bir a oluşturun JLabelve Iconona özel bir uygulama iletin. JPanelBoyama için alt sınıfa geçmek gerekmez (ve neden JPanelyerine JComponent).
Holger

1
Burada doğru fikre sahip olduğunuzu düşünüyorum, ancak daha iyi örnekler seçebilirim.

1
Ama cevabın doğru olduğunu yinelemek istiyorum ve iyi puanlar verdin . Sadece belirli bir örnek ve öğreticinin onu zayıf bir şekilde desteklediğini düşünüyorum.

1
@KevinWorkman Swing oyununun gelişimi hakkında bir bilgim yok, ancak Swing'te bazı özel kullanıcı arayüzleri yaptım ve "Swing sınıflarını genişletmemek için komponentler ve işleyicileri config aracılığıyla bağlamak kolaydır" oldukça standart.

1
“Bahse girerim, sizi sadece sizi zorlamak için bir örnek olarak kullanıyorlar ...” Başlıca iş arkadaşlarımdan biri! Öğretmek Eğitmenler nasıl korkunç dengesiz örnekler kullanarak X yapmaya neden X'i yapmak
Solomon Yavaş
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.