39 Stimmen

Zu vermeidende Klassen (Code vollständig)

Ich bin etwas verwirrt über einen Absatz in dem Buch "Code Complete".

Im Abschnitt "Zu vermeidende Klassen" heißt es:

"Vermeiden Sie Klassen, die nach Verben benannt sind. Eine Klasse, die nur Verhalten, aber keine Daten hat, ist im Allgemeinen keine richtige Klasse. Erwägen Sie, eine Klasse wie DatabaseInitialization() oder StringBuilder() in eine Routine für eine andere Klasse zu verwandeln.

Mein Code besteht hauptsächlich aus Verbklassen ohne Daten. Es gibt Rechnungsleser, Preiskalkulatoren, Nachrichtenersteller usw. Ich mache das, um die Klassen auf jeweils eine Aufgabe zu konzentrieren. Dann füge ich Abhängigkeiten zu anderen Klassen für andere Funktionen hinzu.

Wenn ich den Absatz richtig verstehe, sollte ich folgenden Code verwenden

class Webservice : IInvoiceReader, IArticleReader {
    public IList<Invoice> GetInvoices();
    public IList<Article> GetArticles();
}

statt

class InvoiceReader : IInvoiceReader {
    public InvoiceReader(IDataProvider dataProvider);
    public IList<Invoice> GetInvoices();
}

class ArticleReader : IArticleReader {
    public ArticleReader(IDataProvider dataProvider);
    public IList<Article> GetArticles();
}

Modifier Vielen Dank für die vielen Antworten.

Meine Schlussfolgerung ist, dass mein derzeitiger Code eher SRP als OO ist, aber auch unter dem "anämischen Domänenmodell" leidet.

Ich bin sicher, dass mir diese Erkenntnisse in Zukunft helfen werden.

20voto

Ash Punkte 58914

Klassennamen wie InvoiceReader, PriceCalculator, MessageBuilder, ArticleReader, InvoiceReader sind eigentlich keine Verbnamen. Sie sind eigentlich "Substantiv-Agent-Nomen"-Klassennamen. Siehe Erregersubstantive .

Ein Verbklassenname wäre z. B. Validieren, Bedienen, Verwalten usw. Offensichtlich werden diese besser als Methoden verwendet und wären als Klassennamen recht umständlich.

Das größte Problem bei "Substantiv-Agent-Nomen"-Klassennamen besteht darin, dass sie nur sehr wenig darüber aussagen, was die Klasse tatsächlich tut (z. B. UserManager, DataProcessor usw.). Infolgedessen ist die Wahrscheinlichkeit größer, dass sie aufgebläht werden und den internen Zusammenhalt verlieren. (Siehe Prinzip der einzigen Verantwortung ).

Daher ist die WebService-Klasse mit den Schnittstellen IInvoiceReader und IArticleReader wahrscheinlich das klarere und sinnvollere OO-Design.

Dadurch erhalten Sie den einfachen, offensichtlichen Substantiv-Klassennamen "WebService", zusammen mit "Substantiv-Agent-Nomen"-Schnittstellennamen, die klar angeben, was die WebService-Klasse für den Anrufer tun kann.

Sie könnten der eigentlichen Klasse wahrscheinlich auch mehr Bedeutung verleihen, indem Sie ein anderes Substantiv voranstellen, zum Beispiel PaymentWebService.

Die Schnittstellen sind jedoch immer besser als ein einzelner Klassenname, um genauer zu beschreiben, was die Klasse für den Aufrufer tun kann. Wenn die Klasse komplexer wird, können auch neue Schnittstellen mit sinnvollen Namen hinzugefügt werden.

5voto

Daniel Daranas Punkte 22022

Befolgen Sie keine Ratschläge blindlings. Dies sind nur Leitlinien.

Davon abgesehen, Substantive sind sehr gute Klassennamen solange sie logische Objekte modellieren. Da die Klasse "Person" eine Blaupause für alle "Person"-Objekte ist, ist es sehr praktisch, sie "Person" zu nennen, denn so können Sie wie folgt argumentieren: "Ich werde eine Person aus den Eingaben des Benutzers erstellen, aber zuerst muss ich sie validieren..."

3voto

Coincoin Punkte 26516

Bitte beachten Sie die Verwendung des Wortes "vermeiden". Es heißt nicht "eliminieren", "ausrotten" oder "in der Hölle schmoren", wenn Sie sie jemals verwenden.

