Listener ve Adapter karşılaştırması

JavaBeans bileşen modeli (ve buna bağlı olarak Swing bileşen kümesi) özellikler ve eylemler üzerinde kurulmuştur. Özellikler setter ve getter'lar yardımıyla değerlerini kullandırmaktadır. Eylemler, oluşlarının farkedilmesi için dinleyicileri (Listener) kullanmayı ve arabirimleri (interface) gerçekleştirmeyi gerekli kılmaktadır. Özelliklerle çalışmak kolay olsa da, dinleyici nesnelerin -özellikle grafiksel kullanıcı arayüzü (GUI) dünyasında- nasıl çalıştığını anlamak için biraz açıklama yapmak gereklidir. Özellikle, bu yazıda hem dinleyici arabirimi hem de adaptör gerçekleştirimi sunan AWT ve Swing olay ilişkili sınıflarını açıklamaya çalışacağız.

Aşağıdaki sınıflar hem dinleyici hem de adaptör eşlerine sahip sınıflara örnek olarak gösterilebilir:
package java.awt.event
- ComponentListener / ComponentAdapter
- ContainerListener / ContainerAdapter
- FocusListener / FocusAdapter
- HierarchyBoundsListener / HierarchyBoundsAdapter
- KeyListener / KeyAdapter
- MouseListener / MouseAdapter
- MouseMotionListener / MouseMotionAdapter
- WindowListener / WindowAdapter

package java.awt.dnd
- DragSourceListener / DragSourceAdapter
- DragTargetListener / DragTargetAdapter

package javax.swing.event
- InternalFrameListener / InternalFrameAdapter
- MouseInputListener / MouseInputAdapter

Bu sınıf eşleri aynı işi yapmanın iki yolunu sunarlar. İlk olarak adaptör sınıfı sunmayan basit bir örneği inceleyelim. ActionListener sınıfı tek bir actionPerformed metoduna sahiptir. Anonim iç sınıf kullanarak, ActionListener sınıfını aşağıdaki şekilde kullanırız:

ActionListener listener = new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
System.out.println("Olay gerceklesti");
}
};

Ayrıca ActionListener arabirimini daha yüksek bir sınıfta actionPerformed metodunu gerçekleştirerek kullanabiliriz:

public class MyClass extends JFrame implements ActionListener {
...
public void actionPerformed(ActionEvent actionEvent) {
System.out.println("Olay gerceklesti");
}
}


ActionListener arabirimi tek bir metoda sahiptir, ve arabirimin gerçekleştiricileri sadece bu tek metod için bir gerçekleştirim sunmak zorundadır.

Diğer dinleyici arabirimleri bu kadar kolay değildir. Örneğin, MouseMotionListener arabirimi iki metod içermektedir: mouseDragged ve mouseMoved. Arabirim gerçekleştirirken, arabirim tarafından tanımlanan bütün metodları gerçekleştirmeniz gerekmektedir:

MouseMotionListener listener = new MouseMotionListener() {
public void mouseDragged(MouseEvent mouseEvent) {
System.out.println("Surukleniyorum: " + mouseEvent);
}

public void mouseMoved(MouseEvent mouseEvent) {
System.out.println("Hareket ediyorum: " + mouseEvent);
}
};


Bazı durumlarda uygulamanızda belli bir dinleyici arabirimi için tüm olayları izlemeniz gerekmeyebilir. Örneğin kodunuzun dinleyici arabirimindeki sadece bir veya iki metoda karşılık vermesi yeterli olabilecektir. Örneğin sadece farenin hareket ettiğini mi yoksa sürüklemeyi mi (tuşa basılı hareket ettirme) takip etmek istiyorsunuz? MouseMotionListener'ın sadece bir metodunu gerçekleştirip diğerini dışarıda bırakamazsınız:

MouseMotionListener badListener = new MouseMotionListener() {
public void mouseDragged(MouseEvent mouseEvent) {
System.out.println("Surukleniyorum: " + mouseEvent);
}
};


Bu dinleyici gerçekleştirimi derleme zamanı hatasına neden olacaktır. Çünkü arabirimdeki tüm metodlar gerçekleştirilmemiştir. MouseMotionListener gibi arabirimler için bu çok ciddi problem yaratmayabilir. İlgilenmediğiniz metodları boş bırakabilirsiniz:

MouseMotionListener listener = new MouseMotionListener() {

public void mouseDragged(MouseEvent mouseEvent) {
System.out.println("Surukleniyorum: " + mouseEvent);
}

public void mouseMoved(MouseEvent mouseEvent) {
// Birsey yapma
}
};


Tüm dinleyici arabirimleri bu kadar küçük değildir. MouseMotionListener sadece iki metoda sahiphen MouseListener arabirimi 5 metod içerir:
void mouseClicked(MouseEvent mouseEvent)
void mouseEntered(MouseEvent mouseEvent)
void mouseExited(MouseEvent mouseEvent)
void mousePressed(MouseEvent mouseEvent)
void mouseReleased(MouseEvent mouseEvent)
Bir bileşene MouseListener eklemek istiyorsanız, arabirim gerçekleştiriminiz beş metodu içermelidir:

