Wann riechen Enums NICHT nach Code?

17

Dilemma

Ich habe viele Best-Practice-Bücher über objektorientierte Praktiken gelesen, und fast jedes Buch, das ich gelesen habe, enthielt einen Teil, in dem Enums als Codegeruch bezeichnet werden. Ich denke, sie haben den Teil verpasst, in dem sie erklären, wann Aufzählungen gültig sind.

Als solches suche ich nach Richtlinien und / oder Anwendungsfällen, bei denen Aufzählungen KEIN Codegeruch und tatsächlich ein gültiges Konstrukt sind.

Quellen:

"WARNUNG Als Faustregel gilt, dass Aufzählungen Code-Gerüche sind und in polymorphe Klassen umgewandelt werden sollten. [8]" Seemann, Mark, Dependency Injection in .Net, 2011, p. 342

[8] Martin Fowler et al., Refactoring: Verbesserung des Entwurfs bestehender Codes (New York: Addison-Wesley, 1999), 82.

Kontext

Die Ursache meines Dilemmas ist eine Handels-API. Sie geben mir einen Stream von Tick-Daten, indem sie diese Methode senden:

void TickPrice(TickType tickType, double value)

wo enum TickType { BuyPrice, BuyQuantity, LastPrice, LastQuantity, ... }

Ich habe versucht, einen Wrapper für diese API zu erstellen, da das Brechen von Änderungen die Lebensweise dieser API ist. Ich wollte den Wert jedes zuletzt empfangenen Tick-Typs auf meinem Wrapper verfolgen und habe dazu ein Wörterbuch mit Ticktypen verwendet:

Dictionary<TickType,double> LastValues

Für mich schien dies die richtige Verwendung einer Aufzählung zu sein, wenn sie als Schlüssel verwendet werden. Aber ich habe Bedenken, weil ich einen Ort habe, an dem ich basierend auf dieser Sammlung eine Entscheidung treffen kann, und ich kann mir keine Möglichkeit vorstellen, wie ich die switch-Anweisung beseitigen könnte. Ich könnte eine Factory verwenden, aber diese Factory wird immer noch eine haben switch-Anweisung irgendwo. Es schien mir, dass ich nur Dinge bewege, aber es riecht immer noch.

Es ist einfach, das NICHT von Aufzählungen zu finden, aber die DOs, nicht so einfach, und ich würde es begrüßen, wenn die Leute ihre Fachkenntnisse, die Vor- und Nachteile teilen können.

Bedenken

Einige Entscheidungen und Aktionen basieren auf diesen TickTypeund ich kann mir keine Möglichkeit vorstellen, Enum- / Switch-Anweisungen zu eliminieren. Die sauberste Lösung, die ich mir vorstellen kann, ist die Verwendung einer Factory und die Rückgabe einer Implementierung auf der Basis TickType. Selbst dann habe ich noch eine switch-Anweisung, die eine Implementierung einer Schnittstelle zurückgibt.

Im Folgenden ist eine der Beispielklassen aufgeführt, in denen ich Zweifel habe, dass ich möglicherweise eine falsche Aufzählung verwende:

public class ExecutionSimulator
{
  Dictionary<TickType, double> LastReceived;
  void ProcessTick(TickType tickType, double value)
  {
    //Store Last Received TickType value
    LastReceived[tickType] = value;

    //Perform Order matching only on specific TickTypes
    switch(tickType)
    {
      case BidPrice:
      case BidSize:
        MatchSellOrders();
        break;
      case AskPrice:
      case AskSize:
        MatchBuyOrders();
        break;
    }       
  }
}
stromms
quelle
25
Ich habe noch nie davon gehört, dass Enums ein Codegeruch sind. Könnten Sie eine Referenz hinzufügen? Ich denke, dass sie für eine begrenzte Anzahl potenzieller Werte sehr sinnvoll sind
Richard Tingle
23
Welche Bücher sagen, dass Aufzählungen ein Codegeruch sind? Holen Sie sich bessere Bücher.
JacquesB
3
Die Antwort auf die Frage wäre also "die meiste Zeit".
gnasher729
5
Ein schöner, typsicherer Wert, der Sinn vermittelt? Das garantiert, dass die richtigen Schlüssel in einem Wörterbuch verwendet werden? Wann ist es ein Codegeruch?
7
Ohne Kontext, denke ich, wäre eine bessere Formulierungenums as switch statements might be a code smell ...
pllee 16.10.15