Was der Autor meinte, ist, dass, wenn Sie sich mit einem Bündel von Klassen, die alle nach Verben benannt sind, wiederfinden und alles, was Sie tun, ist, diese Klassen statisch zu erstellen, eine Funktion aufzurufen und sie zu vergessen, ist es wahrscheinlich ein Zeichen dafür, dass Sie ein wenig zu viele Klassenbelange abtrennen.

Es gibt jedoch Situationen, in denen die Erstellung von Klassen zur Implementierung einer Aktion sinnvoll ist, z. B. wenn Sie verschiedene Strategien für dieselbe Aktion haben. Ein sehr gutes Beispiel ist IComparer<>. Er vergleicht lediglich zwei Dinge, aber es gibt mehrere Möglichkeiten, Dinge zu vergleichen.

Wie der Autor vorschlägt, ist es in diesen Fällen eine gute Möglichkeit, eine Schnittstelle zu erstellen und zu implementieren. IComparer<> kommt mir wieder in den Sinn.

Eine weitere häufige Situation ist, wenn die Aktion einen schweren Zustand hat, wie z. B. das Laden von Dateien. Es könnte gerechtfertigt sein, den Zustand in einer Klasse zu kapseln.

2voto

Mongus Pong Punkte 10997

Im Wesentlichen besagt das Buch, dass es beim OO-Design darum geht, die Objekte (die Substantive) zu extrahieren und die Operationen (die Verben) zu identifizieren, die an und zwischen diesen Objekten stattfinden.

Die Substantive werden zu den Objekten, die Verben zu den Methoden, die auf diese Objekte wirken.

Die Idee ist, dass die

näher das Programm modelliert t Problem der realen Welt, desto besser ist das Programm sein.

Praktisch gesehen besteht der Nutzen eines Objekts darin, dass es einen bestimmten Zustand repräsentieren kann. Dann kann man mehrere verschiedene Instanzen dieser Klasse haben, die jeweils einen anderen Zustand haben, um einen Aspekt des Problems darzustellen.

Im Fall der Klasse InvoiceReader

  • Sie werden nur eine Instanz erstellen
  • der einzige Zustand, den es repräsentiert, ist der, dass es einen dataProvider enthält
  • sie enthält nur eine Methode

Es gibt keinen Vorteil, ihn in einem Objekt zu platzieren.

1voto

Groo Punkte 47623

Die Erklärung Eine Klasse, die nur Verhalten, aber keine Daten hat, ist im Allgemeinen keine richtige Klasse. ist schlichtweg falsch.

Das Extrahieren von Verhalten in eine separate Klasse ist eine gute und übliche Vorgehensweise beim Refactoring. Sie kann einen Zustand haben, muss aber auch keinen haben. Sie müssen saubere Schnittstellen haben und sie so implementieren, wie Sie es für notwendig halten.

Außerdem eignen sich zustandslose Klassen hervorragend für Berechnungen, die Sie nur für einen kurzen Zeitraum benötigen. Man instanziiert sie (oder fordert eine Art Factory an, um sie zu erhalten), führt die notwendigen Berechnungen durch und wirft sie dann in den Papierkorb. Sie können die entsprechende "Version" Ihres Verhaltens überall und jederzeit verfügbar machen.

Normalerweise finde ich, dass verschiedene Implementierungen einer Schnittstelle einen gewissen Status haben (z. B. im Konstruktor festgelegt), aber manchmal kann der Typ Ihrer Klasse ihr Verhalten vollständig bestimmen.

Zum Beispiel:

public interface IExporter
{
    /// <summary>
    /// Transforms the specified export data into a text stream.
    /// </summary>
    /// <param name="exportData">The export data.</param>
    /// <param name="outputFile">The output file.</param>
    void Transform(IExportData exportData, string outputFile);
}

kann implementiert werden als

class TabDelimitedExporter : IExporter { ... }
class CsvExporter : IExporter { ... }
class ExcelExporter : IExporter { ... }

Um das Exportieren von IExportData (was auch immer das sein mag) in eine CSV-Datei zu übertragen, brauchen Sie wahrscheinlich überhaupt keinen Status. ExcelExporter könnte dagegen verschiedene Eigenschaften für Exportoptionen haben, aber auch zustandslos sein.

[Bearbeiten]

Umzug GetInvoices y GetArticles in die WebService Klasse bedeutet, dass Sie ihre Implementierung an den WebService-Typ binden werden. Wenn Sie sie in separaten Klassen haben, können Sie unterschiedliche Implementierungen für Rechnungen und Artikel haben. Insgesamt scheint es besser zu sein, sie getrennt zu haben.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X