MouseListener mouseListener = new MouseListener() {
public void mouseClicked(MouseEvent mouseEvent) {
System.out.println("Tiklandim: " + mouseEvent);
}

public void mouseEntered(MouseEvent mouseEvent) {
System.out.println("Girdim: " + mouseEvent);
}

public void mouseExited(MouseEvent mouseEvent) {
System.out.println("Ciktim: " + mouseEvent);
}

public void mousePressed(MouseEvent mouseEvent) {
System.out.println("Basildim: " + mouseEvent);
}

public void mouseReleased(MouseEvent mouseEvent) {
System.out.println("Birakildim: " + mouseEvent);
}

};


Eğer uygulamanızın bir bileşen üzerinde farenin sadece basıldığı veya bırakıldığını bilmesi gerekiyorsa, diğer metodlar boş olup yok sayılacaktır. Bu metodlar dolayısıyla gereksiz kod parçaları olacaktır. Adaptör sınıfları, arabirim metodlarının sadece küçük bir kümesine ihtiyaç duyduğunuzda kullandığınız ve yazmanız gereken kod miktarını azaltan sınıflardır. Her adaptör sınıfı ilgili arabirimi (veya arabirimleri) tam olarak gerçekleştirir. Böylece, ilişkili metodların küçük bir kümesine ihtiyaç duyarsanız, sadece kullanmak istediğiniz metodu doldurarak kullanabilirsiniz. Aşağıdaki örnekte MouseAdapter kullanarak bu şekilde bir işlem gösterilmiştir:

MouseListener mouseListener = new MouseAdapter() {
public void mousePressed(MouseEvent mouseEvent) {
System.out.println("Basildim: " + mouseEvent);
}

public void mouseReleased(MouseEvent mouseEvent) {
System.out.println("Birakildim: " + mouseEvent);
}
};


Bu kod MouseListener yaratacaktır. Ancak, tüm metodları gerçekleştirmek yerine, MouseAdapter yardımıyla sadece ihtiyaç duyduğumuz metodlarını gerçekleştirmemiz mümkün olacaktır.

Her birden fazla metod içeren dinleyici için adaptör vardır. Elbette kendimiz de bir arabirim için adaptör sınıfı yaratabiliriz. Böylece sık kullandığımız arabirim kümeleri oluşturabiliriz. Java içerisinde olan sınıflardan yukarıda listelenmiş olan dinleyiciler adaptörlere sahiptir. Ayrıca unutulmaması gereken adaptörlerin gerçek birer sınıf olduğudur. Arabirimin gerçekleştirilmesi bu sınıf içerisinde yapılarak, adaptör kullananların metodları ezmesi (override) sağlanmış oluyor. Eğer kendinize özgü bir JButton altsınıfının MouseListener arabirimini gerçekleştirmesini istiyorsanız, bu sınıfınızın MouseAdapter'un altsınıfı olmasını yapamazsınız, çünkü bildiğiniz gibi Java'da tekli kalıtım desteklenmektedir. Örneğin aşağıdaki kod örneği derleme zamanı hatasına sebep olacaktır, çünkü aynı sınıf hem JButton hem de MouseAdapter'den kalıtılamaz:

public class BadJButtonSubclass extends JButton, MouseAdapter {
...
public void mousePressed(MouseEvent mouseEvent) {
System.out.println("Basildim: " + mouseEvent);
}
}


Eğer gerçekten bu JButton altsınıfının ayrıca MouseListener olmasını istiyorsanız bunu özellikle belirtmeniz ve arabirimin tüm metodlarını gerçekleştirmeniz gerekir:

public class GoodJButtonSubclass extends JButton implements MouseListener {
...
public void mouseClicked(MouseEvent mouseEvent) {
// birsey yapma
}

public void mouseEntered(MouseEvent mouseEvent) {
// birsey yapma
}

public void mouseExited(MouseEvent mouseEvent) {
// birsey yapma
}

public void mousePressed(MouseEvent mouseEvent) {
System.out.println("Basildim: " + mouseEvent);
}

public void mouseReleased(MouseEvent mouseEvent) {
// birsey yapma
}
...
addMouseListener(this);
...
}


Elbette, yüksek seviyeli sınıfınızın arabirimin kendisini gerçekleştirmesini sağlamak zorunda değilsiniz. Bu dinleyiciyi anonim veya iç sınıf olarak yaratmanıza karar vermek için iyi bir örnek olabilir.

Eğer kullanıcı arayüzü yaratmak için bir IDE (mesela Eclipse) kullanıyorsanız, IDE arabirim çatısını sizin için oluşturur. Sizin sadece ilgili arabirim metodlarının içerisine iş mantığı kodlarını yazmanız yetecektir. Bir IDE yardımıyla büyük arayüzler gerçekleştirme işiniz oldukça kolay olacaktır.

Bu konuda daha ayrıntılı bilgiye Java tutorial içerisindeki How to Write a Mouse Listener dersinden erişebilirsiniz.

Adaptörler sadece fare dinlemeyle kısıtlı değildir. Ancak, MouseListener içerisinde çok fazla metod olması nedeniyle MouseAdapter çok kullanılan bir örnektir. WindowListener arabirimi de bir başka büyük örnektir ve WindowAdapter sınıf adaptörüne sahiptir.

Bağlantılar:
Metnin İngilizce'si (Bazı yerlerde kendimden eklemeler bulabilirsiniz):Listeners vs Adapters
Kaynak kod biçimlendirme: Java2Html converter

1 yorum:

Fatmanur dedi ki...

Çok güzel açık ve net şekilde anlatılmış teşekkürler.