menu

Showing posts with label Design Patterns. Show all posts
Showing posts with label Design Patterns. Show all posts

Wednesday, October 15, 2014

Reflective Visitor Design Pattern

Reflective Visitor Design PatternIntroduction:

     Today we talk about Visitor design pattern which is one of the behavioral design patterns.We point out which basic problem can be solved by this pattern and see how it can be improved to use with reflection for a more elastic solution.
The visitor design pattern is used when there are different value typed objects in a collection and we need similar operation to do on these objects. The UML graphic can be seen in Figure 1.


Reflective Visitor Design Pattern

                                                                    Figure 1

Implementation:

Lets first work on a collection that include different objects without visitor pattern.

        List heterogenList = new ArrayList();

        heterogenList.add("A");
        heterogenList.add(1);
        heterogenList.add(Calendar.getInstance());

        for (Iterator iterator = heterogenList.iterator(); iterator.hasNext();) {
            Object heterogenElement = (Object) iterator.next();
            if (heterogenElement instanceof String) {
                String myString = (String) heterogenElement;
                System.out.println("myString:" + myString);
                //do sth with the value
            }
            else if (heterogenElement instanceof Integer) {
                Integer myInteger = (Integer) heterogenElement;
                System.out.println("myInteger:" + myInteger);
                //do sth with the value
            }
            else if (heterogenElement instanceof Calendar) {
                Calendar myCalendar = (Calendar) heterogenElement;
                System.out.println("myCalendar:" + myCalendar);
                //do sth with the value
            }
        }

As can be seen to read the different objects from the collection we need to use instanceof keyword in the loop.That means if we add a different typed object to our collection, we need to add a new if statement to our source code. If we are continuosly using if keyword in our code, it shows that we do some mistake and we have to revise our design.Instead of if, we can use abstraction to obtain more elastic, tracable and maintainable software.In addition, changing an already tested code after each new type added to our collection is against the open-closed design principle and is a situation that we have to escape. In this case the visitor design pattern comes to rescue.
Lets first define the interfaces and classes that we need.

//Visitor interface
public interface Visitor {
   void visit(WrappedString wString);
   void visit(WrappedCalendar wCalendar);
   void visit(WrappedInteger wInteger);
}
//The interface of the object that will be visited
public interface Element {
    void accept(Visitor visitor);
}
//The objects that will be used in the list to visit
public class WrappedString implements Element {
    private String name;
    private String wString;
    public WrappedString(String name, String wString) {
        this.name = name;
        this.wString = wString;
    }
    public String getName() {
        return name;
    }
    public String getwString() {
        return wString;
    }
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

public class WrappedInteger implements Element {
    private String name;
    private int wInt;
    public WrappedInteger(String name, int wInt) {
        this.name = name;
        this.wInt = wInt;
    }
    public String getName() {
        return name;
    }
    public int getwInt() {
        return wInt;
    }
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

import java.util.Calendar;

public class WrappedCalendar implements Element{
    private Calendar wCalendar;
    private String name;
    public WrappedCalendar(String name, Calendar wCalendar) {
        this.name = name;
        this.wCalendar = wCalendar;
    }

    public Calendar getWCalendar() {
        return wCalendar;
    }

    public String getName() {
        return name;
    }
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

//Concrete Visitor
public class ConcreteVisitor implements Visitor {
    @Override
    public void visit(WrappedString wString) {
        System.out.println(wString.getName());
        // do sth
    }
    @Override
    public void visit(WrappedCalendar wCalendar) {
        System.out.println(wCalendar.getName() + "-" + wCalendar.getWCalendar().getTimeInMillis());
        // do sth
    }
    @Override
    public void visit(WrappedInteger wInteger) {
        System.out.println(wInteger.getName() + "-" + wInteger.getwInt());
        // do sth
    }
}

Now lets rewrite the previous example with visitor design pattern.

       List heterogenList = new ArrayList();

        heterogenList.add(new WrappedString("wString","A"));
        heterogenList.add(new WrappedInteger("wInteger", 1));
        heterogenList.add(new WrappedCalendar("wCalendar", Calendar.getInstance()));