Antworten:

31

Aufzählungen sind für Anwendungsfälle gedacht, in denen Sie buchstäblich jeden möglichen Wert aufgelistet haben, den eine Variable annehmen könnte. Je. Denken Sie an Anwendungsfälle wie Wochentage oder Monate des Jahres oder Konfigurationswerte eines Hardware-Registers. Dinge, die sehr stabil sind und sich durch einen einfachen Wert darstellen lassen.

Denken Sie daran, wenn Sie eine Anti-Korruptions-Schicht erstellen, können Sie es nicht vermeiden, irgendwo eine switch-Anweisung zu haben , da Sie das Design umschließen, aber wenn Sie es richtig machen, können Sie es auf diesen einen Ort beschränken und Verwenden Sie Polymorphismus an anderer Stelle.

Karl Bielefeldt
quelle
3
Dies ist der wichtigste Punkt - das Hinzufügen eines zusätzlichen Werts zu einer Aufzählung bedeutet, dass Sie jede Verwendung des Typs in Ihrem Code finden und möglicherweise einen neuen Zweig für den neuen Wert hinzufügen müssen. Vergleichen Sie dies mit einem polymorphen Typ. Wenn Sie eine neue Möglichkeit hinzufügen, müssen Sie lediglich eine neue Klasse erstellen, die die Schnittstelle implementiert. Die Änderungen werden an einer einzigen Stelle konzentriert, sodass sie einfacher durchzuführen sind. Wenn Sie sicher sind, dass niemals ein neuer Tick-Typ hinzugefügt wird, ist die Aufzählung in Ordnung, andernfalls sollte sie in eine polymorphe Schnittstelle eingeschlossen werden.
Jules
Das ist eine der Antworten, nach denen ich gesucht habe. Ich kann es einfach nicht vermeiden, wenn die Methode, die ich einpacke, eine Aufzählung verwendet und das Ziel darin besteht, diese Aufzählung an einem Ort zu zentralisieren. Ich muss den Wrapper so gestalten, dass ich immer dann, wenn die API diese Aufzählungen ändert, nur den Wrapper und nicht die Konsumenten des Wrappers ändern muss.
Stromms
@Jules - "Wenn Sie sicher sind, dass niemals ein neuer Tick-Typ hinzugefügt wird ..." Diese Aussage ist auf so vielen Ebenen falsch. Eine Klasse für jeden einzelnen Typ, obwohl jeder Typ genau dasselbe Verhalten aufweist, ist ungefähr so ​​weit von einem "vernünftigen" Ansatz entfernt, wie man ihn nur bekommen kann. Es dauert nicht lange, bis Sie ein anderes Verhalten haben und anfangen, "switch / if-then" -Anweisungen zu benötigen, bei denen die Linie gezeichnet werden muss. Es hängt sicherlich nicht davon ab, ob ich in Zukunft weitere Aufzählungswerte hinzufügen werde oder nicht.
Dunk
@dunk Ich denke du hast meinen Kommentar falsch verstanden. Ich habe nie vorgeschlagen, mehrere Typen mit identischem Verhalten zu erstellen - das wäre verrückt.
Jules
1
Selbst wenn Sie "jeden möglichen Wert aufgezählt haben", bedeutet dies nicht, dass der Typ niemals zusätzliche Funktionen pro Instanz hat. Sogar so etwas wie Month kann andere nützliche Eigenschaften haben, wie die Anzahl der Tage. Durch die sofortige Verwendung einer Aufzählung wird verhindert, dass das von Ihnen modellierte Konzept erweitert wird. Es ist im Grunde ein Anti-OOP-Mechanismus / Muster.
Dave Cousineau
17

