Zu viel Abstraktion macht es schwierig, Code zu erweitern

9

Ich habe Probleme mit dem, was ich für zu viel Abstraktion in der Codebasis halte (oder zumindest damit zu tun habe). Die meisten Methoden in der Codebasis wurden abstrahiert, um das höchste übergeordnete Element A in der Codebasis aufzunehmen, aber das untergeordnete Element B dieses übergeordneten Elements verfügt über ein neues Attribut, das die Logik einiger dieser Methoden beeinflusst. Das Problem ist, dass diese Attribute in diesen Methoden nicht überprüft werden können, da die Eingabe nach A abstrahiert ist und A dieses Attribut natürlich nicht hat. Wenn ich versuche, eine neue Methode zu erstellen, um B anders zu behandeln, wird sie zur Codeduplizierung aufgerufen. Der Vorschlag meines technischen Leiters besteht darin, eine gemeinsam genutzte Methode zu erstellen, die boolesche Parameter berücksichtigt. Das Problem dabei ist jedoch, dass einige Leute dies als "versteckten Kontrollfluss" betrachten, bei dem die gemeinsam genutzte Methode eine Logik aufweist, die für zukünftige Entwickler möglicherweise nicht erkennbar ist , Außerdem wird diese gemeinsam genutzte Methode einmal zu komplex / kompliziert, wenn zukünftige Attribute hinzugefügt werden müssen, selbst wenn sie in kleinere gemeinsam genutzte Methoden unterteilt ist. Dies erhöht auch die Kopplung, verringert den Zusammenhalt und verstößt gegen das Prinzip der Einzelverantwortung, auf das jemand in meinem Team hingewiesen hat.

Im Wesentlichen hilft ein Großteil der Abstraktion in dieser Codebasis dabei, die Codeduplizierung zu reduzieren, aber es erschwert das Erweitern / Ändern von Methoden, wenn sie die höchste Abstraktion annehmen. Was soll ich in einer solchen Situation tun? Ich bin im Zentrum der Schuld, obwohl sich alle anderen nicht darauf einigen können, was sie für gut halten, also tut es mir am Ende weh.

YamizGers
quelle
10
Fügen Sie ein Codebeispiel hinzu, um das "" Problem "" zu Analphabeten zu
erklären. Dies
Ich denke, hier sind zwei SOLID-Prinzipien gebrochen. Einzelverantwortung - Wenn Sie einen Booleschen Wert an eine Funktion übergeben, die das Verhalten steuern soll, hat die Funktion keine Einzelverantwortung mehr. Das andere ist das Liskov-Substitutionsprinzip. Stellen Sie sich vor, es gibt eine Funktion, die Klasse A als Parameter akzeptiert. Wenn Sie Klasse B anstelle von A bestehen, wird die Funktionalität dieser Funktion beeinträchtigt?
Bobek
Ich vermute, Methode A ist ziemlich lang und macht mehr als eine Sache. Ist das der Fall?
Rad80

Antworten:

27

Wenn ich versuche, eine neue Methode zu erstellen, um B anders zu behandeln, wird sie zur Codeduplizierung aufgerufen.

Nicht alle Codeduplikationen sind gleich.

Angenommen, Sie haben eine Methode, die zwei Parameter verwendet, die aufgerufen werden total(). Angenommen, Sie haben einen anderen angerufen add(). Ihre Implementierungen sehen völlig identisch aus. Sollten sie zu einer Methode zusammengeführt werden? NEIN!!!

Beim Don't-Repeat-Yourself- oder DRY-Prinzip geht es nicht darum, Code zu wiederholen. Es geht darum, eine Entscheidung, eine Idee, zu verbreiten, damit Sie, wenn Sie Ihre Idee jemals ändern, sie überall neu schreiben müssen, wo Sie diese Idee verbreiten. Blegh. Das ist schrecklich. Tu es nicht. Verwenden Sie stattdessen DRY, um Entscheidungen an einem Ort zu treffen .

Das DRY-Prinzip (Wiederholen Sie sich nicht) besagt:

Jedes Wissen muss eine einzige, eindeutige und maßgebliche Darstellung innerhalb eines Systems haben.

wiki.c2.com - Wiederholen Sie sich nicht

DRY kann jedoch zur Gewohnheit werden, Code nach einer ähnlichen Implementierung zu durchsuchen, die wie das Kopieren und Einfügen von einem anderen Ort aussieht. Dies ist die hirntote Form von DRY. Zum Teufel, Sie könnten dies mit einem statischen Analysewerkzeug tun. Es hilft nicht, weil es den Punkt von DRY ignoriert, der darin besteht, den Code flexibel zu halten.

