Ist es übertrieben, eine Sammlung nur aus Gründen der besseren Lesbarkeit in eine einfache Klasse zu packen?

15

Ich habe folgende Karte:

Map<Double, List<SoundEvent>> soundEventCells = new HashMap<Double, List<SoundEvent>>();

Dies HashMapordnet doubleWerte (die Zeitpunkte sind) der entsprechenden SoundEvent'Zelle' zu: Jede 'Zelle' kann eine Anzahl von SoundEvents enthalten. Deshalb ist es als implementiert List<SoundEvent>, weil es genau das ist, was es ist.

Um den Code besser lesbar zu machen, habe ich mir überlegt, eine sehr einfache statische innere Klasse wie die folgende zu implementieren:

private static class SoundEventCell {
    private List<SoundEvent> soundEvents = new ArrayList<SoundEvent>();
    public void addEvent(SoundEvent event){
        soundEvents.add(event);
    }
    public int getSize(){
        return soundEvents.size();
    }
    public SoundEvent getEvent(int index){
        return soundEvents.get(index);
    }
    // .. remove() method unneeded
}

Und dann würde die Kartendeklaration (und viele andere Codes) besser aussehen, zum Beispiel:

Map<Double, SoundEventCell> soundEventCells = new HashMap<Double, SoundEventCell>();

Ist das übertrieben? Würden Sie dies in Ihren Projekten tun?

Aviv Cohn
quelle
man kann argumentieren, dass dies konzeptionell in Wie würden Sie wissen, wenn Sie lesbaren und leicht zu wartenden Code geschrieben haben? Wenn Ihre Kollegen über Ihre Art und Weise immer wieder beschweren , Dinge zu tun, sei es eine oder andere Weise, Sie besser ändern, damit sie sich besser fühlen
gnat
1
Was macht die Liste der Klangereignisse zu einer "Zelle" und nicht zu einer Liste? Bedeutet diese Wortwahl, dass sich eine Zelle anders verhält oder irgendwann anders verhält als eine Liste?
X-Code
@DocBrown Warum? Die Klasse wird private staticnur von der äußeren Klasse verwendet, bezieht sich jedoch nicht auf eine bestimmte Instanz der äußeren Klasse. Ist das nicht genau die richtige Verwendung von private static?
Aviv Cohn
2
@ Doc Brown, Aviv Cohn: Es gibt kein Tag, das eine Sprache angibt, also kann alles gleichzeitig richtig und falsch sein!
Emilio Garavaglia
@EmilioGaravaglia: Java (Ich denke, es ist ziemlich klar, da es nach der Syntax entweder Java oder C # sein könnte, und die verwendeten Konventionen beschränken es auf Java;)).
Aviv Cohn

Antworten:

12

Es ist überhaupt nicht übertrieben. Beginnen Sie mit den Operationen, die Sie benötigen, anstatt mit "Ich kann eine HashMap verwenden" zu beginnen. Manchmal ist eine HashMap genau das, was Sie brauchen.
Ich vermute, dass es in Ihrem Fall nicht so ist. Was Sie wahrscheinlich tun möchten, ist etwa so:

public class EventsByTime {
    public EventsByTime addEvent(double time, SoundEvent e);
    public List<SoundEvent> getEvents(double time);
    // ... more methods specific to your use ...
}

Sie möchten definitiv keinen Code haben, der dies sagt:

List<SoundEvent> events = eventMap.get(time);
if (events == null) {
   events = new ArrayList<SoundEvent>();
   eventMap.put(time, events);
}

Oder Sie könnten einfach eine der Guava Multimap- Implementierungen verwenden.

Kevin Cline
quelle
Sie befürworten also die Verwendung einer Klasse, bei der es sich im Wesentlichen um einen Mechanismus zum Ausblenden von Informationen handelt, als ... Mechanismus zum Ausblenden von Informationen? Der Horror.
Robert Harvey
1
Eigentlich ja, ich habe eine TimeLineKlasse genau für diese Art von Dingen :) Es ist eine dünne Hülle um eine HashMap<Double, SoundEventCell>(irgendwann habe ich mich für die SoundEventCellanstatt für eine List<SoundEvent>Idee entschieden). Also kann ich es einfach tun timeline.addEvent(4.5, new SoundEvent(..))und die Sachen auf niedrigerer Ebene einkapseln lassen :)
Aviv Cohn
14

