Ist es ein Code-Geruch, wenn Sie häufig ein Objekt erstellen, nur um eine Methode aufzurufen


14

Ich habe eine Codebasis geerbt, in der es eine Menge Code gibt, der ungefähr so ​​aussieht:

SomeDataAdapter sda = new SomeDataAdapter();
sda.UpdateData(DataTable updateData);

Und dann wird sda nie wieder benutzt.

Ist das ein Codegeruch, der darauf hinweist, dass diese Methoden stattdessen statische Klassenmethoden sein sollten?


Implementieren diese Klassen Schnittstellen?
Reactgular

7
Der Tag nach der Umgestaltung auf statische Methoden ist der Tag, an dem Sie Komponententests mit einer gespielten Version Ihres Datenadapters ausführen möchten.
Mike

22
@ Mike: Warum müsstest du jemals eine statische Methode verspotten? Ich werde dieses trügerische Argument, dass statische Methoden nicht überprüfbar sind, schrecklich müde. Wenn Ihre statischen Methoden den Status halten oder Nebenwirkungen hervorrufen, liegt dies an Ihnen und nicht an Ihrer Testmethode .
Robert Harvey

9
Meiner Meinung nach ist es ein Sprachgeruch , der die Schwäche der Analyse "Alles muss eine Klasse sein" zeigt.
Ben

7
@RobertHarvey: Möglicherweise müssen Sie eine statische Methode aus dem gleichen Grund verspotten, aus dem Sie eine andere Methode verspotten: Der Aufruf während des Komponententests ist zu teuer.
Kevin Cline

Antworten:


1

Ich würde es als Architekturgeruch betrachten, dass UpdateData wahrscheinlich zu einer 'Service'-Klasse gehören sollte.

Wo die Daten ein Apple sind. Wobei AppleAdapter eine Service- / Business-Intelligence-Klasse ist. Wobei AppleService ein Singleton-Verweis auf einen AppleAdapter ist, der außerhalb der aktuellen Methode vorhanden ist.

private static volatile AppleAdapter _appleService = null;
private static object _appleServiceLock = new object();
private AppleAdapter AppleService
{
    get
    {
        if (_appleService == null)
        {
            lock (_appleServiceLock)
            {
                if (_appleService == null)
                    _appleService = new AppleAdapter();
            }
        }
        return _appleService;
    }
}

public SomeAppleRelatedMethod(Apple apple)
{
    AppleService.UpdateData(apple);
}

Ich denke nicht, dass das, was Sie tun, notwendigerweise falsch ist, aber wenn SomeDataAdapter tatsächlich eine Art zustandslosen Geschäftsdienst darstellt, dann wäre ein Singleton die beste Vorgehensweise dafür. Ich hoffe, das hilft! Das angegebene Beispiel ist eine hervorragende Möglichkeit, um sicherzustellen, dass der _appleService nicht in Konflikt gerät, wenn er null ist und zwei oder mehr Threads gleichzeitig auf ihn zugreifen.

Weißt du was? Wenn SomeDataAdapter ein ADO-IDbDataAdapter ist (was mit ziemlicher Sicherheit der Fall ist), ignorieren Sie diese gesamte Antwort!

: P

Ich habe keine Berechtigung, der ursprünglichen Frage einen Kommentar hinzuzufügen, aber wenn Sie angeben könnten, wo dieser Code vorhanden ist.

Wenn dieser Code eine benutzerdefinierte Implementierung eines IDbDataAdapters darstellt und UpdateData eine IDbConnection, einen IDbCommand erstellt und alles hinter den Kulissen verkabelt, dann würde ich keinen Codegeruch in Betracht ziehen, da es sich jetzt um Streams und andere handelt Dinge, die entsorgt werden müssen, wenn wir damit fertig sind.


12
Hilfe! Ich ertrinke in Software-Mustern.
Robert Harvey

4
... oder als Abhängigkeit injiziert. ;)
Rob

12
Das Spielen mit Singletons und doppelt überprüften Sperren, um etwas zu erhalten, das einer einfachen Funktion / Prozedur entspricht, ist für mich ein weitaus größerer Codegeruch!
Ben

3

Ich denke, dieses Problem geht noch tiefer. Selbst wenn Sie es in eine statische Methode umgestalten, erhalten Sie überall Code, in dem Sie eine einzelne statische Methode aufrufen. Was meiner Meinung nach Code-Geruch ist. Es könnte darauf hindeuten, dass Sie eine wichtige Abstraktion vermissen. Möglicherweise möchten Sie einen Code erstellen, der einige Vor- und Nachbearbeitungen vornimmt und gleichzeitig die Möglichkeit bietet, die Zwischenabläufe zu ändern. Diese Vor- und Nachbearbeitung wird die gemeinsamen Aufrufe enthalten, während das dazwischenliegende von der konkreten Implementierung abhängt.


Ich stimme zu, dass es ein viel tieferes Problem gibt. Ich versuche, es schrittweise zu verbessern, ohne eine vollständige architektonische Neugestaltung vorzunehmen, aber es hört sich so an, als wäre das, was ich dachte, kein guter Weg, dies zu tun.
Shane Wealti

