Frage

Ich mache eine Mini-ORM für ein Java-Programm, ich schreibe ... es eine Klasse für jede Tabelle in meiner db ist, die alle aus ModelBase vererben.

ModelBase ist abstrakt und bietet eine Reihe von statischen Methoden für die Suche und Bindung von Objekten aus der Db, zum Beispiel:

public static ArrayList findAll(Class cast_to_class) {
  //build the sql query & execute it 
}

So können Sie Dinge wie ModelBase.findAll(Albums.class) tun können, um eine Liste aller Alben beibehalten zu bekommen. Mein Problem ist, dass in diesem statischen Kontext, ich brauche die entsprechende SQL-Zeichenfolge aus dem konkreten Klasse Album zu bekommen. Ich kann nicht eine statische Methode wie haben

public class Album extends ModelBase {
  public static String getSelectSQL() { return "select * from albums.....";}
}

, weil es kein Polymorphismus für statische Methoden in Java. Aber ich will nicht, eine Instanzmethode in getSelectSQL() machen Album denn dann muß ich nur eine Instanz davon erstellen eine Zeichenfolge erhalten, die im Verhalten wirklich statisch ist.

Im Moment findAll() Reflexion verwendet die entsprechende SQL für die Klasse in Frage zu bekommen:

select_sql = (String)cast_to_class.getDeclaredMethod("getSelectSql", new Class[]{} ).invoke(null, null);

Aber das ist ziemlich eklig.

So irgendwelche Ideen? Es ist ein generelles Problem, ich bin immer und immer wieder mit - die Unfähigkeit, abstrakte statische Methoden in Klassen oder Schnittstellen zu spezifizieren. Ich weiß, Warum statische Methode Polymorphismus nicht und kann nicht funktionieren, aber das macht mich nicht davon abhalten will es wieder Zeit verwenden!

Gibt es ein Muster / Konstrukt, das mir, dass konkrete Subklassen, um sicherzustellen, kann X und Y eine Klassenmethode implementieren (oder ersatzweise eine Klasse konstant!)?

War es hilfreich?

Lösung

Wenn auch, ich stimme völlig in dem Punkt der „Static ist die falsche Sache, hier zu verwenden“, verstehe ich Art von dem, was Sie versuchen, hier zu lösen. Noch Instanz Verhalten soll der Weg zu arbeiten, aber wenn Sie darauf bestehen, das ist, was ich tun würde:

Starten von Ihrem Kommentar: „Ich brauche eine Instanz davon erstellen, um eine Zeichenfolge zu erhalten, die wirklich statisch im Verhalten ist“

Es ist nicht ganz korrekt. Wenn Sie gut aussehen, ändern Sie nicht das Verhalten Ihrer Basisklasse, sondern nur die Parameter für eine Methode zu ändern. Mit anderen Worten sind Ändern Sie die Daten, nicht der Algorithmus.

Die Vererbung ist nützlicher, wenn eine neue Unterklasse will die Art und Weise eine Methode funktioniert ändern, wenn Sie nur die „Daten“ die Klasse ändern müssen nutzt wahrscheinlich ein Ansatz wie dies funktionieren würde den Trick tun.

class ModelBase {
    // Initialize the queries
    private static Map<String,String> selectMap = new HashMap<String,String>(); static {
        selectMap.put( "Album", "select field_1, field_2 from album");
        selectMap.put( "Artist", "select field_1, field_2 from artist");
        selectMap.put( "Track", "select field_1, field_2 from track");
    }

    // Finds all the objects for the specified class...
    // Note: it is better to use "List" rather than "ArrayList" I'll explain this later.
    public static List findAll(Class classToFind ) {
        String sql = getSelectSQL( classToFind );
        results = execute( sql );
        //etc...
        return ....
    }

    // Return the correct select sql..
    private static String getSelectSQL( Class classToFind ){
        String statement = tableMap.get( classToFind.getSimpleName() );
        if( statement == null ) {
            throw new IllegalArgumentException("Class " + 
                 classToFind.getSimpleName + " is not mapped");
        }
        return statement;

    }
}