         Visitor visitor = new ConcreteVisitor();
         //Visitor visitor = new AnotherConcreteVisitor();
         for (Iterator iterator = heterogenList.iterator(); iterator.hasNext();) {
            Element element = (Element) iterator.next();
            element.accept(visitor);
        }

Now we get rid of the instanceof keyword.Since all the objects in the collection are implements the Element interface , the only thing we have to do is to read the Element interface from the list and call the accept method with a visitor object.Each of the objects in the list, will pass itself to the visit method of the visitor in its accept method, and in the visitor object the business logic related with the object will be done.The important point here is, the ability to add new visitors with new business logics on the objects without affecting the client code. 

Reflective Visitor Design Pattern Implementation:

The visitor design pattern behaves quite elastic adding new visitors, however when we want to add new object to the collection, it requires to change the Visitor interface.In java and all of other object oriented languages, changing an interface after its published is one of the most problematic thing, because when the interface is changed, all of the classes that implement that interface has to be changed.To solve this issue, we can build the visitor interface by reflection.
The reflected interface will be like below.

//Reflective Visitor interface
public interface ReflectiveVisitor {
   void visit(Object o);
}
//Reflective Element
public interface ReflectiveElement {
    void accept(ReflectiveVisitor visitor);
}
//The new object to visit (We change WrappedString class as to implement //ReflectiveElement 
public class WrappedDouble implements ReflectiveElement {
    private String name;
    private double wDouble;
    public WrappedDouble(String name, double wDouble) {
        this.name = name;
        this.wDouble = wDouble;
    }
    public String getName() {
        return name;
    }
    public double getWDouble() {
        return wDouble;
    }
    @Override
    public void accept(ReflectiveVisitor visitor) {
        visitor.visit(this);
    }
}
public class WrappedString implements ReflectiveElement {
    private String name;
    private String wString;
    public WrappedString(String name, String wString) {
        this.name = name;
        this.wString = wString;
    }
    public String getName() {
        return name;
    }
    public String getwString() {
        return wString;
    }
    @Override
    public void accept(ReflectiveVisitor visitor) {
       visitor.visit(this);
    }
    @Override
    public String toString() {
        return name + "-" + wString;
    }
}
//Reflective Concrete Visitor
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class AnotherConcreteVisitor implements ReflectiveVisitor {
    @Override
    public void visit(Object o) {
        try {
            Method visitMethod = this.getClass().getMethod("visit", new Class[] { o.getClass() });
            if (visitMethod == null) {
                defaultVisit(o);
            } else {
                visitMethod.invoke(this, new Object[] { o });
            }
        } catch (NoSuchMethodException e) {
            this.defaultVisit(o);
        } catch (InvocationTargetException e) {
            this.defaultVisit(o);
        } catch (IllegalAccessException e) {
            this.defaultVisit(o);
        }
    }
    public void defaultVisit(Object o) {
        System.out.println(o.toString());
    }
    public void visit(WrappedDouble wDouble){
        System.out.println(wDouble.getName() + "-" + wDouble.getWDouble());
        // do sth
    }
}

Lets rewrite the previous example again,

        List heterogenList = new ArrayList();

        heterogenList.add(new WrappedString("wString","A"));//default visit method
        heterogenList.add(new WrappedDouble("wDouble", 1.0));

         ReflectiveVisitor visitor = new AnotherConcreteVisitor();
         for (Iterator iterator = heterogenList.iterator(); iterator.hasNext();) {
            ReflectiveElement reflectiveElement =  (ReflectiveElement) iterator.next();
            reflectiveElement.accept(visitor);
        } 

In this approach, all the objects in our collection are calling the generic visit method of visitor class.
This generic visit method takes Object as parameter, and depending on the type of parameter, calls the related visit method.The important point is, the visitor class has to define visit(data:DataType) method for all data types that it wants to play.If the related visit method cannot be found, there can be a defaultVisit method to do a generic default job.

Conclusion:

     With reflective visitor design pattern, the elasticity of adding new visitors is to be improved to include adding new objects to the collection.When we need to add new object to the already defined visitor, all we need is to add a visit method for the new objects to the visitor class.We don't need to change the ReflectiveInterface interface and other visitors.
However this elasticity comes with a performance loss.Since the method that will be called by reflection determined at runtime dynamically, some JVM optimization cannot be done and so reflected parts can cause very bad performance especially at hot spots.
When we execute the below example in my machine using reflection and without using reflection with 1000000 objects we get 50 times slower results.In addition when we increase the number of objects to 10000000 we get Out of Memory error.
The reason may because of the heap space or perm gen space depending on the situation.If you use heavy reflection then it may the JVM's mechanism called "inflation" to run the reflected method faster causes the Out of Memory error with perm gen.Normally, up to a defined calling number, creating an instance of MethodAccessor which is an inner class defined in Method class, each invoke call send to the MethodAccessor's invoke method by JNI (Java Native Interface). Since the JNI method call is very slow, after some method calls, JVM creates a new class at runtime.This class also include invoke method , the instance of it will be used for the invoke calls instead of JNI calls. This technique is called as inflation, and the performance problem of reflection minimized using this.However even with this improvement we still have 50 times slower results.
The classes and the references of instances of this class are kept in permanent generation part of heap.(In our case only one instance of the class created) (Permanent generation hold  class and method definitions, constant pool information (taken from class file), references of object arrays and type arrays associated with the class, classes created at runtime by JVM by inflation, references of static variables, and the information used by JIT for optimization.)
When there is huge amount of reflected method calls with different objects, the classes created for inflation by JVM may cause to fill the permanent generation causing Out of Memory.
In this case you have to reserve enough memory for permanent generation to be used by JVM.

References and Resources:

Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides. Desing Patterns -  Elements of Reusable Object-Oriented Software

http://anshuiitk.blogspot.com/2010/11/excessive-full-garbage-collection.html

You can download the source code from here.

Monday, January 13, 2014

Reflective Ziyaretçi (Visitor) Tasarım Örüntüsü

Reflective Ziyaretçi (Visitor) Tasarım ÖrüntüsüGiriş:

     Bugün davranış temelli tasarım örüntülerinden (behavioral design patterns) Ziyaretçi (Visitor) tasarım örüntünün hangi temel soruna çözüm olduğunu ve esnek yapıda bir Ziyaretçi tasarımının reflection ile nasıl gerçekleştirilebileceğini göreceğiz.
Ziyaretçi tasarım örüntüsü farklı veri tipindeki nesnelerin bir collection içerisinde kullanılması ve nesneler üzerinde benzer işlemlerin yapılması gerektiğinde kullanılır. Ziyaretçi tasarım örüntüsünün UML çizimi Şekil 1 de gösterilmiştir.

Reflective Ziyaretçi (Visitor) Tasarım Örüntüsü

                                                                    Şekil 1

Ziyaretçi Tasarım Örüntüsü Gerçekleştirimi:

Öncelikle ziyaretçi kullanmadan farklı nesneler içeren bir collection üzerinde çalışalım.

        List heterogenList = new ArrayList();
      
        heterogenList.add("A");
        heterogenList.add(1);
        heterogenList.add(Calendar.getInstance());
      