1

So'ne Art. In der Regel bedeutet dies, dass Sie eine Funktion weitergeben möchten und die Sprache Ihrer Wahl dies nicht zulässt. Es ist etwas ungeschickt und unelegant, aber wenn Sie in einer Sprache ohne erstklassige Funktionen festsitzen, können Sie nicht viel tun.

Wenn keines dieser Objekte als Argumente an andere Funktionen übergeben wird, können Sie sie in statische Methoden umwandeln, und es ändert sich nichts.


Dies kann auch bedeuten, dass die statische Methode eine geänderte Version (oder eine geänderte Kopie) eines Objekts zurückgeben soll, das Sie an das Objekt übergeben.
Robert Harvey

2
"Wenn Sie in einer Sprache ohne erstklassige Funktionen stecken bleiben": Es gibt keinen Unterschied zwischen einem Objekt mit nur einer Methode und einer erstklassigen Funktion (oder einem erstklassigen Abschluss): Es handelt sich lediglich um zwei unterschiedliche Ansichten derselben Sache.
Giorgio

4
Theoretisch nein. In der Praxis ist es der Unterschied zwischen fn x => x + 1und new Function<Integer, Integer>() { public Integer eval(Integer x) { return x + 1; }};oder die Beschränkung auf ein paar hartkodierte und äußerst nützliche Funktionen , wenn Ihre Sprache nicht mindestens einen Syntaxzucker hat .
Doval

Warum konnte fn x => x + 1syntaktischer Zucker nicht sein new Function<Integer, Integer>() { public Integer eval(Integer x) { return x + 1; }}? Ok, vielleicht meinst du, wenn man in einer Sprache ohne diese Art von syntaktischem Zucker steckt.
Giorgio

@ Giorgio Sie müssen Oracle fragen. (Zugegeben, das ändert sich endlich mit Java 8). Beachten Sie, dass Sie mehr Syntaxzucker benötigen, um wirklich nützlich zu sein - Sie möchten auch in der Lage sein, vordeklarierte Methoden namentlich zu übergeben. Currying wäre auch praktisch. Irgendwann muss man sich fragen, ob es sich lohnt, all diese Hacks zu haben, anstatt Funktionen als erste Bürger zu behandeln. Aber jetzt komme ich vom Thema ab.
Doval

0

Wenn der Haltestatus eine bestimmte Klasse nützlicher, funktionaler… wiederverwendbar macht, dann nein. Aus irgendeinem Grund kommt das Befehlsentwurfsmuster in den Sinn; dieser Grund ist sicherlich nicht der Wunsch, Robert Harvey ertrinken zu lassen.


0

Definieren Sie "häufig". Wie oft instanziieren Sie das Objekt? Viel - wahrscheinlich nicht?

Es gibt wahrscheinlich größere Probleme in Ihrem System. Wenn Sie statisch werden, wird dem Programmierer klarer mitgeteilt, dass sich der interne Zustand des Objekts nicht ändert - großartig.

Wenn der Zustand jedoch durcheinander gebracht wird und Sie andere Dinge statisch machen müssen, um das Erste statisch zu machen, müssen Sie sich fragen, welche Teile statisch sein müssen und welche nicht.

Ja, es ist ein Codegeruch, nur ein kleiner.


0

Machen Sie es zu einer statischen Methode und fahren Sie fort. An statischen Methoden ist nichts auszusetzen. Sobald ein Verwendungsmuster auftritt, können Sie sich Gedanken über die Abstraktion des Musters machen.


0

Der Geruch hier ist, dass dies prozeduraler Code ist, der (minimal) in Objekte eingeschlossen ist.

Es muss einen Grund geben warum Sie diese Operation in der Datentabelle ausführen möchten, aber anstatt diese Operation mit anderen verwandten Operationen zu modellieren, die eine bestimmte Semantik aufweisen (dh eine gemeinsame Bedeutung haben), hat der Autor einfach eine neue Prozedur in einer neuen Prozedur gestartet Klasse & nannte es.

In einer funktionalen Sprache würden Sie so vorgehen;), aber im OO-Paradigma möchten Sie modellieren eine Abstraktion , die diese Operation einschließt. Dann würden Sie OO-Techniken verwenden, um dieses Modell zu verfeinern und eine Reihe verwandter Operationen bereitzustellen, die einige Semantik bewahren.

Der Hauptgeruch hier ist also, dass der Autor Klassen verwendet, wahrscheinlich weil der Compiler dies benötigt, ohne Operationen in einem konzeptuellen Modell zu organisieren.

Übrigens: Passen Sie immer auf, wenn Sie sehen, dass das Programm anstelle des Problembereichs modelliert wird . Dies ist ein Zeichen dafür, dass das Design von mehr Überlegungen profitieren könnte. Mit anderen Worten, achten Sie auf Klassen, die alle "xAdaptor" und "xHandler" und "xUtility" sind und normalerweise an ein anämisches Domänenmodell gebunden sind . Es bedeutet, dass jemand nur Code bündelt, um die Implementierung zu vereinfachen, anstatt die Konzepte zu modellieren, die er implementieren möchte.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.