Wenn sich meine Gesamtanforderungen ändern, muss ich möglicherweise meine totalImplementierung ändern . Das bedeutet nicht, dass ich meine addImplementierung ändern muss . Wenn ein Goober sie zu einer Methode zusammengefügt hat, habe ich jetzt ein bisschen unnötigen Schmerz.

Wie viel Schmerz? Sicherlich könnte ich den Code einfach kopieren und eine neue Methode erstellen, wenn ich sie brauche. Also keine große Sache, oder? Malarky! Wenn nichts anderes kostet dich mich einen guten Namen! Gute Namen sind schwer zu finden und reagieren nicht gut, wenn Sie an ihrer Bedeutung herumspielen. Gute Namen, die die Absicht klar machen, sind wichtiger als das Risiko, dass Sie einen Fehler kopiert haben, der offen gesagt leichter zu beheben ist, wenn Ihre Methode den richtigen Namen hat.

Mein Rat ist also, aufhören zu lassen, dass Knie-Ruck-Reaktionen auf ähnlichen Code Ihre Codebasis in Knoten binden. Ich sage nicht, dass Sie die Tatsache, dass Methoden existieren, ignorieren und stattdessen wohl oder übel kopieren und einfügen können. Nein, jede Methode sollte einen verdammt guten Namen haben, der die eine Idee unterstützt, um die es geht. Wenn die Implementierung mit der Implementierung einer anderen guten Idee übereinstimmt, wen interessiert das heute?

Auf der anderen Seite, wenn Sie eine sum()Methode haben, die eine identische oder sogar andere Implementierung hat als total()Ihre, aber immer wenn sich Ihre Gesamtanforderungen ändern, müssen Sie diese ändern, sum()dann besteht eine gute Chance, dass dies dieselbe Idee unter zwei verschiedenen Namen ist. Der Code wäre nicht nur flexibler, wenn er zusammengeführt würde, es wäre auch weniger verwirrend, ihn zu verwenden.

Was boolesche Parameter betrifft, ja, das ist ein übler Codegeruch. Dieses Kontrollflussmaterial ist nicht nur ein Problem, es zeigt auch, dass Sie eine Abstraktion an einem schlechten Punkt eingeschnitten haben. Abstraktionen sollen die Verwendung einfacher und nicht komplizierter machen. Das Übergeben von Bools an eine Methode zur Steuerung ihres Verhaltens ist wie das Erstellen einer geheimen Sprache, die entscheidet, welche Methode Sie wirklich aufrufen. Au! Tu mir das nicht an. Geben Sie jeder Methode ihren eigenen Namen, es sei denn, Sie haben einen ehrlichen Polymorphismus .

Jetzt scheinen Sie von der Abstraktion ausgebrannt zu sein. Das ist schade, denn Abstraktion ist eine wunderbare Sache, wenn sie gut gemacht wird. Sie benutzen es oft, ohne darüber nachzudenken. Jedes Mal, wenn Sie ein Auto fahren, ohne das Zahnstangensystem verstehen zu müssen, jedes Mal, wenn Sie einen Druckbefehl verwenden, ohne an Betriebssystemunterbrechungen zu denken, und jedes Mal, wenn Sie Ihre Zähne putzen, ohne an jede einzelne Borste zu denken.

Nein, das Problem, mit dem Sie anscheinend konfrontiert sind, ist eine schlechte Abstraktion. Abstraktion, die geschaffen wurde, um einem anderen Zweck als Ihren Bedürfnissen zu dienen. Sie benötigen einfache Schnittstellen zu komplexen Objekten, mit denen Sie die Erfüllung Ihrer Anforderungen anfordern können, ohne diese Objekte jemals verstehen zu müssen.

Wenn Sie Client-Code schreiben, der ein anderes Objekt verwendet, wissen Sie, welche Anforderungen Sie haben und was Sie von diesem Objekt benötigen. Das tut es nicht. Aus diesem Grund besitzt Client-Code die Schnittstelle. Wenn Sie der Kunde sind, kann Ihnen nichts sagen, was Sie brauchen, außer Sie. Sie stellen eine Schnittstelle zur Verfügung, die Ihre Anforderungen zeigt, und fordern, dass alles, was Ihnen übergeben wird, diese Anforderungen erfüllt.