In einigen Bereichen kann dies die Lesbarkeit verbessern, aber auch die Sache komplizieren. Ich persönlich lehne es aus Gründen der Übersichtlichkeit ab, Sammlungen zu verpacken oder zu erweitern, da der neue Umschlag beim ersten Lesen für mich impliziert, dass es möglicherweise ein Verhalten gibt, dessen ich mich bewusst sein muss. Betrachten Sie es als eine Nuance des Prinzips der geringsten Überraschung.

Wenn ich mich an die Schnittstellenimplementierung halte, muss ich mich nur um die Schnittstelle kümmern. Die konkrete Implementierung kann natürlich zusätzliches Verhalten beinhalten, aber ich sollte mir darüber keine Sorgen machen müssen. Wenn ich also versuche, mich im Code von jemandem zurechtzufinden, bevorzuge ich die einfachen Schnittstellen für die Lesbarkeit.

Wenn Sie auf der anderen Seite einen Anwendungsfall finden, der von zusätzlichem Verhalten profitiert, haben Sie ein Argument für die Verbesserung des Codes, indem Sie eine vollwertige Klasse erstellen.

Rückkopplungsschleife
quelle
11
Ein Wrapper kann auch verwendet werden, um nicht benötigtes Verhalten zu entfernen (oder zu verbergen).
Roman Reiner
4
@ RomanReiner - Ich würde vor solchen Dingen warnen. Nicht benötigtes Verhalten heute ist oft der Programmierer, der morgen Ihren Namen verflucht. Jeder weiß, was eine ListDose kann, und das alles aus gutem Grund.
Telastyn
Ich schätze den Wunsch, die Funktionalität beizubehalten, obwohl ich der Meinung bin, dass die Lösung ein ausgewogenes Verhältnis zwischen Funktionalität und Abstraktion darstellt. SoundEventCellkönnte Iterablefür SoundEvents implementieren , was den Iterator des soundEventsMitglieds bieten würde, so dass Sie in der Lage wären, wie jede Liste zu lesen (aber nicht zu schreiben). Ich zögere, die Komplexität fast so zu maskieren, wie ich zögere, eine zu verwenden, Listwenn ich in Zukunft etwas Dynamischeres brauchen könnte.
Neil
2

Durch das Umschließen wird Ihre Funktionalität auf die Methoden beschränkt, für die Sie sich zum Schreiben entschieden haben, und der Code wird im Grunde genommen ohne Nutzen erhöht. Zumindest würde ich Folgendes versuchen:

private static class SoundEventCell : List<SoundEvent>
{
}

Sie können weiterhin den Code aus Ihrem Beispiel schreiben.

Map<Double, SoundEventCell> soundEventCells = new HashMap<Double, SoundEventCell>();

Das habe ich allerdings nur getan, wenn es einige Funktionen gibt, die die Liste selbst benötigt. Aber ich denke, Ihre Methode wäre übertrieben. Es sei denn, Sie hatten einen Grund, den Zugriff auf die meisten Methoden von List einzuschränken.

Lawtonfogle
quelle
-1

Eine andere Lösung könnte darin bestehen, Ihre Wrapper-Klasse mit einer einzigen Methode zu definieren, die die Liste verfügbar macht:

private static class SoundEventCell
{
    private List<SoundEvent> events;

    public SoundEventCell(List<SoundEvent> events)
    {
        this.events = events;
    }

    public List<SoundEvent> getEvents()
    {
        return events;
    }
}

Dies gibt Ihnen Ihre gut benannte Klasse mit minimalem Code, bietet Ihnen aber dennoch eine Kapselung, mit der Sie z. B. die Klasse unveränderlich machen können (indem Sie eine defensive Kopie im Konstruktor erstellen und Collections.unmodifiableListim Accessor verwenden).

(Wenn diese Listen jedoch nur in dieser Klasse verwendet werden, sollten Sie sie meines Erachtens besser durch Map<Double, List<SoundEvent>>ein Multimap<Double, SoundEvent>( docs ) ersetzen , da dies häufig viel Logik und Fehler bei der Nullprüfung erspart.)

MikeFHay
quelle