Code-Wiederverwendung in Methods.class vs Strategie Muster und Abhängigkeitsinjektion

8

Status: Antworten von Fendy und Glen Best sind gleichermaßen akzeptabel und werden von mir geehrt, aber da man akzeptiert werden kann und Kopfgeld gegeben wird, wähle ich Fendys Antwort.

Scenario:

Wenn ich einen Code habe, der mehrfach in vielen Klassen wiederverwendet werden muss (selten mit geringfügigen Parameteränderungen, was offensichtlich ist) und gleichzeitige Threads, Welcher Ansatz gilt?

Der Code, der wiederverwendet werden muss, kann ein vernünftiger Code sein (mit angemessener Sorgfalt in Bezug auf den statischen und nicht-statischen Kontext im Kopf und Methoden zur Erstellung von Methoden). Es kann ein Algorithmus sein, eine DB-Methode, die verbindet, operiert, schließt. Alles.

  1. Machen Sie eine Klasse wie MyMethods.class und setzen Sie alle diese Methoden ein .

    1.a. Machen Sie Methoden static und rufen Sie (von allen Klassen und gleichzeitigen Threads) direkt als MyMethods.someMethod();

    auf

    1.b. Machen Sie Methoden non-static und zu der Zeit, um sie aufzurufen, instantiate die ganze Klasse von MyMethods mm = MyMethods(); mm.someMethod();

  2. Verwenden Sie das Strategie-Muster unter Ссылка (hier angehängter Code).

  3. Verwenden Sie die Abhängigkeitsinjektion , die unter Ссылка

Problems:

  1. Manche Leute würden sagen Komponententest Ссылка wird nicht möglich sein > Bei diesem Ansatz wird Schwierigkeiten beim Austauschen machen. Wenn Sie Ihre Klasse testen und eine Mock-Version der Abhängigkeit

    verwenden möchten

    1.a. Wird es Probleme mit gleichzeitigen Anrufen oder mehreren Klassen geben? speziell in JDBC static methods für nur ein Beispiel?

    1.b. Ich denke, es würde zu viel Speicherlast machen, da eine ganze Klasse instanticated wäre, um nur eine oder zwei Methoden aufzurufen

  2. Das ist weit über meinem Kopf, erklären Sie das und oder irgendwelche Vorteile / Nachteile

  3. Ich möchte nicht ein Framework im Zusammenhang mit dieser Frage verwenden. Das ist weit über meinem Kopf, erkläre das und oder irgendwelche Vorteile / Nachteile

  4. Warten auf andere Strategien oder Empfehlungen, falls vorhanden.

Request: Bitte antworten Sie nur, wenn Sie erfahren sind und die Implikationen genau kennen und umfassend und mit Ihrer Antwort mir und der Gemeinschaft als Ganzes helfen können!

Code:

%Vor%     
Masood Ahmad 26.06.2013, 08:37
quelle

3 Antworten

8

Ihre Frage hat eigentlich zwei Bedeutungen.

  

das in vielen Klassen viele Male wiederverwendet werden muss

Dies kann ein Kontext von Entwurfsmustern (wiederverwendbare Komponente) oder Speicherkosten (Klasseninstanziierung) sein. Aus zwei verschiedenen Perspektiven sprechen:

Speicherkosten (Ich hatte wenig Erfahrung damit, aber lassen Sie mich meine Erfahrung teilen)

Dieser Abschnitt behandelt eigentlich nur zwei Arten von Instanziierung.

Zuerst ist statisch (oder DI-Instanziierung im Kompositionswurzel)

  • Eager instanziation bedeutet, dass alle Klassen beim Start der Anwendung instanziiert werden
  • Nur einmalige Instanziierung

Nicht statisch

  • Faule Instanziierung bedeutet, dass die Klasse nur bei Bedarf instanziiert wird
  • Einmalige Instantiierung jeder Verwendung