Erstens bedeutet ein Codegeruch nicht, dass etwas nicht stimmt. Es bedeutet, dass etwas nicht stimmt. enumriecht, weil es häufig missbraucht wird, aber das heißt nicht, dass man sie meiden muss. Sie müssen nur noch tippen enum, anhalten und nach besseren Lösungen suchen .

Der besondere Fall, der am häufigsten auftritt, ist, wenn die verschiedenen Aufzählungen verschiedenen Typen mit unterschiedlichem Verhalten, aber derselben Schnittstelle entsprechen. Zum Beispiel mit verschiedenen Backends sprechen, verschiedene Seiten rendern usw. Diese werden viel natürlicher mit polymorphen Klassen implementiert.

In Ihrem Fall entspricht der TickType nicht unterschiedlichen Verhaltensweisen. Dies sind verschiedene Arten von Ereignissen oder verschiedene Eigenschaften des aktuellen Zustands. Ich denke, dies ist der ideale Ort für eine Aufzählung.

Winston Ewert
quelle
Wollen Sie damit sagen, dass Enums, die Substantive als Einträge enthalten (z. B. Farben), wahrscheinlich korrekt verwendet werden. Und wenn es sich um Verben handelt (Verbunden, Rendern), ist dies ein Zeichen dafür, dass sie missbraucht werden?
Stromms
2
@ Stromms, Grammatik ist ein schrecklicher Leitfaden für die Software-Architektur. Wenn TickType eine Schnittstelle anstelle einer Aufzählung wäre, welche Methoden wären das? Ich kann keine sehen, deshalb scheint es mir ein enum Fall zu sein. In anderen Fällen, wie Status, Farben, Backend usw., kann ich mir Methoden ausdenken, und deshalb sind sie Schnittstellen.
Winston Ewert
@WinstonEwert und mit dem bearbeiten, scheint es , dass sie sind unterschiedliche Verhaltensweisen.
Oh. Also, wenn ich mir eine Methode für eine Aufzählung vorstellen kann, dann könnte es ein Kandidat für eine Schnittstelle sein? Das könnte ein sehr guter Punkt sein.
Stromms
1
@stromms, können Sie weitere Beispiele für Schalter teilen, damit ich eine bessere Vorstellung davon bekomme, wie Sie TickType verwenden?
Winston Ewert
8

Bei der Übertragung von Daten sind Aufzählungen kein Codegeruch

IMHO, wenn Daten übertragen werden, enumsum anzuzeigen, dass ein Feld einen Wert aus einem eingeschränkten (sich selten ändernden) Satz von Werten haben kann, ist dies gut.