        for (Iterator iterator = heterogenList.iterator(); iterator.hasNext();) {
            Object heterogenElement = (Object) iterator.next();
            if (heterogenElement instanceof String) {
                String myString = (String) heterogenElement;   
                System.out.println("myString:" + myString);
                //do sth with the value
            }
            else if (heterogenElement instanceof Integer) {
                Integer myInteger = (Integer) heterogenElement;   
                System.out.println("myInteger:" + myInteger);
                //do sth with the value
            }
            else if (heterogenElement instanceof Calendar) {
                Calendar myCalendar = (Calendar) heterogenElement;   
                System.out.println("myCalendar:" + myCalendar);
                //do sth with the value
            }          
        }

Görüldüğü gibi farklı nesneleri collection içerisinden okumak için döngümüz içerisinde instanceof keyword'ünü kullanmamız gerekiyor. Bu da demek oluyor ki listemize farklı tipte bir liste eklendiğinde hali hazırda çalışan kodumuzu değiştirmek ve yeni bir if eklemek zorunda kalacağız.
Eğer kodumuzda sürekli if kullanıyorsak bir yerlerde yanlış yapıyoruz demektir ve tasarımı gözden geçirmek gerekmektedir. If kullanımı yerine soyutlama (abstraction) yaparak daha esnek, daha izlenebilir ve devam ettirilebilir bir yazılım elde edebiliriz. Ayrıca hali hazırda test edilmiş bir kodu her yeni veri tipinde değiştirmek zorunda kalmamız Open Closed Tasarım Örüntüsüne aykırı olup kaçınılması gereken bir durumdur. Bu durumda yardımımıza Ziyaretçi tasarım örüntüsü gelmektedir.
Öncelikle ihtiyacımız olan arayüz ve sınıfları tanımlayalım.
//Ziyaretçi arayüzü
public interface Visitor {
   void visit(WrappedString wString);
   void visit(WrappedCalendar wCalendar);
   void visit(WrappedInteger wInteger);
}
//Ziyaret edilecek nesne arayüzü
public interface Element {
    void accept(Visitor visitor);
}
//Listemizde kullanacağımız ziyaret edilecek nesneler
public class WrappedString implements Element {
    private String name;
    private String wString;
    public WrappedString(String name, String wString) {
        this.name = name;
        this.wString = wString;
    }
    public String getName() {
        return name;
    }
    public String getwString() {
        return wString;
    }
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

public class WrappedInteger implements Element {
    private String name;
    private int wInt;
    public WrappedInteger(String name, int wInt) {
        this.name = name;
        this.wInt = wInt;
    }
    public String getName() {
        return name;
    }
    public int getwInt() {
        return wInt;
    }
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);    
    } 
}

import java.util.Calendar;

public class WrappedCalendar implements Element{
    private Calendar wCalendar;
    private String name;
    public WrappedCalendar(String name, Calendar wCalendar) {
        this.name = name;
        this.wCalendar = wCalendar;
    }

    public Calendar getWCalendar() {
        return wCalendar;
    }

    public String getName() {
        return name;
    }
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);    
    }
}

//Concrete Ziyaretçi
public class ConcreteVisitor implements Visitor {
    @Override
    public void visit(WrappedString wString) {
        System.out.println(wString.getName());
        // do sth
    }
    @Override
    public void visit(WrappedCalendar wCalendar) {
        System.out.println(wCalendar.getName() + "-" + wCalendar.getWCalendar().getTimeInMillis());
        // do sth
    }
    @Override
    public void visit(WrappedInteger wInteger) {
        System.out.println(wInteger.getName() + "-" + wInteger.getwInt());
        // do sth
    }
}

Şimdi daha önceki liste örneğimizin kodunu Ziyaretçi tasarım örüntüsü ile tekrar yazalım
       List heterogenList = new ArrayList();
       
        heterogenList.add(new WrappedString("wString","A"));
        heterogenList.add(new WrappedInteger("wInteger", 1));
        heterogenList.add(new WrappedCalendar("wCalendar", Calendar.getInstance()));
       
         Visitor visitor = new ConcreteVisitor();
         //Visitor visitor = new AnotherConcreteVisitor();
         for (Iterator iterator = heterogenList.iterator(); iterator.hasNext();) {
            Element element = (Element) iterator.next();
            element.accept(visitor);
        }

Görüldüğü üzere instanceof keyword'ünden kurtulduk.Listedeki her bir sınıfın nesnesi Element arayüzünü gerçekleştirdiğinden tek yapmamız gereken listeden Element arayüzünü okumak ve accept metodunu bir ziyaretçi ile çağırmak.Her bir nesne kendi accept metodunda ziyaretçinin visit metoduna kendisini parametre olarak geçecek ve ziyaretçi sınıfında nesne ile ilgili yapılmak istenen iş mantığı gerçekleştirilecek.Burada önemli nokta client kodunu hiç değiştirmeden nesneler üzerinde yeni iş mantıkları geliştirecek yeni ziyaretçilerin sisteme entegre edilebiliyor oluşu.