Kurz gesagt: Statische Kosten sind hoch, wenn die Klasse viele ist, und nicht-statische Kosten sind hoch, wenn die Anfrage hoch ist (zum Beispiel für for-Schleife). Aber es sollte Ihre Bewerbung nicht schwer machen. Die meisten Operationen in java / csharp sind jedoch Objekte erstellen.

Klassenwiederverwendbarkeit

1 - mega monolithischer Code (eine Gottklasse, die fast alles kann)

Vorteile:

  1. Einfach nach Code zu suchen (hängt immer noch davon ab), Sie wissen, dass jede Logik dort liegt, also müssen Sie sich nur diese große Klasse ansehen
  2. Wenn es statisch ist, können Sie es einfach überall aufrufen, ohne sich Gedanken über die Instanziierung machen zu müssen

Nachteile:

  1. Jede Änderung für eine Methode erzeugt ein Risiko für Fehler an anderen Stellen
  2. Verletzung von SRP bedeutet, dass diese Klasse aus verschiedenen Gründen geändert werden kann, nicht nur ein
  3. Insbesondere in der Versionierung ist es schwieriger zu verschmelzen, wenn die Änderung in getrennten Zweigen erfolgt, was zu einem Aufwand bei der Synchronisierung von Code führt

1a / statische Klasse / Singleton-Muster

Vorteile:

  1. Einfach zu verwenden
  2. Kann überall verwendet werden (nur referenzieren und aufrufen, und fertig)
  3. Das Objekt muss nicht instanziiert werden