Ich halte es für vorzuziehen, willkürliche stringsoder ints. Zeichenfolgen können durch unterschiedliche Schreibweise und Großschreibung Probleme verursachen. Ints ermöglichen Bereichswerte zu übertragen und haben wenig Semantik (zB erhalte ich 3von Ihrem Trading - Service, was ist das? LastPrice? LastQuantity? Etwas anderes?

Das Übertragen von Objekten und die Verwendung von Klassenhierarchien ist nicht immer möglich. Beispielsweise es dem empfangenden Ende nicht, zu unterscheiden, welche Klasse gesendet wurde.

In meinem Projekt verwendet der Dienst eine Klassenhierarchie für die Auswirkungen von Operationen, kurz bevor DataContractdas Objekt über die Klassenhierarchie übertragen wird, wird es in ein unionähnliches Objekt kopiert, das ein enthält enum, um den Typ anzugeben. Der Client empfängt das DataContractObjekt und erstellt ein Objekt einer Klasse in einer Hierarchie. Dabei wird der enumWert verwendet, switchum ein Objekt des richtigen Typs zu erstellen.

Ein weiterer Grund, warum man keine Klassenobjekte übertragen möchte, ist, dass der Dienst möglicherweise ein völlig anderes Verhalten für ein übertragenes Objekt (z. B. LastPrice) als den Client erfordert . In diesem Fall ist das Senden der Klasse und ihrer Methoden unerwünscht.

Sind switch-Anweisungen schlecht?

IMHO, eine Single- switch Anweisung, die abhängig von einem unterschiedliche Konstruktoren aufruft, enumist kein Code-Geruch. Es ist nicht unbedingt besser oder schlechter als andere Methoden, z. B. die Reflexion anhand eines Typnamens. Dies hängt von der tatsächlichen Situation ab.

Schalter enumüberall ist ein Codegeruch, bietet Alternativen, die oft besser sind:

  • Verwenden Sie Objekte verschiedener Klassen einer Hierarchie, die über überschriebene Methoden verfügen. dh Polymorphismus.
  • Verwenden Sie das Besuchermuster, wenn sich die Klassen in der Hierarchie selten ändern und wenn die (vielen) Operationen lose mit den Klassen in der Hierarchie gekoppelt werden sollen.
Kasper van den Berg
quelle
Tatsächlich wird TickType durch die Leitung als übertragen int. Mein Wrapper ist derjenige, der verwendet, TickTypeder aus dem empfangenen gegossen wird int. Bei mehreren Ereignissen wird dieser Tick-Typ mit unterschiedlichen Signaturen verwendet, die auf verschiedene Anforderungen reagieren. Ist es gängige Praxis, eine intständig für verschiedene Funktionen zu nutzen? TickPrice(int type, double value)Verwendet zB 1,3 und 6, typewährend TickSize(int type, double value2,4 und 5 verwendet werden? Ist es überhaupt sinnvoll, diese in zwei Ereignisse zu unterteilen?
Stromms
2

Was wäre, wenn Sie sich für einen komplexeren Typ entscheiden würden:

    abstract class TickType
    {
      public abstract string Name {get;}
      public abstract double TickValue {get;}
    }

    class BuyPrice : TickType
    {
      public override string Name { get { return "Buy Price"; } }
      public override double TickValue { get { return 2.35d; } }
    }

    class BuyQuantity : TickType
    {
      public override string Name { get { return "Buy Quantity"; } }
      public override double TickValue { get { return 4.55d; } }
    }
//etcetera

Dann könnten Sie Ihre Typen aus Reflection laden oder selbst erstellen, aber das Wichtigste dabei ist, dass Sie sich an das Open-Close-Prinzip von SOLID halten

Chris
quelle
1

TL; DR

Um die Frage zu beantworten, fällt es mir jetzt schwer, mir vorzustellen, dass Aufzählungen auf irgendeiner Ebene kein Codegeruch sind . Es gibt eine bestimmte Absicht, die sie effizient erklären (es gibt eine klar begrenzte, begrenzte Anzahl von Möglichkeiten für diesen Wert), aber ihre inhärent geschlossene Natur macht sie architektonisch minderwertig.

Entschuldigen Sie, dass ich Tonnen meines Legacy-Codes umgestalte. / seufz; ^ D


Aber wieso?

Ziemlich guter Rückblick, warum das bei LosTechies so ist :

// calculate the service fee
public double CalculateServiceFeeUsingEnum(Account acct)
{
    double totalFee = 0;
    foreach (var service in acct.ServiceEnums) { 

        switch (service)
        {
            case ServiceTypeEnum.ServiceA:
                totalFee += acct.NumOfUsers * 5;
                break;
            case ServiceTypeEnum.ServiceB:
                totalFee += 10;
                break;
        }
    }
    return totalFee;
} 

Dies hat alle die gleichen Probleme wie der obige Code. Je größer die Anwendung wird, desto höher ist die Wahrscheinlichkeit, dass ähnliche Branch-Anweisungen vorliegen. Auch wenn Sie mehr Premium-Services einführen, müssen Sie diesen Code kontinuierlich ändern, was gegen das Open-Closed-Prinzip verstößt. Auch hier gibt es andere Probleme. Die Funktion zur Berechnung der Servicegebühr muss nicht die tatsächlichen Beträge der einzelnen Services kennen. Das sind Informationen, die gekapselt werden müssen.

Ein kleiner Unterschied: Aufzählungen sind eine sehr begrenzte Datenstruktur. Wenn Sie eine Aufzählung nicht für das verwenden, was sie wirklich ist, eine beschriftete Ganzzahl, benötigen Sie eine Klasse, um die Abstraktion wirklich richtig zu modellieren. Sie können Jimmys fantastische Enumeration-Klasse verwenden, um Klassen zu verwenden, um sie auch als Beschriftungen zu verwenden.

Lassen Sie uns dies umgestalten, um polymorphes Verhalten zu verwenden. Was wir brauchen, ist eine Abstraktion, die es uns ermöglicht, das Verhalten einzudämmen, das zur Berechnung der Gebühr für eine Dienstleistung erforderlich ist.

public interface ICalculateServiceFee
{
    double CalculateServiceFee(Account acct);
}

...

Jetzt können wir unsere konkreten Implementierungen der Schnittstelle erstellen und sie dem Konto hinzufügen.

public class Account{
    public int NumOfUsers{get;set;}
    public ICalculateServiceFee[] Services { get; set; }
} 

public class ServiceA : ICalculateServiceFee
{
    double feePerUser = 5; 

    public double CalculateServiceFee(Account acct)
    {
        return acct.NumOfUsers * feePerUser;
    }
} 

public class ServiceB : ICalculateServiceFee
{
    double serviceFee = 10;
    public double CalculateServiceFee(Account acct)
    {
        return serviceFee;
    }
} 

Ein weiterer Implementierungsfall ...

Wenn Sie ein Verhalten haben, das von den Enum-Werten abhängt, warum sollten Sie nicht stattdessen unterschiedliche Implementierungen einer ähnlichen Schnittstelle oder einer übergeordneten Klasse verwenden, die sicherstellen, dass dieser Wert vorhanden ist? In meinem Fall werden basierend auf den REST-Statuscodes verschiedene Fehlermeldungen angezeigt. Anstatt von...

private static string _getErrorCKey(int statusCode)
{
    string ret;

    switch (statusCode)
    {
        case StatusCodes.Status403Forbidden:
            ret = "BRANCH_UNAUTHORIZED";
            break;

        case StatusCodes.Status422UnprocessableEntity:
            ret = "BRANCH_NOT_FOUND";
            break;

        default:
            ret = "BRANCH_GENERIC_ERROR";
            break;
    }

    return ret;
}

... vielleicht sollte ich Statuscodes in Klassen einbinden.

public interface IAmStatusResult
{
    int StatusResult { get; }    // Pretend an int's okay for now.
    string ErrorKey { get; }
}

Jedes Mal, wenn ich einen neuen IAmStatusResult-Typ benötige, verschlüssele ich ihn ...

public class UnauthorizedBranchStatusResult : IAmStatusResult
{
    public int StatusResult => 403;
    public string ErrorKey => "BRANCH_UNAUTHORIZED";
}

... und jetzt kann ich sicherstellen, dass der frühere Code erkennt, dass er einen eingeschränkten IAmStatusResultGeltungsbereich hat, und auf seinen verweisen, entity.ErrorKeyanstatt auf den komplexeren, tödlicheren _getErrorCode(403).

Und was noch wichtiger ist: Jedes Mal , wenn ich einen neuen Typ von Rückgabewert hinzufüge, muss kein weiterer Code hinzugefügt werden, um damit umzugehen . Was weißt du, die enumund switchwaren wahrscheinlich Code-Gerüche.

Profitieren.

Ruffin
quelle
Wie wäre es mit dem Fall, in dem ich beispielsweise polymorphe Klassen habe und konfigurieren möchte, welche ich über einen Befehlszeilenparameter verwende? Befehlszeile -> enum -> switch / map -> Implementierung scheint ein recht legitimer Anwendungsfall zu sein. Ich denke, das Wichtigste ist, dass die Aufzählung für die Orchestrierung verwendet wird, nicht als Implementierung der bedingten Logik.
Ant P
@AntP Verwenden Sie in diesem Fall den StatusResultWert. Sie könnten argumentieren, dass die Aufzählung in diesem Anwendungsfall nützlich ist, aber ich würde das wahrscheinlich immer noch als Codegeruch bezeichnen, da es gute Alternativen gibt, für die die geschlossene Auflistung nicht erforderlich ist.
Ruffin
Welche Alternativen? Ein Faden? Dies ist eine willkürliche Unterscheidung aus der Perspektive von "Aufzählungen sollten durch Polymorphismus ersetzt werden". Bei beiden Ansätzen muss ein Wert einem Typ zugeordnet werden. Das Vermeiden der Aufzählung bringt in diesem Fall nichts.
Ant P
@AntP Ich bin mir nicht sicher, wo sich die Drähte hier kreuzen. Wenn Sie auf eine Eigenschaft in einer Schnittstelle verweisen, können Sie diese Schnittstelle überall dort verwenden, wo sie benötigt wird, und diesen Code niemals aktualisieren, wenn Sie eine neue Implementierung dieser Schnittstelle erstellen (oder eine vorhandene entfernen) . Wenn Sie eine haben enum, Sie tun müssen , um Update - Code jedes Mal , wenn Sie einen Wert hinzuzufügen oder zu entfernen, und möglicherweise viele Orte, je nachdem , wie Sie Ihren Code strukturiert. Die Verwendung von Polymorphismus anstelle einer Aufzählung entspricht der Sicherstellung, dass Ihr Code in Boyce-Codd-Normalform vorliegt .
Ruffin
Ja. Angenommen, Sie haben N solche polymorphen Implementierungen. In dem Artikel von Los Techies werden sie einfach alle durchlaufen. Was aber, wenn ich nur eine Implementierung bedingt anwenden möchte, die z. B. auf Befehlszeilenparametern basiert? Jetzt müssen wir eine Zuordnung von einem Konfigurationswert zu einem injektionsfähigen Implementierungstyp definieren. Eine Aufzählung ist in diesem Fall so gut wie jeder andere Konfigurationstyp. Dies ist ein Beispiel für eine Situation, in der es keine weitere Möglichkeit gibt, das "Dirty Enum" durch Polymorphismus zu ersetzen. Verzweigen durch Abstraktion kann Sie nur so weit bringen.
Ant P
0

Ob die Verwendung einer Aufzählung ein Codegeruch ist oder nicht, hängt vom Kontext ab. Ich denke, Sie können einige Ideen für die Beantwortung Ihrer Frage erhalten, wenn Sie das Ausdrucksproblem betrachten . Sie verfügen also über eine Sammlung verschiedener Typen und Operationen, und Sie müssen Ihren Code organisieren. Es gibt zwei einfache Möglichkeiten:

  • Ordnen Sie den Code gemäß den Vorgängen. In diesem Fall können Sie mithilfe einer Enumeration verschiedene Typen markieren und in jeder Prozedur, die die markierten Daten verwendet, eine switch-Anweisung verwenden.
  • Ordnen Sie den Code nach den Datentypen. In diesem Fall können Sie die Aufzählung durch eine Schnittstelle ersetzen und für jedes Element der Aufzählung eine Klasse verwenden. Anschließend implementieren Sie jede Operation als Methode in jeder Klasse.

Welche Lösung ist besser?

Wenn, wie Karl Bielefeldt betonte, Ihre Typen festgelegt sind und Sie erwarten, dass das System hauptsächlich durch Hinzufügen neuer Operationen für diese Typen wächst, ist die Verwendung einer Aufzählung und einer switch-Anweisung eine bessere Lösung: Jedes Mal, wenn Sie eine neue Operation benötigen, müssen Sie Implementieren Sie einfach eine neue Prozedur, während Sie bei Verwendung von Klassen jeder Klasse eine Methode hinzufügen müssten.

Auf der anderen Seite ist es bequemer, eine objektorientierte Lösung zu verwenden, wenn Sie erwarten, dass Sie einen ziemlich stabilen Betrieb haben, aber im Laufe der Zeit mehr Datentypen hinzufügen müssen: Da neue Datentypen implementiert werden müssen, müssen Sie nur Fügen Sie immer neue Klassen hinzu, die dieselbe Schnittstelle implementieren. Wenn Sie jedoch eine Aufzählung verwenden, müssen Sie alle switch-Anweisungen in allen Prozeduren mit der Aufzählung aktualisieren.

Wenn Sie Ihr Problem nicht in eine der beiden oben genannten Optionen einordnen können, können Sie sich anspruchsvollere Lösungen ansehen (siehe z. B. die oben zitierte Wikipedia-Seite für eine kurze Diskussion und eine Referenz zur weiteren Lektüre).

Versuchen Sie also zu verstehen, in welche Richtung sich Ihre Anwendung entwickeln kann, und wählen Sie dann eine geeignete Lösung aus.

Da sich die Bücher, auf die Sie sich beziehen, mit dem objektorientierten Paradigma befassen, ist es nicht verwunderlich, dass sie gegen die Verwendung von Aufzählungen voreingenommen sind. Eine objektorientierte Lösung ist jedoch nicht immer die beste Option.

Fazit: Aufzählungen sind nicht unbedingt ein Codegeruch.

Giorgio
quelle
Beachten Sie, dass die Frage im Kontext von C # gestellt wurde, einer OOP-Sprache. Auch, dass die Gruppierung nach Operation oder Typ zwei Seiten derselben Medaille sind, ist interessant, aber ich sehe eine nicht als "besser" als die andere, wie Sie beschreiben. Die resultierende Codemenge ist die gleiche und OOP-Sprachen erleichtern bereits die Organisation nach Typ. Es erzeugt auch wesentlich intuitiveren (einfach zu lesenden) Code.
Dave Cousineau
@ DaveCousineau: "Ich sehe einen nicht als" besser "als den anderen, wie Sie beschreiben": Ich habe nicht gesagt, dass einer immer besser ist als der andere. Ich sagte, dass je nach Kontext eines besser ist als das andere. Zum Beispiel , wenn der Betrieb mehr oder weniger festgelegt und Sie planen , neue Typen auf Hinzufügen (zB ein GUI mit festen paint(), show(), close() resize()Operationen und eigene Widgets) , dann die objektorientierte Ansatz ist besser in dem Sinne , dass es auch einen neuen Typ hinzufügen können ohne Beeinträchtigung viel vorhandenen Code (Sie implementieren im Grunde eine neue Klasse, die eine lokale Änderung ist).
Giorgio
Ich sehe auch nicht als "besser", wie Sie beschrieben haben, was bedeutet, dass ich nicht sehe, dass es von der Situation abhängt. Die Menge an Code, die Sie schreiben müssen, ist in beiden Szenarien genau gleich. Der einzige Unterschied ist, dass ein Weg OOP (enums) widerspricht und einer nicht.
Dave Cousineau
@ DaveCousineau: Die Menge an Code, die Sie schreiben müssen, ist wahrscheinlich die gleiche. Die Lokalität (Stellen im Quellcode, die Sie ändern und neu kompilieren müssen) ändert sich drastisch. Wenn Sie beispielsweise in OOP einer Schnittstelle eine neue Methode hinzufügen, müssen Sie für jede Klasse, die diese Schnittstelle implementiert, eine Implementierung hinzufügen.
Giorgio
Geht es nicht nur um Breite und Tiefe? Hinzufügen von 1 Methode zu 10 Dateien oder 10 Methoden zu 1 Datei.
Dave Cousineau