Das heißt, Karte alle Anweisungen mit einer Karte. Der „offensichtlich“ nächste Schritt dazu ist die Karte von einer externen Quelle, wie eine Properties-Datei oder einem XML oder auch (warum nicht) eine Datenbanktabelle, für zusätzliche Flexibilität zu laden.

So können Sie Ihre Klasse Kunden halten können (und Ihr Selbst-) glücklich, weil Sie nicht benötigt haben „eine Instanz zu schaffen“, um die Arbeit zu tun.

// Client usage:

...
List albums = ModelBase.findAll( Album.class );

...

Ein weiterer Ansatz ist die Instanzen von hinten zu erstellen und Ihre Client-Schnittstelle intakt zu halten, während Instanzmethoden verwendet, werden die Methoden, die als „geschützt“ externen Aufruf zu vermeiden ist. Auf ähnliche Art und Weise der vorherigen Probe können Sie dies auch

// Second option, instance used under the hood.
class ModelBase {
    // Initialize the queries
    private static Map<String,ModelBase> daoMap = new HashMap<String,ModelBase>(); static {
        selectMap.put( "Album", new AlbumModel() );
        selectMap.put( "Artist", new ArtistModel());
        selectMap.put( "Track", new TrackModel());
    }

    // Finds all the objects for the specified class...
    // Note: it is better to use "List" rather than "ArrayList" I'll explain this later.
    public static List findAll(Class classToFind ) {
        String sql = getSelectSQL( classToFind );
        results = execute( sql );
        //etc...
        return ....
    }

    // Return the correct select sql..
    private static String getSelectSQL( Class classToFind ){
        ModelBase dao = tableMap.get( classToFind.getSimpleName() );
        if( statement == null ) {
            throw new IllegalArgumentException("Class " + 
                 classToFind.getSimpleName + " is not mapped");
        }
        return dao.selectSql();
    }
    // Instance class to be overrided... 
    // this is "protected" ... 
    protected abstract String selectSql();
}
class AlbumModel  extends ModelBase {
    public String selectSql(){
        return "select ... from album";
    }
}
class ArtistModel  extends ModelBase {
    public String selectSql(){
        return "select ... from artist";
    }
}
class TrackModel  extends ModelBase {
    public String selectSql(){
        return "select ... from track";
    }
}

Und Sie brauchen nicht den Client-Code zu ändern, und immer noch die Macht der Polymorphie haben.

// Client usage:

...
List albums = ModelBase.findAll( Album.class ); // Does not know , behind the scenes you use instances.

...

Ich hoffe, das hilft.

Eine letzte Anmerkung auf Liste vs. Arraylist verwenden. Es ist immer besser zu Programm auf die Schnittstelle als auf die Umsetzung, auf diese Weise machen Sie Ihren Code flexibler. Sie können eine andere Liste Implementierung verwenden, die schneller ist, oder tut etwas anderes, ohne Client-Code zu ändern.

Andere Tipps

Static ist die falsche Sache, hier zu verwenden.

Konzeptionell statisch ist falsch, weil es nur für Dienstleistungen ist, die zu einem einem tatsächlichen Objekt, physischen oder konzeptionellen nicht entsprechen. Sie haben eine Reihe von Tabellen, und jeder sollte von einem tatsächlichen Objekt im System abgebildet wurde, nicht nur eine Klasse sein. Das klingt es ein bisschen theoretisch ist, aber es hat tatsächlich Folgen, wie wir sehen werden.

Jede Tabelle ist von einer anderen Klasse, und das ist in Ordnung. Da Sie immer nur eine von jeder Tabelle haben können, zu einem der Anzahl der Instanzen jeder Klasse begrenzen (einen Flag verwenden - macht es nicht einen Singleton). Machen Sie das Programm eine Instanz der Klasse erstellen, bevor es die Tabelle zugreift.

Jetzt haben Sie eine Reihe von Vorteilen. Sie können die volle Leistung der Vererbung und überwiegenden verwenden, da Ihre Methoden sind nicht mehr statisch. Sie können mit dem Konstruktor jede Initialisierung zu tun, einschließlich SQL mit der Tabelle Zuordnung (SQL, die Ihre Methoden später verwenden können). Dies sollte vor allem Ihre Probleme weggehen, oder zumindest erhalten viel einfacher.