Das ist Abstraktion. Als Kunde weiß ich nicht einmal, womit ich spreche. Ich weiß nur, was ich davon brauche. Wenn das bedeutet, dass Sie etwas einpacken müssen, um die Benutzeroberfläche zu ändern, bevor Sie es mir übergeben. Es ist mir egal. Tu einfach was ich tun muss. Hör auf, es kompliziert zu machen.

Wenn ich in eine Abstraktion schauen muss, um zu verstehen, wie man sie verwendet, ist die Abstraktion fehlgeschlagen. Ich sollte nicht wissen müssen, wie es funktioniert. Nur dass es funktioniert. Gib ihm einen guten Namen und wenn ich hineinschaue, sollte mich das, was ich finde, nicht überraschen. Lassen Sie mich nicht weiter nach innen schauen, um mich daran zu erinnern, wie man es benutzt.

Wenn Sie darauf bestehen, dass die Abstraktion auf diese Weise funktioniert, spielt die Anzahl der dahinter stehenden Ebenen keine Rolle. Solange Sie nicht hinter die Abstraktion schauen. Sie bestehen darauf, dass die Abstraktion Ihren Bedürfnissen entspricht und sich nicht an ihre anpasst. Damit dies funktioniert, muss es einfach zu bedienen sein, einen guten Namen haben und nicht auslaufen .

Das ist die Einstellung, die Dependency Injection hervorgebracht hat (oder nur Referenzübergabe, wenn Sie wie ich in der alten Schule sind). Es funktioniert gut, wenn Komposition und Delegierung der Vererbung vorgezogen werden . Die Einstellung hat viele Namen. Mein Favorit ist erzählen, nicht fragen .

Ich könnte dich den ganzen Tag in Prinzipien ertränken. Und es hört sich so an, als ob Ihre Mitarbeiter es bereits sind. Aber hier ist die Sache: Im Gegensatz zu anderen technischen Bereichen ist diese Software-Sache weniger als 100 Jahre alt. Wir alle finden es immer noch heraus. Lassen Sie sich also nicht von jemandem mit viel einschüchterndem Hörbuch dazu zwingen, schwer lesbaren Code zu schreiben. Hören Sie ihnen zu, aber bestehen Sie darauf, dass sie Sinn ergeben. Nimm nichts vom Glauben. Leute, die auf irgendeine Weise codieren, nur weil ihnen gesagt wurde, dass dies der Weg ist, ohne zu wissen, warum sie die größten Probleme von allen machen.

candied_orange
quelle
Ich stimme voll und ganz zu. DRY ist ein Drei-Buchstaben-Akronym für das Drei-Wörter-Schlagwort Don't Repeat Yourself, das wiederum ein 14-seitiger Artikel im Wiki ist . Wenn alles , was Sie blind zu tun ist , um diese drei Buchstaben ohne Lesen und Verstehen der 14 - seitigen Artikel murmelnd, Sie werden in Schwierigkeiten geraten. Es ist auch eng verwandt mit Once And Only Once (OAOO) und lockerer verwandt mit Single Point Of Truth (SPOT) / Single Source Of Truth (SSOT) .
Jörg W Mittag
"Ihre Implementierungen sehen völlig identisch aus. Sollten sie zu einer Methode zusammengeführt werden? NEIN !!!" - Das Gegenteil ist auch der Fall: Nur weil zwei Codeteile unterschiedlich sind, heißt das nicht, dass sie keine Duplikate sind. Es gibt ein großartiges Zitat von Ron Jeffries auf der OAOO-Wiki-Seite : "Ich habe einmal gesehen, wie Beck zwei Patches mit fast völlig unterschiedlichem Code als" Duplizierung "deklariert hat, sie so geändert hat, dass sie Duplizierung waren, und dann die neu eingefügte Duplizierung entfernt, um sie aufzurufen mit etwas offensichtlich Besserem. "
Jörg W Mittag
@ JörgWMittag natürlich. Das Wesentliche ist die Idee. Wenn Sie die Idee mit unterschiedlich aussehendem Code duplizieren, verletzen Sie immer noch trocken.
candied_orange
Ich muss mir vorstellen, dass ein 14-seitiger Artikel darüber, dass man sich nicht wiederholt, sich oft wiederholt.
Chuck Adams
7

Das übliche Sprichwort, das wir alle hier und da lesen, lautet:

Alle Probleme können durch Hinzufügen einer weiteren Abstraktionsebene gelöst werden.