Reflective Ziyaretçi Tasarım Örüntüsü Gerçekleştirimi:

Ziyaretçi tasarım örüntüsü yeni ziyaretçi eklemede oldukça esnek davranırken, listemize yeni bir sınıf eklemek gerektiğinde Visitor arayüzünün değiştirilmesini gerektiriyor.Java'da bir arayüzü yayınladıktan sonra değiştirmek en can sıkıcı işlerden birisidir, çünkü arayüz değiştiğinde arayüzü gerçekleştiren tüm concrete ziyaretçilerin de değiştirilmesi gerekmektedir. Bu soruna çözüm üretebilmek için Visitor arayüzünü reflection ile oluşturabiliriz. Bu durumda arayüzün yeni hali;
//Reflective Ziyaretçi arayüzü
public interface ReflectiveVisitor {
   void visit(Object o);
}
//Reflective Element
public interface ReflectiveElement {
    void accept(ReflectiveVisitor visitor);
}
//Yeni ziyaret edilecek nesne (WrappedString sınıfını da implement ReflectiveElement olarak değiştirelim)
public class WrappedDouble implements ReflectiveElement {
    private String name;
    private double wDouble;
    public WrappedDouble(String name, double wDouble) {
        this.name = name;
        this.wDouble = wDouble;
    }
    public String getName() {
        return name;
    }
    public double getWDouble() {
        return wDouble;
    }
    @Override
    public void accept(ReflectiveVisitor visitor) {
        visitor.visit(this);
    } 
}
public class WrappedString implements ReflectiveElement {
    private String name;
    private String wString;
    public WrappedString(String name, String wString) {
        this.name = name;
        this.wString = wString;
    }
    public String getName() {
        return name;
    }
    public String getwString() {
        return wString;
    }
    @Override
    public void accept(ReflectiveVisitor visitor) {
       visitor.visit(this);
    }
    @Override
    public String toString() {
        return name + "-" + wString;
    }
}
//Reflective Concrete Ziyaretçi
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class AnotherConcreteVisitor implements ReflectiveVisitor {
    @Override
    public void visit(Object o) {
        try {
            Method visitMethod = this.getClass().getMethod("visit", new Class[] { o.getClass() });
            if (visitMethod == null) {
                defaultVisit(o);
            } else {
                visitMethod.invoke(this, new Object[] { o });
            }
        } catch (NoSuchMethodException e) {
            this.defaultVisit(o);
        } catch (InvocationTargetException e) {
            this.defaultVisit(o);
        } catch (IllegalAccessException e) {
            this.defaultVisit(o);
        }
    }
    public void defaultVisit(Object o) {
        System.out.println(o.toString());
    }
    public void visit(WrappedDouble wDouble){
        System.out.println(wDouble.getName() + "-" + wDouble.getWDouble());
        // do sth
    } 
}

Daha önceki liste örneğimizin kodunu Reflective Ziyaretçi tasarım örüntüsü ile tekrar yazalım
        List heterogenList = new ArrayList();
       
        heterogenList.add(new WrappedString("wString","A"));//default visit method
        heterogenList.add(new WrappedDouble("wDouble", 1.0));
       
         ReflectiveVisitor visitor = new AnotherConcreteVisitor();
         for (Iterator iterator = heterogenList.iterator(); iterator.hasNext();) {
            ReflectiveElement reflectiveElement = 
(ReflectiveElement) iterator.next();
            reflectiveElement.accept(visitor);
        } 