Es scheint, als ob es sich um zusätzliche Arbeit in mit dem Objekt und zusätzliche Speicher zu schaffen, aber es ist wirklich trivial im Vergleich zu den Vorteilen. Ein paar Byte Speicher für das Objekt wird nicht bemerkt werden, und eine Handvoll Konstruktoraufrufe wird vielleicht zehn Minuten dauern hinzuzufügen. Gegen das ist der Vorteil, dass Code alle Tabellen initialisieren muss nicht ausgeführt werden, wenn die Tabelle nicht verwendet wird (der Konstruktor sollte nicht aufgerufen werden). Sie werden feststellen, es vereinfacht die Dinge viel.

Warum nicht mit Anmerkungen? Sie fitpretty gut, was du tun. Meta-Informationen (hier eine SQL-Abfrage) zu einer Klasse hinzuzufügen,

Wie bereits angedeutet, Sie Anmerkungen verwenden könnte, oder Sie können die statischen Methoden auf die Werks Objekte bewegen:

public abstract class BaseFactory<E> {
    public abstract String getSelectSQL();
    public List<E> findAll(Class<E> clazz) {
       // Use getSelectSQL();
    }
}

public class AlbumFactory extends BaseFactory<Album> {
    public String getSelectSQL() { return "select * from albums....."; }
}

Aber es ist kein sehr guter Geruch Objekte zu haben, ohne Staat.

Wenn Sie eine Klasse zu findAll sind vorbei, warum können Sie nicht eine Klasse getSelectSQL in Modelbase passieren?

Asterite: meinst du, dass getSelectSQL nur in Modelbase vorhanden ist, und es die in Klasse übergibt verwendet einen Tabellennamen oder so etwas zu machen? Ich kann das tun, weil einige der Modelle wildy haben differeing wählen Konstrukte, so kann ich nicht eine universelle „select * from“ + classToTableName () verwenden ;. Und jeder Versuch, Informationen aus den Models über ihre Auswahl Konstrukt läuft in das gleiche Problem von der ursprünglichen Frage zu bekommen - Sie brauchen eine Instanz des Modells oder einer Phantasie Reflexion

.

Gizmo: Ich werde auf jeden Fall einen Blick in Anmerkungen haben. Obwohl ich kann nicht helfen, aber frage mich, was die Leute mit diesen Problemen haben, bevor es Reflexion war?

Sie können Ihre SQL Methoden als Instanzmethoden in einer separaten Klasse haben.
Dann das Modell übergeben Objekt an den Konstruktor dieser neuen Klasse und ihre Methoden aufrufen, SQL für immer.

Wow - das ist ein weit besseres Beispiel für etwas, das ich in allgemeinerer Form vorher gefragt - wie Eigenschaften oder Methoden zu implementieren, die in eine Art und Weise zu jeder implementierenden Klasse Static sind die Vervielfältigung vermeidet, bietet statischen Zugriff, ohne dass die Klasse instanziiert besorgt und fühlt sich 'rechts'.

Kurze Antwort (Java oder .NET): Sie können es nicht. Lange Antwort -. Sie können, wenn Sie nichts dagegen haben eine Klassenstufe Anmerkung (Reflexion) oder instanziieren ein Objekt (Instanz-Methode) zu verwenden, aber weder sind wirklich ‚sauber‘

Siehe meine vorherige (bezogen) Frage hier: Wie geht man mit statischen Felder, die durch die Implementierung von Klasse variieren Ich dachte, die alle Antworten wirklich lahm waren und den Punkt verpasst. Ihre Frage ist viel besser formuliert.

Ich bin mit Gizmo: Sie entweder auf Anmerkungen oder irgendeine Art von Konfigurationsdatei suchen. Ich würde einen Blick auf Hibernate nehmen und anderem ORM-Frameworks (und vielleicht sogar Bibliotheken wie log4j!) Zu sehen, wie sie Beladung von Klasse-Level-Meta-Informationen verarbeiten.

Nicht alles kann oder programmatisch getan werden sollte, ich fühle mich dies einer jener Fälle sein kann.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top