Nun, das ist nicht wahr! Ihr Beispiel zeigt es. Ich würde daher die leicht modifizierte Aussage vorschlagen (zögern Sie nicht, sie wiederzuverwenden ;-)):

Jedes Problem kann mit der richtigen Abstraktionsebene gelöst werden.

In Ihrem Fall gibt es zwei verschiedene Probleme:

  • die Überverallgemeinerung, die durch das Hinzufügen jeder Methode auf abstrakter Ebene verursacht wird;
  • die Fragmentierung der konkreten Verhaltensweisen, die den Eindruck erwecken, nicht den Überblick zu behalten und sich verloren zu fühlen. Ein bisschen wie in einer Windows-Ereignisschleife.

Beide sind miteinander verbunden:

  • Wenn Sie eine Methode abstrahieren, bei der jede Spezialisierung anders vorgeht, ist alles in Ordnung. Niemand hat ein Problem damit, zu verstehen, dass Shapeman es surface()auf spezielle Weise berechnen kann .
  • Wenn Sie eine Operation abstrahieren, bei der es ein allgemeines Verhaltensmuster gibt, haben Sie zwei Möglichkeiten:

    • Entweder wiederholen Sie das allgemeine Verhalten in jeder Spezialisierung: Dies ist sehr redundant. und schwer zu warten, insbesondere um sicherzustellen, dass der gemeinsame Teil über die Spezialisierungen hinweg im Einklang bleibt:
    • Sie verwenden eine Variante des Musters der Vorlagenmethode : Auf diese Weise können Sie das allgemeine Verhalten berücksichtigen, indem Sie zusätzliche abstrakte Methoden verwenden, die leicht spezialisiert werden können. Es ist weniger redundant, aber die zusätzlichen Verhaltensweisen neigen dazu, extrem gespalten zu werden. Zu viel würde bedeuten, dass es vielleicht zu abstrakt ist.

Darüber hinaus kann dieser Ansatz zu einem abstrakten Kopplungseffekt auf Entwurfsebene führen. Jedes Mal, wenn Sie ein neues spezialisiertes Verhalten hinzufügen möchten, müssen Sie es abstrahieren, das abstrakte übergeordnete Element ändern und alle anderen Klassen aktualisieren. Das ist nicht die Art von Veränderungsausbreitung , die man sich wünschen kann. Und es ist nicht wirklich im Geiste der Abstraktionen, unabhängig von der Spezialisierung (zumindest im Design).

Ich kenne Ihr Design nicht und kann nicht mehr helfen. Vielleicht ist es wirklich ein sehr komplexes und abstraktes Problem, und es gibt keinen besseren Weg. Aber wie stehen die Chancen? Die Symptome einer Übergeneralisierung sind hier. Kann es wieder ist es Zeit zu sehen sein, und betrachtet Zusammensetzung über Verallgemeinerung ?

Christophe
quelle
5

Immer wenn ich eine Methode sehe, bei der das Verhalten den Typ ihres Parameters einschaltet, überlege ich sofort, ob diese Methode tatsächlich zum Methodenparameter gehört. Zum Beispiel, anstatt eine Methode wie:

public void sort(List values) {
    if (values instanceof LinkedList) {
        // do efficient linked list sort
    } else { // ArrayList
        // do efficient array list sort
    }
}

Ich würde das tun:

values.sort();

// ...

class ArrayList {
    public void sort() {
        // do efficient array list sort
    }
}

class LinkedList {
    public void sort() {
        // do efficient linked list sort
    }
}

Wir verschieben das Verhalten an den Ort, der weiß, wann es verwendet werden soll. Wir erstellen eine echte Abstraktion, bei der Sie die Typen oder Details der Implementierung nicht kennen müssen. In Ihrer Situation ist es möglicherweise sinnvoller, diese Methode aus der ursprünglichen Klasse (die ich aufrufen werde O) zu verschieben, Aum sie einzugeben und in type zu überschreiben B. Wenn die Methode doItfür ein Objekt aufgerufen wird, wechseln Sie doItzu Adem anderen Verhalten in und überschreiben Sie es B. Wenn es Datenbits gibt, von denen doItursprünglich aufgerufen wurde, oder wenn die Methode an genügend Stellen verwendet wird, können Sie die ursprüngliche Methode verlassen und delegieren:

class O {
    int x;
    int y;

    public void doIt(A a) {
        a.doIt(this.x, this.y);
    }
}

Wir können jedoch etwas tiefer tauchen. Schauen wir uns den Vorschlag an, stattdessen einen booleschen Parameter zu verwenden, und sehen wir, was wir über die Denkweise Ihres Kollegen lernen können. Sein Vorschlag ist zu tun:

public void doIt(A a, boolean isTypeB) {
    if (isTypeB) {
        // do B stuff
    } else { 
        // do A stuff
    }
}

Dies sieht sehr ähnlich aus wie instanceofin meinem ersten Beispiel, außer dass wir diese Prüfung externalisieren. Dies bedeutet, dass wir es auf zwei Arten nennen müssten:

o.doIt(a, a instanceof B);

oder:

o.doIt(a, true); //or false

Erstens hat der Callpoint keine Ahnung, welchen Typ Aer hat. Sollten wir deshalb Boolesche Werte ganz nach unten weitergeben? Ist das wirklich ein Muster, das wir überall in der Codebasis haben wollen? Was passiert, wenn es einen dritten Typ gibt, den wir berücksichtigen müssen? Wenn die Methode so aufgerufen wird, sollten wir sie in den Typ verschieben und das System die Implementierung für uns polymorph auswählen lassen.

Zweitens müssen wir den Typ des Anrufpunkts bereits kennena . Normalerweise bedeutet dies, dass wir entweder die Instanz dort erstellen oder eine Instanz dieses Typs als Parameter verwenden. Das Erstellen einer Methode O, die Bhier benötigt wird, würde funktionieren. Der Compiler würde wissen, welche Methode er wählen soll. Wenn wir solche Änderungen durchlaufen, ist Duplizieren besser, als die falsche Abstraktion zu erstellen , zumindest bis wir herausfinden, wohin wir wirklich gehen. Natürlich schlage ich vor, dass wir nicht wirklich fertig sind, egal was wir bis jetzt geändert haben.

Wir müssen uns die Beziehung zwischen Aund genauer ansehen B. Im Allgemeinen wird uns gesagt, wir sollten die Zusammensetzung der Vererbung vorziehen . Dies ist nicht in jedem Fall der Fall, aber es ist in einer überraschenden Anzahl von Fällen der Fall, wenn wir uns eingegraben haben. BErbt von A, was bedeutet, dass wir glauben, dass es sich Bum eine handelt A. Bsollte genauso verwendet werden A, nur dass es etwas anders funktioniert. Aber was sind diese Unterschiede? Können wir den Unterschieden einen konkreteren Namen geben? Ist es nicht Bein A, aber hat wirklich Aein Xdas könnte A'oder sein B'? Wie würde unser Code aussehen, wenn wir das tun würden?

Wenn wir die Methode Awie zuvor vorgeschlagen auf verschieben, können wir eine Instanz von Xin einfügen Aund diese Methode an Folgendes delegieren X:

class A {
    X x;
    A(X x) {
        this.x = x;
    }

    public void doIt(int x, int y) {
        x.doIt(x, y);
    }
}

Wir können A'und implementieren und B'loswerden B. Wir haben den Code verbessert, indem wir einem möglicherweise impliziteren Konzept einen Namen gegeben haben, und uns erlaubt, dieses Verhalten zur Laufzeit anstatt zur Kompilierungszeit festzulegen. Aist eigentlich auch weniger abstrakt geworden. Anstelle einer erweiterten Vererbungsbeziehung werden Methoden für ein delegiertes Objekt aufgerufen. Dieses Objekt ist abstrakt, konzentriert sich jedoch nur auf die Unterschiede in der Implementierung.

Es gibt jedoch noch eine letzte Sache zu beachten. Kehren wir zum Vorschlag Ihres Kollegen zurück. Wenn wir an allen Anrufstellen explizit wissen, welchen Typ Awir haben, sollten wir Anrufe tätigen wie:

B b = new B();
o.doIt(b, true);

Wir gingen davon aus früher beim Komponieren , dass Aeine hat , Xdie entweder A'oder B'. Aber vielleicht ist auch diese Annahme nicht richtig. Ist dies der einzige Ort, an dem dieser Unterschied zwischen Aund Bwichtig ist? Wenn ja, können wir vielleicht einen etwas anderen Ansatz wählen. Wir haben immer noch ein X, das entweder A'oder ist B', aber es gehört nicht dazu A. Nur O.doItSorgen darüber, also lassen Sie uns geben sie nur an O.doIt:

class O {
    int x;
    int y;

    public void doIt(A a, X x) {
        x.doIt(a, x, y);
    }
}

Jetzt sieht unsere Anrufseite so aus:

A a = new A();
o.doIt(a, new B'());

Wieder einmal Bverschwindet, und die Abstraktion bewegt sich in die mehr konzentriert X. Diesmal Aist es jedoch noch einfacher, wenn man weniger weiß. Es ist noch weniger abstrakt.

Es ist wichtig, die Duplizierung in einer Codebasis zu reduzieren, aber wir müssen uns überlegen, warum die Duplizierung überhaupt auftritt. Duplizierung kann ein Zeichen für tiefere Abstraktionen sein, die versuchen, herauszukommen.

cbojar
quelle
1
Es fällt mir auf, dass der Beispielcode "schlecht", den Sie hier angeben, dem ähnelt, was ich in einer Nicht-OO-Sprache tun würde. Ich frage mich, ob sie die falschen Lektionen gelernt und sie so in die OO-Welt gebracht haben, wie sie codieren.
Baldrickk
1
@Baldrickk Jedes Paradigma bringt seine eigenen Denkweisen mit ihren einzigartigen Vor- und Nachteilen mit. In funktionalem Haskell wäre der Mustervergleich der bessere Ansatz. Obwohl in einer solchen Sprache einige Aspekte des ursprünglichen Problems auch nicht möglich wären.
Cbojar
1
Dies ist die richtige Antwort. Eine Methode, die die Implementierung basierend auf dem Typ ändert, mit dem sie arbeitet, sollte eine Methode dieses Typs sein.
Roman Reiner
0

Abstraktion durch Vererbung kann ziemlich hässlich werden. Parallele Klassenhierarchien mit typischen Fabriken. Refactoring kann zu Kopfschmerzen werden. Und auch spätere Entwicklung, der Ort, an dem Sie sich befinden.

Es gibt eine Alternative: Erweiterungspunkte mit strengen Abstraktionen und abgestufte Anpassungen. Sagen wir eine Anpassung von Regierungskunden, basierend auf dieser Anpassung für eine bestimmte Stadt.

Eine Warnung: Leider funktioniert dies am besten, wenn alle (oder die meisten) Klassen erweitert werden. Keine Option für Sie, vielleicht in kleinen.

Diese Erweiterbarkeit funktioniert, indem eine erweiterbare Objektbasisklasse Erweiterungen enthält:

void f(CreditorBO creditor) {
    creditor.as(AllowedCreditorBO.class).ifPresent(allowedCreditor -> ...);
}

Intern erfolgt eine verzögerte Zuordnung von Objekten zu erweiterten Objekten nach Erweiterungsklassen.

Für GUI-Klassen und -Komponenten die gleiche Erweiterbarkeit, teilweise mit Vererbung. Hinzufügen von Schaltflächen und dergleichen.

In Ihrem Fall sollte eine Validierung prüfen, ob sie erweitert ist, und sich anhand der Erweiterungen validieren. Das Einführen von Erweiterungspunkten nur für einen Fall fügt unverständlichen Code hinzu, der nicht gut ist.

Es gibt also keine Lösung, als zu versuchen, im aktuellen Kontext zu arbeiten.

Joop Eggen
quelle
0

'Hidden Flow Control' klingt für mich zu handgewellt.
Jedes Konstrukt oder Element, das aus dem Kontext genommen wurde, kann diese Eigenschaft haben.

Abstraktionen sind gut. Ich temperiere sie mit zwei Richtlinien:

  • Besser nicht zu früh abstrahieren. Warten Sie auf weitere Beispiele für Muster, bevor Sie abstrahieren. "Mehr" ist natürlich subjektiv und spezifisch für die schwierige Situation.

  • Vermeiden Sie zu viele Abstraktionsebenen, nur weil die Abstraktion gut ist. Ein Programmierer muss diese Ebenen für neuen oder geänderten Code im Kopf behalten, während er die Codebasis auslotet und 12 Ebenen tief geht. Der Wunsch nach gut abstrahiertem Code kann zu so vielen Ebenen führen, dass es für viele Menschen schwierig ist, ihnen zu folgen. Dies führt auch dazu, dass nur Ninja-Codebasen verwaltet werden.

In beiden Fällen sind "mehr" und "zu viele" keine festen Zahlen. Es hängt davon ab, ob. Das macht es schwer.

Ich mag auch diesen Artikel von Sandi Metz

https://www.sandimetz.com/blog/2016/1/20/the-wrong-abstraction

Duplizierung ist weitaus billiger als die falsche Abstraktion
und
zieht Duplizierung der falschen Abstraktion vor

Michael Durrant
quelle