Dienstag, 7. September 2010

Open Closed Principle Idiom

Das Open Closed Principle (OCP) ist von Dr. Bertrand Meyer in seinem Buch "Object-oriented Software Construction" erläutert worden. Das Prinzip besagt, dass eine Komponente offen für Erweiterungen und geschlossen für Modifikationen sein soll. Eine geschlossene Komponente besitzt eine wohldefinierte öffentliche Schnittstelle, die zwar erweitert aber keinesfalls reduziert oder nachträglich geändert werden darf.  Eine Änderung könnte zum Beispiel die Methodensignaturen der öffentlichen Schnittstelle betreffen, die nach der Publizierung nicht mehr stattfinden darf. Geschlossen bezieht sich bei dieser Aussage auf die Abnahme, Versionierung und Freigabe einer zu publizierenden Schnittstelle.

Offen für Änderungen muss eine Komponente sein, weil nicht alle Aspekte der Komponente bei der Veröffentlichung betrachtet werden können. Mit zunehmender Verwendung der Komponente werden neue Anforderungen gestellt, die die Weiterentwicklung der Komponente prägen.

Das Open Closed Principle ist klassischerweise durch Vererbung lösbar. Bei der Vererbung werden im einfachen Fall nicht nur Methoden überschrieben, sondern vorausschauend mit abstrakten Methoden oder Hooks, die Verarbeitung in bestehenden Methoden nachträglich erweitert. Eine praktikable Lösungsmöglichkeit für die Anforderungen des Prinzips, ist das folgende Idiom, bei dem eine öffentliche und eine private Java Schnittstelle verwendet wird.

In der öffentlichen Schnittstelle sind die Methoden für die Verwender deklariert. Die öffentliche Schnittstelle kann in Folgeversionen der Software erweitert, aber nicht mehr reduziert bzw. geändert werden. In der privaten Schnittstelle hingegen werden die Methoden deklariert, die noch nicht öffentlich bekannt sein sollen. Diese Schnittstelle ist mit Paketsichtbarkeit belegt und nicht von außerhalb des Java Packages nutzbar. Die Komponente verwendet einen privaten Konstruktor und ist deshalb nur über eine Factory Methode instanziierbar, die die öffentliche Schnittstelle als Rückgabewert liefert. Durch diese Vorgehensweise ist die Komponente verriegelt und nur über die öffentliche Schnittstelle ansprechbar.

Öffentliche Schnittstelle:

public interface PublicInterface {

    String publicMethod(String strArg);
    Integer publicMethod(Integer intArg);
    Long publicMethod(Long longArg);
}

Private Schnittstelle:

interface PrivateInterface {
   
    void privateMethod(String strArg);
    void privateMethod(Integer intArg);
    void privateMethod(Long longArg);
}

Bean-Implementierung:

public class OcpBean implements PublicInterface, PrivateInterface {

    public static PublicInterface newInstance() {
   
        return new OcpBean();
    }
   
    private OcpBean() {/* prevents instantiation*/}
       
    @Override
    public String publicMethod(String strArg) {
       
        return strArg;
    }

    @Override
    public Integer publicMethod(Integer intArg) {
       
        return intArg;
    }

    @Override
    public Long publicMethod(Long longArg) {
       
        return longArg;
    }

    @Override
    public void privateMethod(String strArg) {}

    @Override
    public void privateMethod(Integer intArg) {}

    @Override
    public void privateMethod(Long longArg) {}
}

Bean-Implementierung Testfall:

import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;

public class OcpBeanTest {

    private PublicInterface ocpBean;
   
    @Before
    public void setUp() {
       
        ocpBean = OcpBean.newInstance();       
    }
   
    @Test
    public void testPublicInterface() {
           
        assertEquals("test", ocpBean.publicMethod("test"));
        assertEquals(new Integer(1), ocpBean.publicMethod(1));
        assertEquals(new Long(1l), ocpBean.publicMethod(1l));
    }
}


Der Rechtshinweis des Java Blog für Clean Code Developer ist bei der Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu beachten.