Nachteile:

  1. Es ist schwierig, einen Unit-Test durchzuführen (es ist schwer nachzuahmen, und zu einem späteren Zeitpunkt wird es Zeit brauchen, die Testumgebung vorzubereiten. Besonders mit Daten
  2. Wenn stateful (hat Zustand), ist es schwierig, den aktuellen Zustand während des Debuggens zu bestimmen. Darüber hinaus ist es schwierig zu bestimmen, welche Funktion den Zustand ändert, und kann von überall geändert werden (
  3. )
  4. Neigen dazu, viele Parameter zu haben (etwa 5-11)

Einige Punkte zur statischen Klasse: siehe diese Frage

2 Strategie-Muster

Eigentlich hat dies das gleiche Design mit 3 oder composition over inheritance .

3 Abhängigkeitsinjektion

Vorteile:

  • Einfach zu simulieren und zu testen
  • %Code%. Es ist einfacher zu debuggen und Komponententest, wenn die Klasse zustandslos ist
  • Unterstützung, die umstrukturiert werden soll

Der Nachteil:

  • Schwer zu debuggen für diejenigen, die mit den Schnittstellen nicht vertraut sind (jedes Mal, wenn Sie die Methode umleiten, geht es an die Schnittstelle)
  • Erstellt eine Überlagerung, die zum Mapping führt

Staat / Staatenlos

Ich denke, dass Staaten wichtige Regeln in Ihrem Anwendungsdesign spielen. Normalerweise versuchen Entwickler, Zustände im Geschäftslogikcode zu vermeiden, wie zum Beispiel:

%Vor%

Entwickler neigen dazu, die Logik in andere Must be stateless -Klasse oder zumindest Methoden wie:

zu legen %Vor%

Es wird eine bessere Lesbarkeit bieten und einfacher für Modifizierungs- und Komponententests sein. Es gibt auch ein Entwurfsmuster stateless , um insbesondere solche Fälle zu behandeln.

Prinzip der einfachen Verantwortung

IMHO, dieses Prinzip hat den größten Nutzen, wenn es befolgt wird. Die Vorteile sind:

  1. In der Versionierung sind die Änderungen klar darüber, welche Klasse geändert wurde und warum
  2. In DI können mehrere kleinere Klassen verdrahtet werden, was sowohl bei der Verwendung als auch beim Komponententest für Flexibilität sorgt
  3. Modularität, geringe Kopplung und hohe Kohäsion erhöhen

TDD (Test Driven Development)

Dieser Entwurf garantiert nicht, dass Ihr Code frei von Fehlern ist. Im Gegensatz zu Kosten für die Design-Phase und Layering-Aufwand hat es den Vorteil von:

  1. Einfach zu testendes Objekt für Komponententest
  2. Einfachere Refactoring aufgrund von Komponententest und Modularität
  3. Leichter zu pflegen / zu erweitern

Einige nützliche Quellen

Anti-Pattern für Service Locator

Verwenden von Decorator als Querschnittsaufgabe

Meine 2 Cent:

Der Vorteil der Verwendung der Schnittstelle (gilt auch für die Zusammensetzung der Vererbung)

Top-down-Design / DI-Design

Abschließende Gedanken

Diese Designs und Strategien sind nicht der Schlüssel, der Ihre Anwendungsstruktur bestimmt. Es ist immer noch der Architekt, der es bestimmen wird. Ich bevorzuge es, einigen Prinzipien wie SOLID, KISS und GRASP zu folgen, anstatt zu entscheiden, was die beste Struktur ist. Es wird gesagt, dass Dependency Injection den meisten dieser Prinzipien folgt, aber zu viel Abstraktion und falsches Design der Komponenten führt zu dem Missbrauch von Singleton-Mustern.

    
Fendy 26.06.2013, 12:53
quelle
3
  

1.a. Machen Sie Methoden statisch und rufen Sie (von allen Klassen und gleichzeitigen Threads) direkt als MyMethods.someMethod ();

auf      

1.b. Machen Sie Methoden nicht statisch und initialisieren Sie die gesamte Klasse zu dem Zeitpunkt, zu dem sie aufgerufen werden. MyMethods mm = MyMethods (); mm.someMethod ();

Die Wahl zwischen diesen beiden hängt von der Funktionalität von MyMethods.class ab. Wenn MyMethods zustandslos sein soll, dann ist es ein guter Ansatz, mit static Methoden zu gehen. Andernfalls, wenn ein Methodenaufruf von einem anderen abhängt und MyMethods über Zustände (d. H. Nicht endgültige Felder) verfügt, verwenden Sie die zweite Option.

  

Verwenden Sie das in Ссылка angegebene Strategie-Muster (Code hier beigefügt).

Verwenden Sie dieses Muster, wenn MyMethods mit verschiedenen Klassen für verschiedene Zwecke erweitert werden soll und Sie auswählen, welcher Code abhängig von Ihrem Kontext ausgeführt werden soll. Wie das Wiki sagt, wenn Sie den zu verwendenden Algorithmus vor der Laufzeit nicht kennen (hängt von einigen Bedingungen ab), ist dies der richtige Weg. Gemäß Ihrer Spezifikation von MyMethods haben Sie solche Probleme nicht.

  

Verwenden Sie die Abhängigkeitsinjektion, die unter Ссылка

angegeben ist

Gleiche Antwort wie oben. Die Sache mit Abhängigkeitsinjektion ist in Inversion der Kontrolle . Eine Klasse, die MyMethods verwendet, weiß nicht über die tatsächliche Implementierung von MyMethods , aber die Injektion der realen Implementierung wird an eine höhere Autorität delegiert. Es abstrahiert externe Abhängigkeiten aus dem Kontext, in dem es verwendet werden soll. Wenn MyMethods wieder zustandslos und konstant sein soll (nicht geplant, sich zu ändern, und das Verhalten der Methoden in der Klasse nicht vom Kontext abhängt, in dem sie verwendet werden), brauchen Sie diese Muster nicht, da dies nur Übertechnik bedeuten würde .

Ich würde daraus schließen, dass Sie das Strategie oder DI Muster verwenden sollten, wenn die Logik von MyMethods vom Kontext abhängt, aus dem sie ausgeführt werden. Wenn diese Konstante konstant ist (z. B. ob Javas Math -Klasse egal ist, wer oder in welchem ​​Kontext jemand sqrt() , max() oder pow() aufruft), dann sind statische Methoden die richtige Wahl.

Bei Problemen:

Probleme, die Sie angegeben haben, sind nicht vorhanden, wenn Sie MyMethods mit static Methoden verwenden. Sie müssen testen, ob Ihre Methoden korrekte Werte für bestimmte Argumente liefern, und das war's. Ich glaube nicht, dass es viel mehr Probleme beim Testen der tatsächlichen Implementierung von Strategy in Strategy pattern oder der Implementierung von Schnittstellen geben würde, die durch Dependency injection injiziert werden. Was vielleicht schwieriger ist, testet die Klassen, die die Strategie verwenden, weil es manchmal nicht einfach ist, den Kontext wiederherzustellen, in dem die bestimmte Strategie ausgeführt wird, da dies oft von Benutzereingaben abhängt, aber es ist definitiv nicht unmöglich. Die Abhängigkeitsinjektion ist meines Erachtens ideal zum Testen, da Sie die zu testende Einheit von einer Abhängigkeit trennen können, die Sie leicht verspotten können.

    
darijan 26.06.2013 08:51
quelle
3
  1. Die Hauptfrage: Wiederverwendung von Code

      

    Wenn ich Code habe, der viele Male in vielen Klassen wiederverwendet werden muss (selten mit geringfügigen Parameteränderungen, was offensichtlich ist) und gleichzeitige Threads, welcher Ansatz ist zu beachten?

    Weil Sie keine Schnitt- und Pasteten in Betracht ziehen, meinen Sie:

      

    ... viele Male von vielen Klassen wiederverwendet ...

    Was Sie fragen, ist nichts Besonderes oder Spezifisches. Es ist üblich, dass Code entweder in einer einzelnen App oder in mehreren Apps wiederverwendet wird. Die allgemeine Antwort: Verwenden Sie objektorientiertes Design / Programmierung . Setzen Sie den Code in eine Klasse, erstellen Sie ein Objekt als Instanz, rufen Sie das Objekt ...

    auf

    1a. Wiederverwendung über statische Methoden:

      

    Machen Sie Methoden statisch und rufen Sie (durch alle Klassen und gleichzeitige Threads) direkt als MyMethods.someMethod ()

    auf
    • Wenn Ihre Klasse statuslos ist (keine Instanzvariablen), ist dies ein großartiger Ansatz.
    • Wenn Ihre Klasse einen Status auf Klassenebene hat (nur statische Instanzvariablen), aber die Variablen schreibgeschützt (unveränderlich) sind, ist dies ein guter Ansatz.
    • Wenn Ihre Klasse einen Status auf Klassenebene hat (nur statische Instanzvariablen) und die Variablen Werte ändern (veränderbar), kann dies ein geeigneter Ansatz sein. Wenn Sie jedoch auf Ihre Klasse aus mehreren Threads zugreifen möchten, müssen Sie dafür sorgen, dass sie threadsicher ist: Sie können Ihre Methoden synchronisieren oder über einen internen Code für alle Daten verfügen, der sich gegenseitig ausschließt liest und schreibt.
    • Wenn Ihr Code über den Status auf Objektebene verfügt (nicht statische Instanzvariablen), funktioniert dieser Ansatz nicht - es ist nicht möglich, auf nicht statische Instanzvariablen zuzugreifen, ohne ein Objekt zu instanziieren.

    1b. Wiederverwendung über nicht-statische Methoden, mit Objekt Instanziierung:

      

    Machen Sie Methoden nicht-statisch und initialisieren Sie die ganze Klasse zu der Zeit, um sie aufzurufen, indem Sie MyMethods verwenden mm = MyMethods (); mm.someMethod ();

    • Wenn Ihre Klasse nur statische Instanzvariablen hat, ist das ein schlechter Ansatz, da das Instanziieren des Objekts nichts bewirkt
    • Wenn Ihre Klasse nicht statische Instanzvariablen hat, ist dies der einzige Ansatz. Obligatorisch, um ein Objekt für den Zugriff auf die Variablen zu instanziieren.
    • Wenn die instanziierten Objekte über mehrere Threads hinweg verwendet werden sollen, sollten sie (in der Reihenfolge ihrer Präferenz):
      • stateless (keine Instanzvariablen) - wirklich eine Option für 1a - keine Notwendigkeit,
      • zu instanziieren
      • unveränderliche (schreibgeschützte nicht-statische Instanzvariablen)
      • synchronisiert auf allen Daten liest & amp; schreibt
  2. Verwenden Sie das Strategie-Muster

    Strategie Muster kann gute Praxis sein. Aber es hat wenig mit Ihrer allgemeinen Frage zu tun. Das Strategie-Pattern wird aus einem bestimmten Grund verwendet - um die Implementierung des Algorithmus / der Verarbeitungslogik "on-the-fly" zu vertauschen, ohne den Aufrufer zu beeinflussen.

  3. Verwenden Sie die Abhängigkeitsinjektion

    Die Abhängigkeitsinjektion wird aus folgenden Gründen verwendet:

    • Fabrik & amp; Objekt-Cache-Funktionalität: Entfernt Ihre Code-Verantwortung für die Objekterstellung, Zwischenspeicherung und Suche
    • Intermediation für die Objektfreigabe: ermöglicht es verschiedenen Klassen, dieselbe Objektinstanz (in einem bestimmten Bereich / Kontext gespeichert) zu teilen, ohne dass die beiden Klassen das Objekt direkt zwischen sich übergeben
    • "Verdrahtungssteuerung" zwischen Objektinstanzen - Einrichten von Objektassoziationen und unter CDI Unterstützung für Interceptor-, Decorator- und Observer-Patterns

    Dies kann eine sehr gute Übung sein, wenn es entsprechend verwendet wird. In Ihrem Fall könnte dies nur unter Option 1b gelten. Bei der Abhängigkeitsinjektion geht es um die Instanziierung und Bereitstellung von Objekten in Variablen.

Probleme:

  1. Einige Leute würden sagen, Unit-Test wird nicht möglich sein

    • Mocking-Frameworks (und handcodierte Komponententests) befassen sich mit dem Ersetzen von Klassen durch Scheinlogik, die ganze Zeit . Es ist ein sehr normales Szenario. Sie können eine Klasse erweitern, um ihre Logik zu verspotten - wenn sie keine endgültigen öffentlichen Methoden hat. Sie können auch Methodendeklarationen an eine Schnittstelle übergeben, die Klasse die Schnittstelle implementieren und dann durch Implementation der Schnittstelle mit einer anderen Klasse vortäuschen.
    • Mit anderen Worten, dies ist keine Einschränkung / Kraft, die irgendeine Ihrer Optionen beeinträchtigt.

    1a. Siehe oben

    1b. Speicher laden

      

    Ich denke, es würde zu viel Speicher laden, da eine ganze Klasse viele Male instantiiert werden würde, um nur eine oder zwei Methoden aufzurufen.

    Ein kleines Problem. Abhängig von den Daten innerhalb jeder Objektinstanz (den Instanzvariablen) kann jede Objektinstanz so klein wie ein Dutzend Bytes oder so groß wie Megabytes sein - aber normalerweise zum unteren Ende geneigt (oft & lt; 1kB). Der Speicherverbrauch der Klasse code selbst wird nicht jedes Mal repliziert, wenn die Klasse instanziiert wird.

    Natürlich ist es eine gute Übung, das Volumen von Objekten entsprechend Ihren Anforderungen zu minimieren. Erstellen Sie keine neue Instanz, wenn Sie bereits eine verwendbare haben. Erstellen Sie weniger Objektinstanzen und teilen Sie sie in Ihrer App auf, indem Sie sie an Konstruktormethoden und Setter-Methoden übergeben. Die Abhängigkeitsinjektion ist eine gute Möglichkeit, Objektinstanzen "automatisch" zu teilen, ohne sie an Konstruktoren / Setter zu übergeben.

  2. Siehe oben

  3. Siehe oben

Glen Best 04.07.2013 05:06
quelle