Bu yaklaşımda listemizdeki tüm nesneler ziyaretçi sınıfının generic visit metodunu çağırmaktadır.Parametre olarak Object alan bu generic visit metodu, gelen parametrenin tipine göre gerçekleştirilmiş olan ilgili visit metodunu çağırmaktadır. Burada önemli olan ziyaretçi sınıfının hangi veri tipleri için işlem yapmak istiyorsa o tipler için visit(veri:VeriTipi) metodunu tanımlamasıdır.Eğer ilgili visit metodu ziyaretçi sınıfında bulunamazsa defaultVisit metodu nesnenin String gösterimini ekrana yazma ya da default olarak belirleyeceğimiz başka bir işi gerçekleştirmeyi yapacaktır.

Sonuç:

     Bu tasarımda ziyaretçi eklemedeki esneklik, üzerinde çalıştığımız listeye yeni sınıf eklendiğinde de sağlanmış oluyor.Hali hazırda kullanılan bir ziyaretçiye yeni bir sınıf eklemek istediğimizde, yeni sınıf için bir visit metodunu ziyaretçi sınıfına eklememiz ve implementasyonunu yapmamız yeterli, ReflectiveInterface arayüzünü ve diğer ziyaretçileri değiştirmemize gerek kalmamış oldu.
Ancak bu esneklik bir performans kaybı ile beraber gelmektedir.Reflection ile çağırılacak metot dinamik olarak runtime'da belirlendiğinden bazı JVM optimizasyonları yapılamamaktadır ve reflection sık çalışan kod bölümleri için çok kötü performans verebilmektedir.Reflection kullanarak ve kullanmayarak 1000000 nesne için yukarıdaki kodu çalıştırdığımızda reflection ile 50 kat yavaş sonuç aldığımızı görmekteyiz.Ayrıca 10000000 nesne için aynı kodu çalıştırdığımızda reflection temelli yaklaşım Out of Memory hatası almaktadır.
Bunun sebebi java heap space in dolmasından olabileceği gibi JVM'in reflected metodun daha performanslı çalışması için kullandığı "inflation" mekanizmasından kaynaklı olarak perm gen space den kaynaklı da olabilir. Normalde belirli bir çağırım sayısına kadar java'nın Method classının içerisindeki MethodAccessor sınıfının bir nesnesi oluşturulup her invoke çağırımı MethodAccessor sınıfının aynı isimli invoke metoduna JNI(Java Native Interface) yöntemi ile yönlendirilmektedir. JNI metot çağırımı çok yavaş olduğundan belirli miktarda metot çağırımı yapıldığında, JVM runtime'da bir sınıf oluşturur.Bu sınıfta invoke metodu içermekte olup, bu sınıftan oluşturulan instance ile artık invoke metot çağırımları JNI yerine bu sınıfın nesneninin invoke metoduna yönlendirilir. Inflation olarak adlandırılan bu mekanizma ile reflected metot çağırımında performans problemi en aza indirilmiş olur. Ancak bu durumda dahi reflected metot çağırımı ile 50 kat yavaş sonuç aldığımızı söylemiştik.
Runtime'da oluşturulan sınıflar ve her sınıf için oluşturulan bir nesnenin referansı permanent generation dediğimiz heap space'de tutulur.(Permanent generation sınıf, metot tanımlarını, constant pool bilgisini (constant pool verisi class file'dan alınır),  class ile ilişkili object dizilerinin ya da type dizilerinin referanslarını, JVM tarafından runtime'da oluşturulan inflation dan kaynaklı sınıfları, static değişkenlerin referanslarını ve optimizasyon için JIT tarafından kullanılan bilgileri tutmaktadır.) Çok fazla farklı nesne üzerinden reflected metot çağırımı yapıldığında runtime'da inflation için oluşturulan sınıflardan kaynaklı olarak permanent generation dolup Out of Memory hatasına sebep olabilmektedir. Çok fazla reflected metot çağırımı yapılacağı durumlarda permanent generation için yeterli miktarda hafıza ayrılmalıdır.

Referanslar ve Kaynaklar:

Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides. Desing Patterns -  Elements of Reusable Object-Oriented Software
http://anshuiitk.blogspot.com/2010/11/excessive-full-garbage-collection.html

Kaynak kodu buradan indirebilirsiniz.