Das "I"-Prefix bei Interfaces



  • in C++ ja, da man dort immer das header benötigt!

    Aber auch eine Header-Datei ist letztendlich auch nur "leichtgewichtig" (was auch immer das damit zutun hat, aber ich benutze mal eure Begrifflichkeit). Denn in der Header steht nur die Beschreibung der Funktionssignaturen und welche Klasse es gibt. Die Implementierung ist doch auch (wie in anderen Sprachen) in einer binären Library "versteckt". Nimm mal mein obiges Beispiel: es ist nur eine Beschreibung, wie Interface A aussieht. Mehr nicht. Damit habe ich eine Abstraktion auf seeehr starker weise.



  • mathik schrieb:

    df23sdg schrieb:

    DEvent schrieb:

    Aber du siehst doch gar nicht ob der Typ jetzt "schwergewichtig" oder "leichtg." ist. Du siehst doch in beiden Fällen nur die public-Methoden. [...] Na gut, aber was bringt es mir zu wissen ob Iterable und Comparable Interfaces sind? Eigentlich doch nichts.

    Die transitiven Abhängigkeiten, die eine konkrete Implementierung zwangsweise mit sich zieht, sind jene, die den Typ "schwergewichtig" machen, nicht die öffentliche Schnittstelle. Insofern macht es sehr wohl einen Unterschied ob es sich beim Typ um ein Interface oder eine Klasse handelt.

    in C++ ja, da man dort immer das header benötigt! das I-prefix verwendet dort aber zum glück kaum einer, außer bei MS-sachen wie COM.

    hier gehts aber viel mehr um java und .net.
    und dort siehst du die abhängigkeiten nicht, sofern du nur den zwischencode und die dazugehörige doku hast (mehr braucht man auch nicht bei auslieferung einer komponente). somit es in java oder .net völlig egal ob es eine klasse oder interface ist. du siehst in beiden fällen nur die öffentlichen methoden.

    Die öffentliche Schnittstelle ist doch für transitive Abhängigkeiten (fast vollkommen) egal. Abhängigkeiten lassen sich durch Kapselung nicht auflösen, nur durch Abstraktion. Folgendes Beispiel soll das verdeutlichen:

    public interface HttpServer {
    
        public void start() throws IOException;
    
        public void stop() throws IOException;
    
    }
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    public class Jdk6HttpServer implements HttpServer {
    
        private static final Log log = LogFactory.getLog(HttpServer.class);
    
        /** JDK6 HttpServer, an den sämtliche Anfragen delegiert werden */
        private com.sun.net.httpserver.HttpServer httpServer;
    
        public void start() throws IOException {
            // ...
        }
    
        public void stop() throws IOException {
            // ...
        }
    
    }
    

    Auch wenn die öffentliche Schnittstelle der Klasse Jdk6HttpServer nicht darauf schließen lässt bringt diese Klasse immense (zugegebenermaßen bewusst inszenierte) Abhängigkeiten mit sich, nämlich Java 6 und commons-logging. Die Schnittstelle hingegen hängt gerade mal von der Klasse IOException ab. Natürlich würde in diesem Beispiel kaum jemand kein Interface verwenden, aber auch bei sonstigen alltäglicheren Klassen enstehen schnell Abhängigkeiten. In diesem Beispiel wirken sich die Abhängigkeiten natürlich extrem aus, nachdem man ganze Bibliotheken benötigt um diese zu erfüllen, aber es können auch schon Abhängigkeiten zu anderen Modulen des Projekts unerwünscht sein. Ansätze wie Dependency Injection feiern derzeit nicht umsonst Hochkonjunktur.



  • dsf3246dsf schrieb:

    Die öffentliche Schnittstelle ist doch für transitive Abhängigkeiten (fast vollkommen) egal. Abhängigkeiten lassen sich durch Kapselung nicht auflösen, nur durch Abstraktion. Folgendes Beispiel soll das verdeutlichen:

    public interface HttpServer {
    
        public void start() throws IOException;
    
        public void stop() throws IOException;
    
    }
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    public class Jdk6HttpServer implements HttpServer {
    
        private static final Log log = LogFactory.getLog(HttpServer.class);
    
        /** JDK6 HttpServer, an den sämtliche Anfragen delegiert werden */
        private com.sun.net.httpserver.HttpServer httpServer;
    
        public void start() throws IOException {
            // ...
        }
    
        public void stop() throws IOException {
            // ...
        }
    
    }
    

    Auch wenn die öffentliche Schnittstelle der Klasse Jdk6HttpServer nicht darauf schließen lässt bringt diese Klasse immense (zugegebenermaßen bewusst inszenierte) Abhängigkeiten mit sich, nämlich Java 6 und commons-logging. Die Schnittstelle hingegen hängt gerade mal von der Klasse IOException ab. Natürlich würde in diesem Beispiel kaum jemand kein Interface verwenden, aber auch bei sonstigen alltäglicheren Klassen enstehen schnell Abhängigkeiten. In diesem Beispiel wirken sich die Abhängigkeiten natürlich extrem aus, nachdem man ganze Bibliotheken benötigt um diese zu erfüllen, aber es können auch schon Abhängigkeiten zu anderen Modulen des Projekts unerwünscht sein. Ansätze wie Dependency Injection feiern derzeit nicht umsonst Hochkonjunktur.

    das ist in der tat ein beispiel, wo man sowieso ein interface verwenden würde.
    aber auch bei diesem beispiel würde man in den IDE nur "start" und "stop" sehen. dass dort abhängigkeiten zu log4j und anderen bibliotheken existieren siehst du in der IDE nicht. wie gesagt, in sehr vielen fällen ist eine trennung von interface und klasse sinnvoll, insbesondere bei typen aus der analyse. irgendwelche "helper-klassen" aufzuteilen halte ich nicht für sinnvoll.

    BTW: eigentlich habe ich mich auch nicht darüber beschwert, dass zu viele interfaces verwendet werden. ich habe mich nur über das I-prefix beschwert 🙂



  • Artchi schrieb:

    Ja, wenn die Klasse eine Implementierung hat, haste Recht. Aber Klassen können auch pure virtual sein.

    class A
    {
    public:
       virtual void foo() = 0;
    };
    

    [...]

    Aber auch eine Header-Datei ist letztendlich auch nur "leichtgewichtig" (was auch immer das damit zutun hat, aber ich benutze mal eure Begrifflichkeit). Denn in der Header steht nur die Beschreibung der Funktionssignaturen und welche Klasse es gibt. Die Implementierung ist doch auch (wie in anderen Sprachen) in einer binären Library "versteckt". Nimm mal mein obiges Beispiel: es ist nur eine Beschreibung, wie Interface A aussieht. Mehr nicht. Damit habe ich eine Abstraktion auf seeehr starker weise.

    Dein Interface/rein virtuelle Klasse/was auch immer mit dem Namen A ist doch nur so lange "leicht" wie du es so lässt. Aber was machst du jetzt mit der Implementierung?
    Ich dachte man hätte sich im Thread darauf geeinigt, dass man in C++ das I davor schreibt damit man für die Implementierungsklasse den richtigen Namen benutzen kann. Also bei deinem Beispiel ein IA für die öffentliche Schnittstelle die leicht ist und keine abhängigkeiten hat und A welche die Implementierung beinhaltet.



  • mathik schrieb:

    rapso schrieb:

    mathik schrieb:

    interfaces sind ja auch schön und gut und man verwendet besser mehr von denen als zu wenig. für jede klasse ein interface zu erzeugen, auch wenn diese nur innerhalb der komponente verwendet wird und sonst keiner kennt, finde ich einfach übertrieben und verkompliziert einfach unnötig den code.

    fuer jede fuer die es moeglicherweise alternativ implementierungen geben kann wird es gemacht, das ist auch sinnig.

    d.h. nun doch nicht für jede klasse!? 🙂

    das hab ich nie gesagt. lies nochmal.

    solche begründungen, wie "weil es in zukunft vielleicht benötigt wird" finde ich jedoch gefährlich. bei agiler entwicklung soll man ja möglichst alles so einfach wie möglich halten und bei bedarf refaktorisieren. man sollte also nicht zu weit in die zukunft schauen.

    mit der zeit lernt man planvoll zu arbeiten, sodass die wenigen griffe weniger zeit beantspruchen als spaeteres refractoring, ansonsten laesst man es.



  • Marc++us schrieb:

    Das tun die Leute mit dem "I"-Prefix doch auch. 😉

    die tun das wie ich? 😕



  • lolz schrieb:

    Dein Interface/rein virtuelle Klasse/was auch immer mit dem Namen A ist doch nur so lange "leicht" wie du es so lässt. Aber was machst du jetzt mit der Implementierung?

    Welche Implementierung??? Es ist ein Iterface! Nichts mit Implementierung. Kannst Du dir nicht vorstellen, das A einfach _so_ bleibt, wie es da steht? 😮 🙄

    Wenn dann macht jemand andere sowas:

    // Header von Interface A.
    class A
    {
    public:
       virtual void foo() = 0;
    };
    

    Der bleibt unangetastet.
    Will ich A implementieren, mach ich das:

    // Header von DefaultA, einer Interface-Impl. von A.
    
    #include "A.hpp"
    
    class DefaultA : public A
    {
      public:
         void foo()
         {
              //irgendwas...
         }
    };
    

    Und? A ist immer noch ein Interface. Da wird nichts dran gedreht. Und jemand anderes macht das, wenn er mit A arbeiten will:

    #include <A.hpp>
    
    // keine Abhängigkeit zum komplexen DefaultA!!!
    // Nur Interface A wird benutzt.
    
    class X
    {
      public:
          // Parameter: Interface A
          void bar( A &a )
          {
              a.foo(); // foo vom Interface A
          }
    };
    

    Natürlich muß irgendwo im Projekt irgendwie mal DefaultA instaziert werden - klar. Aber ich kann immer A benutzen. Das ist in jeder OO-Sprache so.

    So schwer vorzustellen, das obwohl es nicht interface A {}; sondern class A {}; heißt, nicht implementiert werden muß? "Namen sind Schall und Rauch!". Genau das gleich mit class und interface. Der Effekt ist entscheidend.



  • Artchi, ich glaub wir haben gerade aneinander vorbei geredet. Ich sprach von einer Interface Klasse für eine konkrete Klasse. Z.B. IList für die Klasse List.



  • Versteh trotzdem nicht was du sagen willst.



  • Artchi schrieb:

    lolz schrieb:

    Dein Interface/rein virtuelle Klasse/was auch immer mit dem Namen A ist doch nur so lange "leicht" wie du es so lässt. Aber was machst du jetzt mit der Implementierung?

    Welche Implementierung??? Es ist ein Iterface! Nichts mit Implementierung. Kannst Du dir nicht vorstellen, das A einfach _so_ bleibt, wie es da steht? 😮 🙄

    Wenn dann macht jemand andere sowas:

    // Header von Interface A.
    class A
    {
    public:
       virtual void foo() = 0;
    };
    

    Der bleibt unangetastet.
    Will ich A implementieren, mach ich das:

    // Header von DefaultA, einer Interface-Impl. von A.
    
    #include "A.hpp"
    
    class DefaultA : public A
    {
      public:
         void foo()
         {
              //irgendwas...
         }
    };
    

    Und? A ist immer noch ein Interface. Da wird nichts dran gedreht. Und jemand anderes macht das, wenn er mit A arbeiten will:

    #include <A.hpp>
    
    // keine Abhängigkeit zum komplexen DefaultA!!!
    // Nur Interface A wird benutzt.
    
    class X
    {
      public:
          // Parameter: Interface A
          void bar( A &a )
          {
              a.foo(); // foo vom Interface A
          }
    };
    

    Natürlich muß irgendwo im Projekt irgendwie mal DefaultA instaziert werden - klar. Aber ich kann immer A benutzen. Das ist in jeder OO-Sprache so.

    So schwer vorzustellen, das obwohl es nicht interface A {}; sondern class A {}; heißt, nicht implementiert werden muß? "Namen sind Schall und Rauch!". Genau das gleich mit class und interface. Der Effekt ist entscheidend.

    public interface A {
        public void foo();
        private B b = new B();
    }
    
    class A {
        public:
            A() {
                b = new B;
            }
    
            virtual void foo() = 0;
    
        private:
            B* b;
    };
    


  • lolz schrieb:

    Ich dachte man hätte sich im Thread darauf geeinigt, dass man in C++ das I davor schreibt damit man für die Implementierungsklasse den richtigen Namen benutzen kann. Also bei deinem Beispiel ein IA für die öffentliche Schnittstelle die leicht ist und keine abhängigkeiten hat und A welche die Implementierung beinhaltet.

    wer hat sich darauf geeinigt? bisher habe ich den anschein, dass es die meisten hier unsinnig finden. mir wäre lieber wenn man besser für implementierungsklassen ein präfix schreiben würde, als beim interface, weil man ja auf schnittstellen programmiert. oder noch besser: gar keine prefixe.



  • Also, wie man sich um ein I den Kopf zerbrechen kann, verstehe ich nicht. Es ist eine Konvention, an die man sich halten kann oder nicht. Ist genau das gleiche, wie wenn jemand sagt, er schreibt alle Methodennamen am Anfang groß. Und? Ich mache es nicht. Ich schreibe alles klein, selbst Klassennamen.

    Auf Arbeit aber schreiben wir die Interface, die in der Eclipse-Platform benutzt werden, am Anfang mit einem I. Warum? Weil jemand aus dem Team sich gerne an die Eclipse-Convention halten wollte. Naja, wurde dann halt so im Team beschlossen, und gut ist. Hätten aber auch alle sagen können "Nö, haben wir bisher auch nicht gemacht. Bin nicht dafür!".



  • Irgendwie verstehe ich nicht worauf der "unterschied ist ja .." hinaus will. Will er damit sagen, dass wenn man Attribute in Interfaces zulässt, sie das selbe sind wie Klassen? - Das will ja wohl keiner bestreiten.

    Aber sowas ist nunmal in einem Interface nicht erlaubt, aus einem gutem Grund,

    public interface A {
        public void foo();
        private B b = new B();
    }
    

    und das ist kein Interface mehr sondern eine abstrakte Klasse.

    class A {
        public:
            A() {
                b = new B;
            }
    
            virtual void foo() = 0;
    
        private:
            B* b;
    };
    

    Imho waren die Entwickler einfach zu faul sich Namen für Interfaces auszudenken, so habe sie einfach den Präfix I genommen. Man muss schon reichlich kreativ sein um für die Klasse Path ein Interface-Namen auszudenken. 😃



  • Was spricht eigentlich gegen? :

    //header
    class Path{
    public:
      void foo();
      // Methoden
    private:
      // Daten
    };
    
    // CPP Datei
    void Path::foo(){
      //...
    }
    

    oder wenn Kompilezeiten oder strikte Binärkompatibilität ein Thema sind dann:

    //header
    class Path{
    public:
      void foo();
      // Methoden
    private:
      struct Impl;
      Impl*m_impl;
    };
    
    // CPP Datei
    struct Path::Impl{
      //Daten
    };
    void Path::foo(){
      m_impl->...
    }
    


  • DEvent schrieb:

    Irgendwie verstehe ich nicht worauf der "unterschied ist ja .." hinaus will. Will er damit sagen, dass wenn man Attribute in Interfaces zulässt, sie das selbe sind wie Klassen? - Das will ja wohl keiner bestreiten.

    Aber sowas ist nunmal in einem Interface nicht erlaubt, aus einem gutem Grund,

    public interface A {
        public void foo();
        private B b = new B();
    }
    

    und das ist kein Interface mehr sondern eine abstrakte Klasse.

    class A {
        public:
            A() {
                b = new B;
            }
    
            virtual void foo() = 0;
    
        private:
            B* b;
    };
    

    Langsam komme ich mir echt veräppelt vor, wenn ich weiter argumentiere bzw. begründe. Das Problem beim zweiten Konstrukt ist ja, dass es nie ein Interface war sondern immer nur eine abstrakte Klasse, zwar hatte sie keine Felder, aber das allein macht es meiner Ansicht nach nicht zum Interface. Auch wenn man natürlich sinngemäß etwas ähnliches erreichen kann, ist es nicht dasselbe. Ich hoffe, dass ich auf dieses Thema nicht näher eingehen muss, denn sonst kommen wir vom Hundertsten ins Tausendste.

    Ben04 schrieb:

    Was spricht eigentlich gegen? :

    //header
    class Path{
    public:
      void foo();
      // Methoden
    private:
      // Daten
    };
    
    // CPP Datei
    void Path::foo(){
      //...
    }
    

    oder wenn Kompilezeiten oder strikte Binärkompatibilität ein Thema sind dann:

    //header
    class Path{
    public:
      void foo();
      // Methoden
    private:
      struct Impl;
      Impl*m_impl;
    };
    
    // CPP Datei
    struct Path::Impl{
      //Daten
    };
    void Path::foo(){
      m_impl->...
    }
    

    Mit dem Handle/Body Idiom löst du erstens nur ein C++ spezifisches Problem und sorgst dabei nur dafür, dass man die Member einer Klasse verändern kann, ohne gleich das ganze Projekt neu übersetzen zu müssen. Das sind Abhängigkeiten auf einer ganz anderen Ebene, die es so in anderen Sprachen überhaupt nicht gibt.

    Du kannst meinetwegen noch 5 Indirektionen einbauen, aber trotzdem wirst du transitive Abhängigkeiten nicht auflösen können. Um den ganzen theoretischen Erläuterungen einen praktischen Hintergrund zu geben: Erstelle ein neues Projekt und kopiere Path.hpp und Path.cpp in ein entsprechendes Include-Verzeichnis. Du wirst sehen, dass sich das Projekt nicht kompilieren lässt, weil vermutlich einige Header und Source Files fehlen, die Path.cpp includiert bzw., wenn man so will, referenziert. Diese ganze Prozedur wird sich wahrscheinlich wiederholen bis du letztendlich die gesamte Bibliothek einbinden musst und bitte tut mir den Gefallen und nervt mich jetzt nicht mit Fragen und Kommentaren ala "Ist mir doch egal ob ich die gesamte Bibliothek einbinde, dafür ist sie ja doch da.", denn darum geht es nicht. Dieses Beispiel sollte einfach nochmal auf einfache, nachvollziehbare Art klarstellen, dass Datenkapselung absolut nichts zur Abhängigkeitsauflösung beiträgt, aber das hatte ich eigentlich bereits erwähnt.

    Ich denke an dieser Stelle klinke ich mich, zugegebenermaßen bereits etwas gereizt, aus der Diskussion aus. Wer immer noch nicht nachvollziehen kann, was ich meine, soll sich selbst in einem guten Buch zu Objektorientierung schlau machen.



  • sdfsdf235sdf schrieb:

    Du kannst meinetwegen noch 5 Indirektionen einbauen, aber trotzdem wirst du transitive Abhängigkeiten nicht auflösen können.

    ein interface ist nichts anderes als eine art indirektion. und zu viel unnötige indirektion schadet mehr als es nutzt.

    zu den transitiven abhängigkeiten:

    |-----|          |-----|
    | A   | <--------| C   |      
    |-----|          |-----|
      / \
       |
       |
    |-----|
    | B   |
    |-----|
    

    C hängt von A ab. Da B eine spezialisierung von A ist, was formal gesehen nichts anders ist als "B teilmenge von A" ist somit C auch indirekt von B abhängig.

    BTW: wieso funktioniert latex nicht!? 😕



  • Langsam komme ich mir echt veräppelt vor, wenn ich weiter argumentiere bzw. begründe. Das Problem beim zweiten Konstrukt ist ja, dass es nie ein Interface war sondern immer nur eine abstrakte Klasse, zwar hatte sie keine Felder, aber das allein macht es meiner Ansicht nach nicht zum Interface. Auch wenn man natürlich sinngemäß etwas ähnliches erreichen kann, ist es nicht dasselbe. Ich hoffe, dass ich auf dieses Thema nicht näher eingehen muss, denn sonst kommen wir vom Hundertsten ins Tausendste.

    Ich glaub es ist jedem klar das man sich an die Regeln halten muss was ein Interface ausmacht, um ein Interface hinzuschreiben und keine abstrakte Klasse. Ein Interface hat nunmal keine Implementation sondern nur eine Definition der Schnittstelle. Wenn du dich daran hällst kannst du auch statt interface auch blablub schreiben, trotzdem bleibt es ein Interface.



  • mathik schrieb:

    sdfsdf235sdf schrieb:

    Du kannst meinetwegen noch 5 Indirektionen einbauen, aber trotzdem wirst du transitive Abhängigkeiten nicht auflösen können.

    ein interface ist nichts anderes als eine art indirektion. und zu viel unnötige indirektion schadet mehr als es nutzt.

    zu den transitiven abhängigkeiten:

    |-----|          |-----|
    | A   | <--------| C   |      
    |-----|          |-----|
      / \
       |
       |
    |-----|
    | B   |
    |-----|
    

    C hängt von A ab. Da B eine spezialisierung von A ist, was formal gesehen nichts anders ist als "B teilmenge von A" ist somit C auch indirekt von B abhängig.

    B, was imho mehr als A ist, ist Teilmenge von A 😕

    Entweder du hast dich da verschrieben, oder ich kann dir nicht folgen.



  • Klugscheiß0r schrieb:

    B, was imho mehr als A ist, ist Teilmenge von A 😕

    Entweder du hast dich da verschrieben, oder ich kann dir nicht folgen.

    nö. B ist nich mehr als A, sondern spezieller. A ist allgemeiner.
    Bsp.: die kategorie "Tiere" ist "größer" als die kategorie "Säugetiere".

    semantisch ist somit B eine teilmenge von A. Kannst dich ja mal mit der semantik von programmiersprachen befassen, dort wird sowas erläutert.


Anmelden zum Antworten