[JAVA] Wie man guten Code schreibt

Überblick

Codierungsrichtlinien für die kontinuierliche Weiterentwicklung durch das Team. Es ist nicht für eine bestimmte Sprache gedacht, sondern hauptsächlich für statisch typisierte objektorientierte Sprachen. Der Beispielcode wird in Swift beschrieben, sofern nicht anders angegeben.

Zweck der Richtlinie

Beschreibung der Inhalte

Die [: star: number] am Anfang des Titels ist die Wichtigkeit. Je höher der Wert, desto größer die Auswirkung auf das System. Je niedriger der Wert, desto geringer die Auswirkung und desto einfacher ist die Reparatur.


[: Star: 5] Reduzieren Sie den Umfang der Variablen

Variablenwerte verursachen Komplexität und führen zu Missverständnissen und Fehlern. Je weniger Variablen ein Programm hat, desto weniger wahrscheinlich ist es, dass es Probleme verursacht. Behalten Sie als allgemeine Programmierregel die minimal erforderlichen Variablen bei und erhöhen Sie sie nicht unnötig.

Je größer der Umfang und die Lebensdauer einer Variablen sind, desto größer ist auch der nachteilige Effekt. Versuchen Sie daher bei Verwendung einer Variablen, den Umfang und die Lebensdauer zu minimieren. Grundsätzlich haben die oben genannten einen größeren Anwendungsbereich. Vermeiden Sie daher deren Verwendung.

  1. Globale Variablen
  2. Instanzvariablen (Klassenmitgliedsvariablen)
  3. Lokale Variablen

Globale Variablen

Wie oben erwähnt, ist der Schaden umso größer, je größer der Umfang einer Variablen ist. Vermeiden Sie daher die Verwendung der globalen Variablen mit dem größten Umfang so weit wie möglich.

Was sind globale Variablen?

Variablen, die von überall gelesen und geschrieben werden können, werden als globale Variablen bezeichnet.

Neben sogenannten globalen Variablen wie der Sprache C werden statische Variablen (Klassenvariablen) wie Java häufig als globale Variablen bezeichnet. Darüber hinaus werde ich die Bedeutung hier etwas näher erläutern und mit der Diskussion von Singletons und gemeinsam genutzten Objekten fortfahren, auf die von überall als globale Variablen zugegriffen werden kann.

Speicher wie Datenbanken und Dateien haben die gleiche Eigenschaft, von überall lesbar und beschreibbar zu sein. Obwohl er leicht unregelmäßig ist, wird er hier als eine Art globale Variable behandelt.

Warum globale Variablen schlecht sind

Globale Variablen verursachen wahrscheinlich die folgenden Probleme, da sie Werte von überall lesen und schreiben können.

Darüber hinaus kann das Vorhandensein globaler Variablen es Klassen und Modulen ermöglichen, die ansonsten nicht miteinander in Beziehung stehen, sich gegenseitig durch die globalen Variablen (sogenannte eng gekoppelte Zustände) zu beeinflussen. Infolgedessen ist es schwierig, Klassen und Module wiederzuverwenden, Probleme zu isolieren und Komponententests durchzuführen.

Solltest du es überhaupt nicht benutzen?

Warum also überhaupt keine globalen Variablen verwenden? Das ist nicht der Fall. Wie oben erwähnt, werden Singletons, gemeinsam genutzte Objekte und DBs ebenfalls als eine Art globale Variable definiert, es ist jedoch schwierig, eine Anwendung zu erstellen, ohne sie überhaupt zu verwenden.

Anwendungen verfügen häufig über kontextähnliche Informationen, die häufig benötigt werden, z. B. Einstellungen und Sitzungsinformationen. Es ist einfacher, solche Informationen zu implementieren, indem eine Methode bereitgestellt wird, auf die von überall zugegriffen werden kann, z. B. eine globale Variable, anstatt diese Informationen als Funktionsargument an ein anderes Objekt wie ein Bucket-Relay zu übergeben.

Die Politik, die Verwendung globaler Variablen so weit wie möglich zu vermeiden, bleibt jedoch unverändert. Überlegen Sie das Gesamtdesign sorgfältig und stellen Sie sicher, dass nur die minimal erforderlichen globalen Zugriffsmethoden verfügbar sind.

Globale Backend-Variablen

In WEB-Anwendungen (PHP, Rails, SpringMVC, ASP usw.), die auf der Serverseite HTML generieren, spielen DBs und Sitzungen die Rolle globaler Variablen, also im engeren Sinne sogenannte globale Variablen (PHP globale Variablen und Statische Java-Variablen usw.) werden selten benötigt. Wenn Sie in Ihrer WEB-App sogenannte globale Variablen verwenden, besteht eine hohe Wahrscheinlichkeit, dass ein Entwurfsproblem vorliegt.

Es scheint auch, dass kurzfristige Prozesse wie die Stapelverarbeitung selten sogenannte globale Variablen ohne DB erfordern.

Globale Front-End-Variablen

Andererseits werden in Front-End-Anwendungen wie Smartphones, Desktop-Apps und SPAs globale Variablen häufig selbst erstellt und verwendet. Vermeiden Sie in diesem Fall die folgende Verwendung.

Auf der anderen Seite existieren globale Variablen eher permanent als vorübergehend, werden für Informationen bereitgestellt, die üblicherweise von verschiedenen Funktionen der Anwendung verwendet werden, und sollten verwendet werden, wenn der Zugriff auf einige Klassen eingeschränkt ist. Es wird gut sein.

Punkte bei Verwendung globaler Variablen

Wenn Sie eine globale Variable oder eine statische Variable erstellen, machen Sie die zugehörigen Daten zu einem Objekt und halten Sie sie in Form eines Singletons oder eines gemeinsam genutzten Objekts, anstatt einen einfachen Datentyp wie Int oder String in der Variablen zu belassen. Ist gut.

Bad


var userName: String = ""
var loginPassword: String = ""

Good


class AccountInfo {
   static var shared = AccountInfo()

   var userName: String = ""
   var loginPassword: String = ""
}

Da es außer Kontrolle geraten würde, wenn jemand solche gemeinsam genutzten Objekte auf unbestimmte Zeit hinzufügen könnte, ist es vorzuziehen, dass einige Experten im Team die gemeinsam genutzten Objekte entwerfen.

Selbst wenn Sie eine globale Variable erstellen, sollten Sie nicht unnötig aus verschiedenen Klassen darauf verweisen. Entscheiden Sie, auf welche Ebenen Sie auf globale Variablen zugreifen können, und greifen Sie nicht von anderen Ebenen aus auf sie zu.

Freigegebenes Objekt von Singleton

Ich habe geschrieben, dass globale Variablen in Form von Singletons oder gemeinsam genutzten Objekten vorliegen sollten, aber in Wirklichkeit sind austauschbare gemeinsam genutzte Objekte besser als Singletons.

Bad


class Sample {
   //Einzelne Tonne, die nicht neu zugewiesen werden kann
   static let shared = Sample()
}

Good


class Sample {
   //Neu zuweisbares gemeinsames Objekt
   static var shared = Sample()
}

Singleton hat die folgenden Nachteile, da es nur eine Instanz hat. Diese Nachteile sind insbesondere Hindernisse für UnitTest und können für die Anwendungsimplementierung problematisch sein.

Wenn es sich um ein gemeinsam genutztes Objekt handelt, können die oben genannten Nachteile beseitigt werden, während es eine Singleton-ähnliche Funktion erhält. Im Gegensatz zu Singletons wird für gemeinsam genutzte Objekte mechanisch nicht garantiert, dass sie eine Instanz haben. Wenn Sie jedoch eine Instanz haben möchten, können Sie eine solche Entwurfsrichtlinie innerhalb des Entwicklungsteams freigeben.

In Verbindung stehender Artikel [Verlieren Sie nicht die Versuchung des Singleton-Musters](https://xn--97-273ae6a4irb6e2hsoiozc2g4b8082p.com/%E3%82%A8%E3%83%83%E3%82%BB%E3%82%A4/%E3 % 82% B7% E3% 83% B3% E3% 82% B0% E3% 83% AB% E3% 83% 88% E3% 83% B3% E3% 83% 91% E3% 82% BF% E3% 83 % BC% E3% 83% B3% E3% 81% AE% E8% AA% 98% E6% 83% 91% E3% 81% AB% E8% B2% A0% E3% 81% 91% E3% 81% AA % E3% 81% 84 /)

Stellen Sie sich globale Variablen als Datenbank vor

Wenn Sie auf die oben in den vorherigen Abschnitten beschriebenen globalen Variablen zurückblicken, die gemeinsam genutzte Objekte verwenden, ist dies der Positionierung der Datenbank sehr ähnlich. Es ist leicht vorstellbar, dass globale Variablen so gestaltet sind, als wären sie eine Datenbank oder ein Repository, das im MVC-Muster gesprochen wird.

DI-Behälter

Auf Plattformen mit DI-Containern (wie Spring und Angular) ist es besser, gemeinsam genutzte Objekte mit DI-Containern zu verwalten.

In einer Umgebung mit einem DI-Container ist die gesamte Anwendung in der Regel so konzipiert, dass sie vom DI-Container abhängt. Ob es gut oder schlecht ist, es ist in einer solchen Umgebung einfacher zu verstehen, wenn Sie die Verwaltung dem DI-Container überlassen, anstatt die freigegebenen Objekte selbst zu verwalten.

Ist es möglich, ohne globale Variablen zu entwerfen?

Wenn Sie alle erforderlichen Informationen als Argumente wie ein Bucket-Relay übergeben, können Sie eine Anwendung erstellen, ohne globale Variablen zu verwenden. Es ist eine Form, in der die erforderlichen Informationen von außen weitergegeben werden, anstatt vom Benutzer aufgenommen zu werden (im Folgenden als DI bezeichnet).

Selbst in Form von DI wird dieses Objekt, wenn Sie ein Objekt übergeben, dessen Status geändert werden kann, von mehreren Benutzern geändert und wie eine globale Variable behandelt, sodass die in DI übergebenen Informationen den Status nicht ändern können ( Darf nur nicht beschreibbare Wertobjekte sein.

Durch die gründliche Übergabe unveränderlicher Objekte wie eines solchen Bucket-Relays scheinen Dinge wie globale Variablen vollständig beseitigt zu werden, und auf den ersten Blick wird es sich um ein lose gekoppeltes und schönes Design handeln, aber diese Richtlinie Eigentlich gibt es Fallstricke.

Das Problem bei dieser Richtlinie besteht darin, dass alle vermittelnden Objekte (Passanten des Bucket-Relays) vorübergehend über die Informationen verfügen müssen, die von den Objekten am Ende des Bucket-Relays benötigt werden. Da dieses Problem erfordert, dass eine bestimmte Klasse vorübergehend irrelevante Informationen enthält, kann nicht gesagt werden, dass sie lose gekoppelt ist.

Obwohl die Verwendung globaler Variablen vermieden wird, ist es nicht praktikabel, sie überhaupt nicht zu verwenden, und es ist möglicherweise am besten, globale Variablen unter der richtigen Richtlinie zu verwenden.

Instanzvariablen (Klassenmitgliedsvariablen)

Vermeiden Sie wie bei globalen Variablen nach Möglichkeit die Verwendung von Instanzvariablen. Überlegen Sie beim Hinzufügen einer neuen Variablen sorgfältig, ob dies unbedingt erforderlich ist, anstatt sie beiläufig hinzuzufügen. Wenn es zwei Klassen gibt, die dieselbe Funktion implementieren, ist es wichtig zu sagen, dass es besser ist, weniger Instanzvariablen zu haben.

So reduzieren Sie Instanzvariablen

Ich schrieb, dass ich die Verwendung von Instanzvariablen so weit wie möglich vermeiden sollte, aber wenn es darum geht, Variablen zu reduzieren, sind die folgenden drei die Grundlagen.

Machen Sie die für das Funktionsargument ausreichenden Daten nicht zu einer Instanzvariablen

Wie so oft bei Anfängern, verwenden Sie nicht nur Instanzvariablen, um Daten in mehreren Funktionen wiederzuverwenden. Wenn die Daten nicht lange gespeichert werden müssen, übergeben Sie sie als Funktionsargument, ohne eine Instanzvariable zu verwenden.

bad


class Foo {
    var user: User?

    func setUser(user: User) {
        self.user = user
        printName()
        printEmail()
    }

    func printName() {
        print(user?.name)
    }

    func printEmail() {
        print(user?.email)
    }
}

good


class Foo {
    func setUser(user: User) {
        printName(user: user)
        printEmail(user: user)
    }

    func printName(user: User) {
        print(user.name)
    }

    func printEmail(user: User) {
        print(user.email)
    }
}

In Verbindung stehender Artikel Falsche Verwendung von Instanzvariablen (die Sie möglicherweise auch um sich herum sehen)

Halten Sie den verarbeiteten Wert nicht in der Instanzvariablen

Es kann für die Leistungsoptimierung unvermeidbar sein, enthält jedoch im Grunde keinen verarbeiteten Wert in der Instanzvariablen. Im folgenden Beispiel sind "itemsA" und "itemsB" die verarbeiteten Werte von "items".

bad


class Foo {
    var items = ["A-1", "A-2", "B-1", "B-2"]
    let itemsA: [String]
    let itemsB: [String]

    init() {
        itemsA = items.filter { $0.hasPrefix("A-") }
        itemsB = items.filter { $0.hasPrefix("B-") }
    }
}

In diesem Beispiel ist es nicht erforderlich, die Werte von "itemsA" und "itemsB" in Instanzvariablen zu verwandeln. Machen Sie einfach "items" zu einer Variablen und generieren Sie "itemsA" und "itemsB" daraus mit einer Funktion.

good


class Foo {
    var items = ["A-1", "A-2", "B-1", "B-2"]

    func itemsA() -> [String] {
        return items.filter { $0.hasPrefix("A-") }
    }

    func itemsB() -> [String] {
        return items.filter { $0.hasPrefix("B-") }
    }
}

Halten Sie keine Informationen bereit, die programmgesteuert in Instanzvariablen erfasst werden können

Obwohl es ein wenig darunter leidet, "den verarbeiteten Wert nicht in der Instanzvariablen zu halten", werden die Informationen, die anhand anderer Werte beurteilt werden können, und die Informationen, die von einer API oder einem Programm abgerufen werden können, nicht in der Instanzvariablen gespeichert, und das Programm wird jedes Mal ausgeführt, wenn es benötigt wird. Lauf und hol.

Verwenden Sie den Abschluss, um Instanzvariablen zu löschen

Für Anfänger ist es etwas schwierig, aber Sie können Verschlüsse verwenden, um unnötige Instanzvariablen zu reduzieren.

Grundsätzlich werden Instanzvariablen verwendet, um Daten über einen langen Zeitraum aufzubewahren. Mit Schließungen können Sie Daten jedoch über einen langen Zeitraum aufbewahren, ohne Instanzvariablen zu verwenden. Im folgenden Beispiel kann mithilfe des After-Formulars "dataType" beibehalten werden, bis die API-Kommunikation ohne Verwendung von Instanzvariablen abgeschlossen ist.

before


class Before {
    var dataType: DataType?

    func fetchData(dataType: DataType) {
        self.dataType = dataType //In Instanzvariable speichern

        APIConnection(dataType).getData(onComplete: { response in
            self.setResponse(response)
        })
    }

    func setResponse(_ response: Response) {
        print("\(dataType)War eingestellt")
    }
}

after


class After {
    func fetchData(dataType: DataType) {
        APIConnection(dataType).getData(onComplete: { response in
            //Durch die Verwendung von datType beim Schließen kann dataType beibehalten werden, bis die API-Kommunikation abgeschlossen ist.
            self.setResponse(response, dataType: dataType)
        })
    }

    func setResponse(_ response: Response, dataType: DataType) {
        print("\(dataType)War eingestellt")
    }
}

Die funktionale Programmierung kann verwendet werden, um Variablen zu reduzieren und den Umfang der Variablen einzugrenzen. In einigen funktionalen Sprachen wie Haskell können Variablen überhaupt nicht neu zugewiesen werden, sodass gesagt werden kann, dass es in gewissem Sinne keine Variablen gibt.

Selbst wenn Sie funktionale Programmierung verwenden, werden die Daten am Ende irgendwo gespeichert, aber der Umfang und der Umfang des Einflusses werden kleiner und Sie können weniger schädlichen Code schreiben.

Lokale Variablen

Sie können es verwenden, aber versuchen Sie, den Umfang zu minimieren, indem Sie es nur definieren, wenn Sie es benötigen.

Bad


var num = 0
for i in list {
    num = i
    print(num)
}

Good


for i in list {
    let num = i
    print(num)
}

In einigen Sprachen (z. B. im Umgang mit C-Sprache und JavaScript-Var-Wicklung) kann es jedoch erforderlich sein, Variablen am Anfang des Bereichs zu deklarieren.

Erklärende Variablen können positiv verwendet werden

Zur besseren Lesbarkeit ist es möglich, Variablen zu erstellen, die für die Implementierung nicht unbedingt erforderlich sind, und das Ergebnis des Ausdrucks einmal zuzuweisen. Solche Variablen werden als "erklärende Variablen" bezeichnet und können im Gegensatz zu anderen Variablen bei Bedarf aktiv verwendet werden.

Es ist nicht erforderlich, den Wert der erklärenden Variablen neu zuzuweisen, und er wird wie eine Konstante behandelt, sodass eine Erhöhung keinen großen Schaden verursacht.

before


let totalPrice = ((orangePrice * orangeQuantity) + (applePrice * appleQuanitity)) *  (1 + taxPercentage / 100)

after


//Die folgenden drei sind erklärende Variablen
let orangePriceSum = orangePrice * orangeQuantity
let applePriceSum = applePrice * appleQuanitity
let includesTaxRate = 1 + taxPercentage / 100

let totalPrice = (orangePriceSum + applePriceSum) * includesTaxRate

Erklärende Variablen erhöhen die Anzahl der Codezeilen, verbessern jedoch die Lesbarkeit und haben den Vorteil, dass das Debuggen mithilfe von Haltepunkten vereinfacht wird, da die Ergebnisse in der Mitte eines Ausdrucks angezeigt werden.

Verkürzen Sie die Lebensdauer von Variablen

Wenn Sie einen Zustand in einer Variablen speichern, sollte die Lebensdauer des Werts so kurz wie möglich sein. Speichern Sie den Wert, wenn er benötigt wird, und löschen Sie ihn so schnell wie möglich, wenn er nicht mehr benötigt wird. Der in der Variablen gespeicherte Wert ist eine Momentaufnahme dieses Moments, und es besteht die Gefahr, dass er im Laufe der Zeit vom neuesten Status abweicht. Daher sollte die Lebensdauer des in der Variablen gespeicherten Werts so weit wie möglich verkürzt werden.

[: Star: 5] Single-Source-Prinzip

Bewahren Sie dieselben Informationen nicht mehr als einmal auf. Im folgenden Beispiel wird das Alter beispielsweise in zwei Feldern in unterschiedlichen Formen gespeichert und die Informationen werden dupliziert.

Bad


class Person {
   var age = 17
   var ageText = "17 Jahre alt"
}

In einem solchen Fall ist es besser, die beizubehaltenden Informationen wie unten gezeigt zu kombinieren und den zu verwendenden Wert zu verarbeiten.

Good


class Person {
   var age = 17
   var ageText: String { 
      return "\(age)Alter" 
   }
}

Doppelte Informationen haben die folgenden nachteiligen Auswirkungen auf das System.

――Ich weiß nicht, welche der mehreren Informationen ich verwenden soll

Dieses Beispiel ist eine einfache und leicht verständliche Informationsduplizierung, es gibt jedoch verschiedene andere Formen der Informationsduplizierung.

Durch Cache duplizieren

Im folgenden Beispiel werden die DB-Daten in der Instanzvariablen gelesen und gespeichert (zwischengespeichert). Dies führt jedoch dazu, dass die in der Instanzvariablen enthaltenen Informationen und die Informationen in der DB dupliziert werden.

Bad


class Foo {
   var records: [DBRecord]?
   func readDBRecord(dbTable: DBTable) {
      records = dbTable.selectAllRecords()
   }
}

Es ist besser zu vermeiden, die aus der Datenbank gelesenen Daten wie oben beschrieben in der Instanzvariablen zu halten. Wenn das "Foo" -Objekt über einen längeren Zeitraum existiert und die Datenbank während dieser Zeit aktualisiert wird, besteht ein Unterschied zwischen den Instanzvariableninformationen von Foo und den DB-Informationen.

Codieren Sie keine offensichtlichen Informationen

Im folgenden Beispiel werden 0, 1 und 2 für den Wörterbuchschlüssel (Map) verwendet. Da jedoch ein Index vorhanden ist, wenn er auf Array festgelegt ist, sind keine unnötigen Informationen erforderlich, wenn Sie eine Bestellung annehmen möchten.

Bad


func getName(index: Int) -> String? {
    let map = [0: "Sato", 1: "Shinagawa", 2: "Suzuki"]
    return map[index]
}

Wenn Sie Array verwenden, können Sie über den Index zugreifen, sodass Sie keine Informationen von 0 bis 2 benötigen.

Good


func getName2(index: Int) -> String? {
    let names = ["Sato", "Shinagawa", "Suzuki"]
    if 0 <= index && index < names.count {
        return names[index]
    }
    return nil
}

Anwendung auf andere als Programmierung

Die Richtlinie, Informationen nicht zu duplizieren, ist nicht nur für die Programmierung, sondern auch für die Dokumentenverwaltung nützlich.

Bad


Als ich die Spezifikationen auf den lokalen Computer kopierte und sie mir ansah, wurden die Spezifikationen aktualisiert und ich codierte basierend auf den alten Spezifikationen.

Es gibt Fälle, in denen aufgrund verschiedener Umstände ein lokales Kopieren erforderlich ist. Im obigen Beispiel tritt jedoch ein Problem aufgrund des Kopierens und Duplizierens von Spezifikationen auf.

Good


Auf dem gemeinsam genutzten Server gibt es nur eine Spezifikation, sodass Sie immer die neuesten Spezifikationen sehen können.

Das Duplizieren von Code ist nicht verboten

Das Problem in diesem Abschnitt besteht darin, keine doppelten Informationen zu haben, aber ** keinen Code mit ähnlicher Logik zu duplizieren **. Verstehen Sie mich nicht falsch, denn der Fall "Kopieren und Einfügen von Code zum Erstellen mehrerer ähnlicher Codes" unterscheidet sich von der Duplizierung hier. Das Teilen von Code mit ähnlicher Logik ist eine andere Geschichte mit einer Richtlinie mit niedrigerer Priorität.

Haben Sie nicht mehrere Arten von Informationen in einem Feld

Vermeiden Sie umgekehrt, mehrere Arten von Informationen in einem Feld zu haben (Variablen, DB-Spalten, Text-Benutzeroberfläche usw.). Das Einfügen mehrerer Arten von Informationen in ein Feld erschwert die Implementierung und birgt das Risiko, dass verschiedene Fehler auftreten.

Wenn Sie einen Fehler machen, verwenden Sie eine Variable nicht für mehrere Zwecke.

[: Stern: 5] Geben Sie einen passenden Namen an

Geben Sie programmgesteuert definierten Elementen wie Klassen, Eigenschaften, Funktionen und Konstanten geeignete Namen. Insbesondere der Name der Klasse hat einen großen Einfluss auf das Design und ist sehr wichtig.

Wenn der Klassenname, der Eigenschaftsname und der Funktionsname ordnungsgemäß zugewiesen werden können, ist dies fast gleichbedeutend mit dem richtigen Klassendesign, Datenentwurf und Schnittstellendesign. In gewissem Sinne ist es keine Übertreibung zu sagen, dass das Programmieren die Aufgabe der Benennung ist.

Beachten Sie bei der Benennung Folgendes.

――Der Zweck und die Bedeutung können aus dem Namen verstanden werden, auch wenn andere ihn sehen

Je breiter der Funktionsumfang und die Variablen sind, desto höflicher sollten die Namen sein.

Haben Sie nicht mehr Rolle als im Namen geschrieben

Variablen tun nichts weiter als das, was im Namen geschrieben steht. Die Funktion macht nichts anderes als das, was im Namen geschrieben steht.

Beispielsweise wird im Fall einer Klasse mit dem Namen "LoginViewController" nur der Prozess zum Steuern der Ansicht der Anmeldung beschrieben, und andere Prozesse wie die Anmeldeauthentifizierung werden im Prinzip nicht beschrieben. (Wenn es sich jedoch um einen kurzen Vorgang handelt, kann er in derselben Datei gemäß der Regel "Verwandte Elemente in der Nähe halten" beschrieben werden.)

Es geht nur darum, den Status von etwas mit einem Getter wie "getHoge ()" in Java zu aktualisieren.

Anti-Muster benennen

Fügen Sie Nummern und IDs hinzu

Bad


var code1 = "a"

func func001() {}

enum VieID {
  case vol_01, vol_02, vol_03
}

Vermeiden Sie es, Nummern und IDs wie die oben in Namen in Ihrem Programm zu verwenden, da Fremde nicht wissen, was sie bedeuten. Wenn Sie in Ihrem Code eine ID verwenden, müssen Sie das Programm ändern, wenn sich die ID ändert.

Lass das Wort weg

Bad


func chkDispFlg(){}

Wie viele von Ihnen vielleicht verstehen, ist das Obige eine Abkürzung für "checkDisplayFlag". Das Weglassen solcher Wörter ist eine traditionelle Kultur der Programmierer, aber in der heutigen Zeit gibt es fast keine Code-Vervollständigung durch IDE, so dass es nicht sinnvoll ist, sie wegzulassen. Lassen Sie keine Wörter weg, da es für Dritte schwierig sein wird, die Bedeutung zu verstehen.

Sinnlose Benennung

Bad


let yen = "Kreis"

Vermeiden Sie es, den spezifischen Inhalt des Werts so zu verwenden, wie er ist, da er nicht erweiterbar ist.

Wenn beispielsweise der obige "Yen" in "Dollar" geändert wird, ist der Variablenname "Yen" eine Lüge. Nennen Sie es in einem solchen Fall nicht nach dieser Konstante, sondern nach ihrer Rolle und Bedeutung. Im obigen Beispiel kann beispielsweise "priceSuffix" verwendet werden, weil es als Suffix für den Geldbetrag dient, oder "rencyUnit "kann verwendet werden, weil es eine Währungseinheit bedeutet.

So benennen Sie boolesche Variablen

Bad


var loadFlag = false

Vermeiden Sie die Verwendung von flag im Variablennamen von Boolean, da das Wort flag nur bedeutet, dass es boolean ist und keinen Zweck oder keine Bedeutung ausdrücken kann. Die Benennung von Booleschen Variablen hat das folgende Routinemuster, daher ist es gut, diesem Formular zu folgen.

--ist + Adjektive (isEmpty usw.) --ist + früher partizipativ (isHidden, etc.) --ist + Betreff + letzter Teil (isViewLoaded usw.)

In Verbindung stehender Artikel [So benennen Sie die Methode und Variable, die den booleschen Wert zurückgeben](http://kusamakura.hatenablog.com/entry/2016/03/03/boolean_%E5%80%A4%E3%82%92%E8%BF % 94% E5% 8D% B4% E3% 81% 99% E3% 82% 8B% E3% 83% A1% E3% 82% BD% E3% 83% 83% E3% 83% 89% E5% 90% 8D % E3% 80% 81% E5% A4% 89% E6% 95% B0% E5% 90% 8D% E3% 81% AE% E4% BB% 98% E3% 81% 91% E6% 96% B9)

Referenzinformationen für eine erfolgreiche Methodenbenennung

Ich verstehe kein englisch

Wenn Sie Dingen, deren englischen Namen Sie nicht kennen, Variablennamen zuweisen, gibt es meines Erachtens viele Fälle, in denen Sie zuerst im Internet nachschlagen. Seien Sie jedoch vorsichtig, da Wörter in maschinellen Übersetzungen wie der Google-Übersetzung häufig nicht richtig übersetzt werden. Wenn Sie nach Englisch suchen, ist es besser, nicht nur die Google-Übersetzung, sondern auch Beispielsätze in Wörterbüchern, Wikipedia usw. nachzuschlagen.

Wenn jedoch nicht alle Teammitglieder gut Englisch können und es einige Zeit dauert, Englisch nachzuschlagen, ist dies eine Möglichkeit, Englisch aufzugeben und in römischem Japanisch zu schreiben. Es ist etwas klobig, aber ich denke, es ist wichtig für die Lesbarkeit und Produktivität aller Teammitglieder.

Da der Funktionsname des Komponententests häufig ein beschreibender Satz ist, empfiehlt es sich, ihn auf Japanisch zu schreiben, wenn die Umgebung Japanisch für den Funktionsnamen verwenden kann.

Beispiel für den Namen einer Java-Unit-Testfunktion


public void Ein Test, um zu sehen, was wann passiert() {}

Vermeiden Sie generische Namen

Vermeiden Sie generische Namen so weit wie möglich. Die häufigsten sind "irgendwie Manager" und "irgendwie Controller". Generische Namen neigen dazu, verschiedene Prozesse aufzuerlegen, und die Klasse neigt dazu, zu wachsen.

Erstelle ein Wörterbuch

Bei der Entwicklung als Team wird empfohlen, ein Wörterbuch mit Begriffen zu erstellen, die in der Anwendung verwendet werden, und mit der Entwicklung zu beginnen, nachdem die Erkennung von Begriffen mit den Mitgliedern abgeglichen wurde. Durch das Erstellen eines Wörterbuchs können Inkonsistenzen vermieden werden, bei denen dasselbe von Entwicklern durch unterschiedliche Namen definiert wird, und es wird vermieden, dass einzelne Entwickler sich darum kümmern müssen, dasselbe separat zu benennen.

In Verbindung stehender Artikel Achten Sie beim Benennen von Modellen und Methoden auf englische Teile Anti-Pattern für Klassennamen

Konzentrieren Sie sich auf die Konsistenz des Codestils

Codestile wie Namenskonventionen für Klassen und Variablen sollten innerhalb des Projekts einheitlich und konsistent sein. Die Verwendung außergewöhnlicher Namen und Stile für Teile eines Projekts kann die Lesbarkeit beeinträchtigen und zu Missverständnissen und Versehen führen.

Wenn Sie beispielsweise bereits die Klassen "View001", "View002" und "View003" haben, ist es beim nächsten Hinzufügen der View-Klasse besser, die Benennungsmethode auf "View004" zu vereinheitlichen. Dies steht im Widerspruch zum obigen Abschnitt "Verwenden Sie keine Symbole oder IDs in Namen", aber es ist wichtiger, konsistente Namen im Projekt zu haben.

Wenn Sie ein Problem mit der aktuellen Namenskonvention oder dem aktuellen Stil haben und es ändern möchten, ist es eine gute Idee, die Zustimmung der Teammitglieder einzuholen und alle zu beheben, nicht nur einige.

[: Star: 5] Ziehen Sie die Form von object.function () der Form von function (object) vor.

Ich bevorzuge die Form object.function (), die die Funktion des Objekts aufruft, die Formfunction (object), die das Objekt an die Funktion übergibt.

Bad


if StringUtils.isEmpty(string) {}

Good


if string.isEmpty() {}

Dafür gibt es mehrere Gründe, die im Folgenden erläutert werden.

Zur besseren Lesbarkeit

Das Formular "Funktion (Objekt)", das ein Objekt an eine Funktion übergibt, ist schwer zu lesen, da die Klammern verschachtelt sind, wenn mehrere Prozesse wiederholt werden. Das Formular, das die Funktion eines Objekts "object.function ()" aufruft, ist jedoch durch Punkte und mehrere verbunden Gute Lesbarkeit, da es verarbeitet werden kann.

Bad


StringUtils.convertC(StringUtils.convertB(StringUtils.convertA(string)))

Good


string.convertA().convertB().convertC()

object.function () ist wiederverwendbarer

Im Allgemeinen wird im Fall von "Funktion (Objekt)" die Funktion in einer Klasse oder einem Modul definiert, und um die Verarbeitung durchzuführen, sind zwei Klassen oder Module erforderlich, in denen die Funktion definiert ist und das Objekt als Argument. Andererseits ist es im Fall von "object.function ()" in hohem Maße wiederverwendbar, da es nur vom Objekt verarbeitet werden kann.

Objektorientierung erfassen

** Die Form von object.function () ist die Essenz der Objektorientierung **.

Wenn wir über Objektorientierung sprechen, werden Klassen, Vererbung, Kapselung usw. oft zuerst erklärt, aber in Wirklichkeit sind sie für die Objektorientierung nicht wesentlich, und das einzige, was für die Objektorientierung notwendig ist, ist das Aufrufen einer Methode für ein Objektobjekt. Ich denke, es ist nur die Form von function () `.

Anfängern das Unterrichten der Objektorientierung ist schwierig. Es scheint viel über Objektorientierung zu lehren. Das Vergessen von Vererbung, Kapselung, Polymorphismus usw. und das Einweichen der Form von "object.function ()" in den Körper ist jedoch der kürzeste Weg, um die Objektorientierung zu erlangen.

In Verbindung stehender Artikel [Objektorientiert ist eine der Methoden zum Versenden von Methoden](https://qiita.com/shibukawa/items/2698b980933367ad93b4#%E3%82%AA%E3%83%96%E3%82%B8%E3%82 % A7% E3% 82% AF% E3% 83% 88% E6% 8C% 87% E5% 90% 91% E3% 81% AF% E3% 83% A1% E3% 82% BD% E3% 83% 83 % E3% 83% 89% E3% 83% 87% E3% 82% A3% E3% 82% B9% E3% 83% 91% E3% 83% 83% E3% 83% 81% E3% 81% AE% E6 % 89% 8B% E6% B3% 95% E3% 81% AE1% E3% 81% A4)

Verwenden Sie aktiv die Eigenschaft Computed

Abhängig von der Sprache ist dies möglicherweise nicht möglich, aber die Funktion der berechneten Eigenschaft ermöglicht, dass die Funktion als Eigenschaft behandelt wird. Lassen Sie uns die folgenden Funktionen positiv in berechnete Eigenschaften umwandeln.

Fälle, die nicht zutreffen

Wenn es dann zu irgendeinem Zeitpunkt um die Form von "object.function ()" anstatt um "function (object)" geht, ist dies möglicherweise nicht der Fall.

Sie sollten nicht die Form "object.function ()" annehmen, wenn die Klasse "object" unnötige Abhängigkeiten oder Rollen erhält, die Sie nicht haben sollten.

Das folgende Beispiel hat die Form "object.function ()" und erzeugt eine unnötige Abhängigkeit von der View-Klasse für "enum APIResult".

Bad


class LoginView: MyView {
    //Erhalten Sie das Anmeldeergebnis und fahren Sie mit dem nächsten Bildschirm fort
    func onReceivedLoginResult(result: APIResult) {
        let nextView = result.nextView() // object.function()Form von
        showNextView(nextView)
    }
}

enum APIResult {
    case success
    case warning
    case error

    func nextView() -> UIView {
        switch self {
        case .success: return HomeView()
        case .warning: return WarningView()
        case .error: return ErrorView()
        }
        //Dies hängt von den Klassen HomeView, WarningView und ErrorView ab.
    }
}

In einem solchen Beispiel wäre es besser, die Form "Funktion (Objekt)" wie folgt anzunehmen.

Good


class LoginView: MyView {
    //Erhalten Sie das Anmeldeergebnis und fahren Sie mit dem nächsten Bildschirm fort
    func onReceivedLoginResult(result: APIResult) {
        let nextView = nextView(result: result) // function(object)Form von
        showNextView(nextView)
    }

    func nextView(result: APIResult) -> UIView {
        switch result {
        case .success: return HomeView()
        case .warning: return WarningView()
        case .error: return ErrorView()
        }
    }
}

enum APIResult {
    case success
    case warning
    case error
}

Der Grund, warum Sie keine Abhängigkeit wie die erstere erstellen sollten, wird im Abschnitt "Kenntnis der Abhängigkeitsrichtung" weiter unten ausführlich erläutert.

[: Star: 5] Ziehen Sie Inklusion und Schnittstelle der Vererbung vor

Es gibt ein Problem mit der Klassenvererbung. Vererbung ist eine leistungsstarke Funktion, die jedoch viele Risiken birgt. Die Klassenvererbung ist unflexibel und anfällig für Änderungen.

Änderungen wirken sich auf nicht verwandte Funktionen aus

Wenn beispielsweise A- und B-Klassen von der Basisklasse erben und Base geändert wird, um die A-Funktion zu reparieren, ist die nicht verwandte B-Funktion fehlerhaft. Man kann sagen, dass dies ein Problem beim Klassendesign ist, aber bei der Vererbung können solche Probleme auftreten.

Kann nicht mehrere Eltern erben

Mit Ausnahme einiger Sprachen wie C ++ können nicht mehrere Klassen vererbt werden. Was tatsächlich existiert, hat jedoch oft mehrere übergeordnete Konzepte (übergeordnete Kategorien).

Wenn Sie zum Beispiel plötzlich an "indisches Curry" denken, können seine Eltern "Curry" oder "indisches Essen" sein.

Klasse indisches Curry erweitert Curry
Klasse indisches Curry erweitert indische Küche

Um ein Beispiel zu geben, das dem Ablauf der Implementierung ein wenig mehr folgt, nehmen wir an, dass es ein System gibt, mit dem Sie einen Smartphone-Vertrag im WEB abschließen können. Der Vertragsbildschirm ist in zwei Bereiche unterteilt: "Neuer Vertrag" und "Transfervertrag (MNP)", und es gibt jeweils Klassen, die diesen entsprechen. Da es zwischen neuen Verträgen und Übertragungsverträgen viele gemeinsame Teile gibt, erstellen Sie eine Basisvertragsklasse als gemeinsame übergeordnete Klasse.

スクリーンショット 2019-01-25 21.50.45.png

Als nächstes sollten Sie einen Vertragsänderungsbildschirm implementieren. Wenn Sie eine "neue Vertragsänderung" erstellen, die die neue Vertragsklasse erbt, und eine "Übertragungsvertragsänderungsklasse", die die Übertragungsvertragsklasse für Vertragsänderungen erbt, ist es unter dem Gesichtspunkt der Vertragsänderung nicht möglich, die Verarbeitung durch Vererbung zu standardisieren.

スクリーンショット 2019-01-25 21.44.57.png

Gemeinsame Elternklasse neigt dazu, aufgebläht zu sein

Wenn Sie beispielsweise eine Klasse mit dem Namen "BaseController" erstellen, die die übergeordnete Klasse aller Controller-Klassen ist, ist die BaseController-Klasse in der Regel aufgebläht, da sie die von verschiedenen Controllern verwendeten Funktionen enthält. Die minimal erforderliche Funktionalität ist ausreichend, aber im Grunde sollte eine gemeinsame übergeordnete Klasse darauf vorbereitet sein, als gemeinsame Schnittstelle behandelt zu werden, anstatt Funktionalität bereitzustellen.

In Verbindung stehender Artikel Nachteile der Vererbung Ich möchte nicht so etwas wie BaseViewController im iOS-App-Design erstellen Warum ist "Synthese" besser als "Vererbung" von Klassen? Verbesserte Codeflexibilität und Lesbarkeit in der Spieleentwicklung

[: Star: 4] Vereinfache die Verzweigung

Die Verzweigungslogik nach if-Anweisungen oder switch-Anweisungen beeinträchtigt tendenziell die Lesbarkeit des Programms und führt zu Fehlern. Versuchen Sie daher, sie so einfach wie möglich zu halten.

Vertiefe das Nest nicht

Wenn die Verschachtelung (Verschachtelung) von if- und for-Anweisungen tief wird, ist der Code schwer zu lesen. Machen Sie die Verschachtelung daher nicht so tief wie möglich. Um zu verhindern, dass die Verschachtelung tief wird, ist es besser, "Early Return" und "Cut-Out-Logik" durchzuführen.

Wir werden anhand eines Beispiels erklären, was passiert, wenn "Early Return" und "Logic Cutout" auf den folgenden Code angewendet werden.

Before


if text != nil {
    if text == "A" {
        //Prozess 1
    } else {
        //Prozess 2
    }
}

Vorzeitige Rückkehr

Wenn Sie zuerst den Ausnahmefall zurückgeben, wird die Verschachtelung der Hauptlogik flach gemacht. Im folgenden Beispielcode wird der Fall, in dem der Text Null ist, zuerst als Ausnahmemuster zurückgegeben.

After(Vorzeitige Rückkehr)


if text == nil {
    return
}
if text == "A" {
    //Prozess 1
} else {
    //Prozess 2
}

Wie der Name schon sagt, muss eine vorzeitige Rückgabe vorzeitig erfolgen. Grundsätzlich wird erwartet, dass die Funktion bis zur letzten Zeile verarbeitet wird. Wenn also eine Rückkehr in der Mitte einer langen Funktion erfolgt, besteht die Gefahr, dass sie übersehen wird und einen Fehler verursacht.

Logik ausschneiden

Schneiden Sie Prozesse einschließlich der Verschachtelung wie if-Anweisung und for-Anweisung in Methoden und Eigenschaften aus, um die Verschachtelung der Hauptlogik flach zu machen. Im folgenden Beispielcode wird der Teil, der bestimmt, ob Text "A" ist und eine Verarbeitung ausführt, als Klassenmethode der Textklasse ausgeschnitten.

After(Logik ausschneiden)


if text != nil {
   doSomething(text)
}

func doSomething(_ text: String?) {
    if text == "A" {
        //Prozess 1
    } else {
        //Prozess 2
    }
}

Verwandte Informationen [Reduzieren Sie die Anzahl der Blocknester](https://qiita.com/hirokidaichi/items/c9a76191216f3cc6c4b2#%E3%83%96%E3%83%AD%E3%83%83%E3%82%AF%E3 % 83% 8D% E3% 82% B9% E3% 83% 88% E3% 81% AE% E6% 95% B0% E3% 82% 92% E6% B8% 9B% E3% 82% 89% E3% 81 % 9D% E3% 81% 86)

Nicht nach Anrufer klassifizieren

Von verschiedenen Orten aufgerufene Funktionen sollten vom Anrufer nicht klassifiziert werden. In den folgenden Fällen werden Verarbeitungsfälle beispielsweise nach dem Bildschirm unterteilt, aber bei dieser Schreibmethode wird die Funktion mit zunehmender Anzahl von Bildschirmen unendlich groß.

Bad


class BaseViewController: UIViewController {
    func doSomething() {
        if viewId == "home" {
            //Verarbeitung des Startbildschirms
        } else if viewId == "login" {
            //Verarbeitung des Anmeldebildschirms
        } else if viewId == "setting" {
            //Bildschirmverarbeitung einstellen
        }
    }
}

Wenn Sie auf diese Weise schreiben, werden verschiedene Prozesse in eine Funktion gepackt, und es besteht eine hohe Wahrscheinlichkeit, dass es sich um eine große Funktion handelt, die schwer zu lesen, leicht zu beheben und schwer zu beheben ist.

Polymorphismus

Fälle in Funktionen wie der oben genannten können mithilfe von Polymorphismus gelöst werden. Eine detaillierte Erklärung des Polymorphismus wird weggelassen, da er langwierig ist. Durch Überschreiben von Methoden nach Schnittstelle (Protokoll) oder Vererbung kann jedoch jeder fallspezifische Prozess in jeder untergeordneten Klasse beschrieben werden.

class BaseViewController {
    func doSomething() {}
}
class HomeViewController: BaseViewController {
    override func doSomething() {
        //Verarbeitung des Startbildschirms
    }
}
class LoginViewController: BaseViewController {
    override func doSomething() {
        //Verarbeitung des Anmeldebildschirms
    }
}
class SettingViewController: BaseViewController {
    override func doSomething() {
        //Bildschirmverarbeitung einstellen
    }
}

Übergeben Sie die Funktion

Es ist auch möglich, Verzweigungen wie if-Anweisungen zu entfernen, indem eine Funktion als Argument der Funktion übergeben wird. Wenn die Funktion wie unten gezeigt als Argument empfangen wird, kann die Verarbeitung von etwas vom Aufrufer frei eingestellt werden, so dass die Verzweigung beseitigt werden kann. In diesem Beispiel handelt es sich um eine bedeutungslose Funktion, die nur die empfangene Funktion ausführt. In vielen Fällen werden jedoch die je nach Aufrufer unterschiedliche Abschlussverarbeitung und Fehlerverarbeitung als Argumente übergeben.

class BaseViewController: UIViewController {
    func doSomething(something: () -> Void) {
        something()
    }
}

Dasselbe kann mit Schnittstellen in Sprachen wie Java erreicht werden, die Funktionen nicht als Objekte verarbeiten können.

Reduzieren Sie die Anzahl der Zeilen im Verzweigungsblock

Versuchen Sie, die Anzahl der Zeilen im Verzweigungsblock, z. B. die if-Anweisung, so weit wie möglich zu reduzieren, um die Anzeige der Verzweigung zu vereinfachen.

if conditionA {
    //Schreiben Sie hier keine lange Bearbeitung
} else if conditionB {
    //Schreiben Sie hier keine lange Bearbeitung
}

Separate Befehle und Abfragen

Alle Methoden sollten entweder Befehle sein, die Aktionen ausführen, oder Abfragen, die Daten zurückgeben, nicht beide.

Dies ist die Idee mit dem Namen CQS (Command Query Separation).

Führen Sie zunächst keine Aktion in einer Methode (Abfrage) aus, die Daten zurückgibt. Mit anderen Worten ist es möglicherweise einfacher zu verstehen, dass der Aktualisierungsprozess nicht im Getter ausgeführt wird. Ich denke, viele Leute denken selbstverständlich daran.

Beschreiben Sie als Nächstes die Abfrage in der Methode (Befehl), die die Aktion ausführt, nicht so oft wie möglich. Dies ist schwieriger zu verstehen als das erstere, und es kann sich von dem unterscheiden, was die CQS-Hauptfamilie sagt, aber ich werde es anhand eines Beispiels erklären.

Stellen Sie sich beispielsweise eine Funktion vor, die berücksichtigt, dass Sie angemeldet sind und zur nächsten Seite wechseln, wenn Sie sowohl einen Benutzernamen als auch eine Sitzungs-ID haben.

Bad


func nextAction() {
    if userName.isNotEmpty && sessionId.isNotEmpty {
        showNextPage()
    }
}

Wenn Sie der Meinung sind, dass der Hauptzweck dieser Funktion darin besteht, "zur nächsten Seite zu wechseln", handelt es sich um einen "Befehl" anstelle einer "Abfrage". Der Teil, der bestimmt, ob Sie angemeldet sind, ist jedoch eine Abfrage, die den Wert von Bool erhält. , "Befehl" und "Abfrage" werden gemischt.

Wenn der Login-Beurteilungsteil als Abfrage an eine andere Funktion aus dieser Funktion herausgeschnitten wird, ist dies wie folgt.

Good


//Befehl
func nextAction() {
    if isLoggedIn() {
        showNextPage()
    }
}

//Abfrage
func isLoggedIn() {
   return userName.isNotEmpty && sessionId.isNotEmpty
}

Wenn der bedingte Ausdruck der if-Anweisung sehr kurz ist, ist es schwierig, sie in eine andere Funktion auszuschneiden. Wenn es sich jedoch um eine Abfrage einer bestimmten Länge handelt, ist es besser, sie als andere Funktion oder Eigenschaft aus der Aktion auszuschneiden.

[: Star: 4] Machen Sie die Klasse und Funktion nicht zu groß

Als Richtlinie sollte die Klasse etwa 50 bis 350 Zeilen und die Funktion etwa 5 bis 25 Zeilen umfassen. Wenn dies überschritten wird, sollten Sie die Klasse oder Funktion aufteilen.

Die Anzahl der Zeilen ist nur eine Richtlinie, und es ist möglicherweise besser, mehr Zeilen in dieselbe Klasse oder Funktion einzufügen. Nehmen wir jedoch an, dass eine Klasse mit mehr als 1000 Zeilen als gute Obergrenze gefährlich ist.

Diese Größe ist nicht die richtige Antwort, da sie von der Sprache, dem Schreibstil und der Funktion abhängt. Ich werde jedoch ein grobes Größengefühl für die Anzahl der Zeilen in der Klasse beschreiben.

Anzahl der Zeilen Ein Gefühl von Größe
Weniger als 50 Zeilen klein
Zeilen 50-350 Angemessen
350-700 Zeilen groß
700-1000 Zeilen Sehr groß
Über 1000 Zeilen Gefährlich

Die zugrunde liegenden Funktionen und UI-Klassen mit vielen Elementen können jedoch vom oben genannten Größengefühl abweichen. Zum Beispiel hat die Android View-Klasse 27.000 Zeilen, und im Extremfall würde ein Bildschirm mit 1000 Schaltflächen, die verschiedene Aufgaben ausführen, 1000 Zeilen überschreiten, selbst wenn er kurz geschrieben wird.

In Verbindung stehender Artikel [20 Artikel zum Schreiben von gutem Code, den die Zwischenprogrammierung lesen sollte](https://anopara.net/2014/04/11/%E3%83%97%E3%83%AD%E3%82%B0 % E3% 83% A9% E3% 83% 9F% E3% 83% B3% E3% 82% B0% E4% B8% AD% E7% B4% 9A% E8% 80% 85% E3% 81% AB% E8 % AA% AD% E3% 82% 93% E3% 81% A7% E3% 81% BB% E3% 81% 97% E3% 81% 84% E8% 89% AF% E3% 81% 84% E3% 82 % B3% E3% 83% BC% E3% 83% 89 /)

[: Star: 4] Erstellen Sie keine Flags (Boolesche Eigenschaften)

Es liegt in der Nähe der Richtlinie, keine Instanzvariablen zu haben, wie im Abschnitt "Reduzieren des Bereichs von Variablen" beschrieben, aber versuchen Sie, das Bool-Wert-Flag in den Instanzvariablen nicht strenger zu halten. Die von der Flagge (Bool-Variable) gehaltenen Informationen sind das Bewertungsergebnis zu einem bestimmten Zeitpunkt und im Grunde genommen die vergangenen Informationen. Daher besteht die Gefahr, dass es im Laufe der Zeit vom tatsächlichen Zustand abweicht.

Bad


class Foo {
    var hasReceived: Bool = false //Unnötige Flaggen
    var data: Data?

    func setData(data: Data) {
        self.data = data
        hasReceived = true
    }
}

Im obigen Beispiel wird von einem Flag gehalten, ob Daten empfangen wurden oder nicht. Das Flag ist jedoch nicht erforderlich, da Sie anhand der Variablen "data" sehen können, dass Daten empfangen wurden. Vermeiden Sie das Erstellen solcher Flags, da dies zu einer mehrfachen Verwaltung von Daten führt.

Um die Flagge zu entfernen, prüfen Sie zunächst, ob es möglich ist, in einem anderen Zustand anstelle der Flagge zu urteilen. Flags bestimmen einen Zustand, aber es ist oft möglich, den Zustand zu bestimmen, indem man etwas anderes ohne das Flag betrachtet.

Kombinieren Sie mehrere Flags zu einer Zustandsvariablen

Wenn mehrere Flags vorhanden sind, können mehrere Flags zu einer Enum-Variablen kombiniert werden, indem eine Enum erstellt wird, die den Status darstellt.

Bad


class User {
    var isAdmin = false
    var isSuperUser = false
    var isGeneralUser = false
}

Good


enum UserType {
    case admin       //Administrator
    case superUser   //Super User
    case generalUser //Allgemeiner Benutzer
}

class User {
    var userType: UserType = .admin
}

Stellen Sie jedoch sicher, dass Sie nur einen Informationstyp in Enum kombinieren, und ** Kombinieren Sie nicht mehrere Informationstypen in einem Enum. ** ** ** Vermeiden Sie im obigen Beispiel das Hinzufügen von "angemeldet" zu "Benutzertyp", um die folgende Aufzählung zu erstellen.

Bad


enum UserLoginType {
    case adminLoggedIn          //Administrator (angemeldet)
    case adminNotLoggedIn       //Administrator (nicht angemeldet)
    case superUserLoggedIn      //Super User (eingeloggt)
    case superUserNotLoggedIn   //Superuser (nicht eingeloggt)
    case generalUserLoggedIn    //Allgemeiner Benutzer (angemeldet)
    case generalUserNotLoggedIn //Allgemeiner Benutzer (nicht angemeldet)
}

[: Star: 4] Verwenden Sie keine Zahlen oder Zeichenfolgen als Datenschlüssel

Verwenden Sie keine Zahlen, um die bedingte Verzweigung zu bestimmen

Vermeiden Sie die Verwendung von Zahlen wie 0, 1, 2, um die bedingte Verzweigung zu bestimmen. Alternativen variieren von Fall zu Fall, aber die Verwendung von Arrays und Listen vermeidet häufig die Verwendung von Zahlen.

Bad


func title(index: Int) -> String {
   switch index {
      case 0:
         return "A"
      case 1:
         return "B"
      case 2:
         return "C"
      default:
         return ""
   }
}

Good


let titles = ["A", "B", "C"]

func title(index: Int) -> String {
	return titles.indices.contains(index) ? titles[index] : ""
}

0, 1 und -1 haben jedoch häufig spezielle Rollen, z. B. das Abrufen des ersten Elements oder das Anzeigen eines API-Fehlers oder -Erfolgs. Daher müssen sie je nach Umgebung möglicherweise verwendet werden.

Übergeben Sie nicht mehrere Datentypen in einem Array

Statisch typisierte Sprachen verwenden keine Arrays (Array, Liste), um mehrere Datentypen zu übergeben.

Die folgende Funktion verwendet Zahlen, um den Vornamen, die zweite Postleitzahl und die dritte Adressinformation in der Liste zu speichern. Es ist jedoch möglich, anhand des Listentyps zu bestimmen, welche Informationen in welcher Nummer enthalten sind. Da es nicht gelesen werden kann, wird es schwierig zu lesen und es treten wahrscheinlich Fehler auf.

Bad


func getUserData(): [String: String] {
   var userData: [String] = []
   userData[0] = "Masao Yamada"
   userData[1] = "171-0001"
   userData[2] = "Toyoshima-ku, Tokio"
   return userData
}

Erstellen Sie beim Übergeben einer festen Datenstruktur eine Struktur oder Klasse wie unten gezeigt und übergeben Sie die Daten.

Good


struct UserData {
   let name: String
   let postalCode: String
   let address: String
}

func getUserData(): UserData {
   return UserData(name: "Masao Yamada", 
                   postalCode: "171-0001", 
                   address: "Toyoshima-ku, Tokio")
}

Wenn Sie jedoch eine Liste mit Namen, eine Liste mit Dateien oder eine Liste mit demselben Datentyp übergeben möchten, können Sie ein Array oder eine Liste verwenden.

Übergeben Sie nicht mehrere Datentypen in Map

Vermeiden Sie aus genau demselben Grund die Übergabe mehrerer Datentypen in Map (Dictionary). Wenn es sich um eine statisch typisierte Sprache handelt, erstellen Sie eine Struktur oder Klasse für die Datenübertragung wie im Beispiel eines Arrays.

Bad


func getUserData(): [String: String] {
   var userData: [String: String] = [:]
   userData["name"] = "Masao Yamada"
   userData["postalCode"] = "171-0001"
   userData["address"] = "Toyoshima-ku, Tokio"
   return userData
}

[: Star: 4] Nicht auskommentieren

Löschen Sie den Code, den Sie nicht mehr benötigen, ohne ihn zu kommentieren. Es ist in Ordnung, während eines lokalen Fixes vorübergehend Kommentare abzugeben, aber im Grunde genommen nicht festzuschreiben, was Sie kommentieren.

Wenn die Anzahl der auskommentierten Zeilen zunimmt, ist der Code schwer zu lesen und nicht verwendete Teile werden während der Suche abgefangen, was sehr schädlich ist. Sie können den Verlauf von Löschungen und Änderungen mit Verwaltungstools wie Git anzeigen. Versuchen Sie daher, unnötigen Code zu löschen.

[: Star: 3] Sei dir der Richtung der Abhängigkeit bewusst

Legen Sie Regeln für Abhängigkeiten zwischen Programmen fest, um ungeplante Abhängigkeiten zu vermeiden. Es ist eine abstrakte Erklärung, aber sie hängt von den folgenden Richtungen ab und vermeidet diese entgegengesetzte Richtung.

Was ist Abhängigkeit?

Es hängt von einem Programm ab, das eine andere Klasse, ein anderes Modul, eine andere Bibliothek usw. verwendet, ohne das es nicht kompiliert werden kann oder funktioniert. Zusätzlich zur Abhängigkeit von der Kompilierungsstufe kann gesagt werden, dass es von der Spezifikation abhängt, auch wenn es unter der Voraussetzung einer bestimmten Spezifikation erstellt wird und ohne diese Spezifikation nicht funktioniert.

Hängt von Allzweckfunktionen von dedizierten Funktionen ab

Allzweckklassen sollten nicht von speziellen Klassen abhängen.

Wenn beispielsweise im Extremfall die Klasse "String" (allgemeine Funktion), die eine Zeichenfolge darstellt, von der Anmeldebildschirmklasse "LoginView" (dedizierte Funktion) abhängt, sind alle Systeme, die String verwenden, LoginView. Es wird notwendig, verschwenderisch zu arbeiten, und es wird schwierig, die String-Klasse wiederzuverwenden.

Um ein anderes Beispiel zu nennen, das häufiger vorkommt: Wenn es eine HTTPConnection-Klasse (Allzweckfunktion) für die Kommunikationsverarbeitung gibt, wird die für die Anwendung eindeutige Bildschirmklasse (dedizierte Funktion) in dieser Klasse verarbeitet. Wenn Sie es zur Beurteilung verwenden, können Sie die Klasse "HTTPConnection" nicht auf eine andere App portieren.

Vermeiden Sie es, die eigene dedizierte Bildschirmklasse oder ID der App für die Beurteilung in der Allzweckfunktionsklasse zu verwenden, da dies zu einer übermäßigen Verzweigung in der Allzweckklasse führen und diese komplizieren kann.

Platzieren Sie alle Teile, die von der Bibliothek abhängen, an einem Ort

In der heutigen Zeit ist es selbstverständlich, eine Art Open Source-Bibliothek zum Implementieren einer Anwendung zu verwenden, aber die Verarbeitung unter Verwendung einer bestimmten Bibliothek wird in einer Klasse oder Datei zusammengefasst, und verschiedene Klassen hängen nicht von der Bibliothek ab. Es ist ein Bild, das nicht von der Oberfläche, sondern vom Punkt abhängt.

Durch Konsolidieren des Codes, der die Bibliothek verwendet, an einem Ort können die Auswirkungen und Korrekturen der folgenden Änderungen minimiert werden.

Die Datenklasse hängt nicht von bestimmten Funktionen oder Spezifikationen ab

Obwohl es unter dem leidet, was ich im Abschnitt "Abhängig von Allzweckfunktionen von dedizierten Funktionen" geschrieben habe, sollten Klassen, die Daten wie DTO beibehalten sollen, so einfach wie möglich sein und von anderen Funktionen abhängen. Es ist besser, sich nicht auf eine bestimmte Spezifikation zu verlassen.

//Einfaches Beispiel für Datenklassen
struct UserInfo {
    let id: Int
    let name: String
}

Eine solch einfache Datenklasse kann über viele Ebenen hinweg verwendet und manchmal auf eine andere Anwendung portiert werden, aber zusätzliche Abhängigkeiten können sich nachteilig auswirken.

Verwenden Sie die Schnittstelle, um die Abhängigkeit von der Konkretheit zu überwinden

Viele statisch typisierte Sprachen können Schnittstellen (Protokolle) verwenden, um ihre Abhängigkeit von bestimmten Implementierungsklassen zu beseitigen. (Dieser Abschnitt gilt häufig nicht für dynamisch typisierte Sprachen, da diese häufig keine Schnittstelle haben.)

Im folgenden Beispiel hängt die Klasse "Beispiel" beispielsweise von der Klasse "HTTPConnector" ab, mit der sie kommuniziert.

Before


class Example {
    let httpConnector: HTTPConnector

    init(httpConnector: HTTPConnector) {
        self.httpConnector = httpConnector
    }

    func httpPost(url: String, body: Data) {
        httpConnector.post(url: url, body: body)
    }
}

class HTTPConnector {
    func post(url: String, body: Data) {
        //Implementierung der HTTP-Kommunikation
    }
}

Wenn die Klasse "HTTPConnector" eine Art Kommunikationsbibliothek verwendet, hängt die Klasse "Example" indirekt von dieser Kommunikationsbibliothek ab. Wenn Sie jedoch den "HTTPConnector" wie unten gezeigt in eine Schnittstelle (ein Protokoll) ändern, können Sie die "Example" -Klasse von der Kommunikationsbibliothek unabhängig machen.

After


class Example {
    let httpConnector: HTTPConnector

    init(httpConnector: HTTPConnector) {
        self.httpConnector = httpConnector
    }

    func httpPost(url: String, body: Data) {
        httpConnector.post(url: url, body: body)
    }
}

protocol HTTPConnector {
    func post(url: String, body: Data)
}

Konsolidieren Sie die Zusammenarbeit mit externen Systemen an einem Ort

Der Code für die Zusammenarbeit und die Schnittstelle zu Systemen außerhalb der Anwendung sollte so weit wie möglich an einem Ort zusammengestellt werden. Verschiedene externe Systeme können in Betracht gezogen werden, die häufigsten sind jedoch HTTP-APIs und -Datenbanken.

Es ist dasselbe, wie ich über die Abhängigkeit von Open-Source-Bibliotheken geschrieben habe, aber durch die Konsolidierung der Integration mit externen Systemen an einem Ort können die Auswirkungen und Änderungen von Änderungen im externen System minimiert werden. Auch hier wäre es schön, eine Schnittstelle (Protokoll) zu verwenden, um die Abhängigkeit von einer bestimmten Implementierungsklasse zu beseitigen.

[: Star: 3] Der Codewert sollte enum sein

Enum ist für numerische Konstanten, Zeichenfolgenkonstanten, Codewerte usw. mit mehreren Werten definiert.

Bad


func setStatus(status: String) {
    if status == "0" {
        //Verarbeitung auf Erfolg
    }
}

Good


enum Status: String {
    case success = "0"
    case error = "1"
}

func setStatus(status: Status) {
    if status == .success {
        //Verarbeitung auf Erfolg
    }
}

Wenn Sie unerwartete Werte berücksichtigen müssen

Enum kann nur vorgegebene Werte verarbeiten, für unerwartete Werte wie API-Statuscodes kann jedoch eine gewisse Verarbeitung erforderlich sein. In einem solchen Fall ist es besser, den Rohcodewert wie folgt beizubehalten, damit Enum durch Getter oder berechnete Eigenschaft erhalten werden kann.

struct Response {
    var statusCode: String
    var status: Status? {
        return Status(rawValue: statusCode)
    }
}

func setResponse(response: Response) {
   if response.status == .success {
      //Verarbeitung auf Erfolg
   } else if response.status == .error {
      //Verarbeitung zum Zeitpunkt des Fehlers
   } else {
      //Protokollausgabe des Codewerts, wenn ein unerwarteter Codewert eintrifft
      print("Statuscode: \(response.statusCode)")
   }
}

[: Star: 3] Schreibe einen Kommentar

Erklären Sie verwirrenden Code

Schreiben Sie Kommentare zu Code, der für Dritte schwer zu erkennen ist. Geben Sie in den Kommentaren eine Beschreibung des Codes für Code an, der beim Lesen durch andere nicht sofort sinnvoll ist, z. B. Code, der spezielle Spezifikationen erfüllt, oder kniffliger Code, der Fehler behebt.

Spezieller Code für Fehlerbehebungen

Es gibt Zeiten, in denen Sie einen Fehler nicht einfach beheben können und keine andere Wahl haben, als schmutzigen Code einzugeben. Wenn dies der Fall ist, hinterlassen Sie einen Kommentar, warum Sie dies für spätere Zuschauer getan haben.

In den folgenden Fällen ist es beispielsweise besser, eine Erklärung in die Kommentare zu schreiben.

magische Zahl

Der folgende Code erfordert einen Kommentar, da kein Dritter weiß, was "3" bedeutet.

Bad


if status == 3 {
   showErrorMessage()
}

Good


//Status 3 ist ein Kommunikationsfehler
if status == 3 {
   showErrorMessage()
}

Wie man Sätze schreibt, die für Kommentare geeignet sind

Entfernen Sie Wörter ohne Informationen oder doppelte Wörter

Japanisch ist eher ein Kreisverkehr, und das Schreiben von Sätzen ohne nachzudenken enthält normalerweise einige Wörter, die nicht informativ sind. Überprüfen Sie einen Satz nach dem Schreiben einmal, suchen Sie nach doppelten oder unnötigen Wörtern. Löschen Sie diese und kürzen Sie sie, falls vorhanden.

Beispielsweise können die folgenden Wörter vereinfacht und gekürzt werden, ohne ihre Bedeutung zu ändern.

―― "Kann gekürzt werden" → "Kann gekürzt werden"

Verwenden Sie keine ehrenwerten oder höflichen Worte

Je höflicher der Wortlaut auf Japanisch ist, desto mehr Zeichen gibt es. "Ich habe" hat 2 Zeichen, während "Ich habe" 4 Zeichen hat. Es gibt eine Geschmackssache, aber wenn Sie sie nicht brauchen, können Sie den Kommentar verkürzen, indem Sie keine höflichen oder ehrenwerten Worte verwenden.

Grundschülern und Ausländern erzählen

Versuchen Sie, einfache und leicht verständliche Sätze zu schreiben, ohne schwierige Wörter, Phrasen, Kanji und Fachbegriffe zu verwenden. Im Gegenteil, es ist oft schwierig, Fremdwörter in Katakana an englischsprachige Menschen zu übermitteln. Seien Sie also vorsichtig.

Es ist auch am besten, nicht so viele Fachbegriffe wie möglich zu verwenden, aber es ist in Ordnung, das zu verwenden, was zur Erläuterung des Programms erforderlich ist. Beispielsweise ist das Wort "Socket-Kommunikation" ein Fachbegriff, der jedoch möglicherweise in den Kommentaren von Programmen verwendet werden muss, die sich auf "Socket-Kommunikation" beziehen.

Wenn an dem Projekt Personen verschiedener Nationalitäten beteiligt sind, ist es gut, die Kommentare auf Englisch zu vereinheitlichen.

Sie müssen keine Klammern setzen

Das Wichtigste ist, dass es für den Leser leicht zu verstehen ist. Extreme Geschichte, wenn der Leser verstehen kann ** Es spielt keine Rolle, ob die Grammatik falsch oder das Englisch falsch ist. ** ** ** Sie müssen keine Zeit damit verbringen, wieder schöne Sätze zu schreiben, und Frank geht es gut, daher ist es wichtig, Kommentare zu schreiben, die für den Leser nützlich sind.

Besser als keine verantwortungslosen Kommentare?

Die folgenden verdächtigen Kommentare sind möglicherweise besser als nur Informationen.

//Hier liegt ein Speicherverlust vor.

//Die Verzweigung hier macht möglicherweise nicht viel Sinn. ..

//Ich weiß nicht, warum es funktioniert, aber vorerst hat dieser Code das Problem hier gelöst

[: Star: 3] Beachten Sie die Verfügbarkeit der Dienste

Stürzen Sie die Anwendung nicht ab

Generell sollte ** die App nicht fallen **.

Es mag offensichtlich erscheinen, aber moderne Programmiersprachen können leicht abstürzen. Beispielsweise stürzen viele Sprachen ab, wenn Sie in einem Array mit 3 Elementen auf die vierte zugreifen. Diese Art von Fehler tritt leicht auf und es ist nicht ungewöhnlich, dass einige unbemerkt veröffentlicht werden.

Es ist eine gute Idee, eine Ausnahme auszulösen, wenn sie sich in einem schlechten Zustand befindet, aber es ist nicht gut, wenn die gesamte Anwendung abstürzt.

In Back-End-Programmen wie APIs und Batches stoppen Ausnahmen selten das gesamte System. Daher ist es gut, Ausnahmen aktiv zu nutzen. In Front-End-Anwendungen stoppt eine Ausnahme die Anwendung vollständig. Da dies häufig vorkommt, ist es besser, nicht so oft wie möglich eine Ausnahme zu machen.

Um einen solchen Absturz so weit wie möglich zu verhindern, sind folgende Maßnahmen erforderlich.

Implementieren Sie den entsprechenden Schutz

Fügen Sie dem Code, der möglicherweise abstürzt, eine if-Anweisung hinzu. Wenn sie abstürzt, beenden Sie die Verarbeitung. Dies beinhaltet die NULL-Prüfung in nicht NULL-sicheren Sprachen wie Java.

Beispiel für eine NULL-Prüfung in Java


if (hoge != null) {
    hoge.fuga();
}

Solche Wachen haben jedoch das Risiko, Fehler zu quetschen und zu verbergen. Wenn es also einen Bestätigungsmechanismus gibt, der die Verarbeitung stoppt, wenn Betrug nur in der Entwicklungsumgebung auftritt, ist es besser, ihn stattdessen zu verwenden. Beispielsweise kann die "assert" -Funktion von Swift nur auf Bedingungen überprüft und abgestürzt werden, wenn sie ungültig ist, nur für DEBUG-Builds.

Beispiel für die Behauptung in Swift


func hoge(text: String?) {
    assert(text != nil) //Der DEBUG-Build stürzt hier ab, wenn der Text Null ist
    //Normale Verarbeitung
}

Selbst wenn Sie einen Schutz in die if-Anweisung einfügen, ist es besser, mindestens ein Protokoll auszugeben, wenn ein ungültiger Status auftritt.

Vermeiden Sie Abstürze

Erstellen Sie nach Möglichkeit eine Entwicklungsumgebung, in der es überhaupt schwierig ist, abstürzenden Code zu schreiben. Dies ist eine radikalere Lösung als die Implementierung von Wachen. Eine grundlegende Lösung für NullPointerException besteht beispielsweise darin, "Java zu beenden und NULL-sicheres Kotlin zu verwenden".

Sie können auch Erweiterungen hinzufügen, um Code sicher zu schreiben, z. B. eine optionale Klasse zu einer Sprache hinzufügen, die nicht NULL-sicher ist, ohne die Sprache ändern zu müssen.

Das Folgende ist ein Beispiel für die Erweiterung von Swift's Collection und das Hinzufügen eines Getters (Index), der nicht abstürzt, selbst wenn ein Index außerhalb des Bereichs angegeben wird und "nil" zurückgibt.

extension Collection {
    subscript (safe index: Index) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

let list = [0, 1]
print(list[safe: -1]) //Werden Sie Null, ohne zu stürzen

Häufige und häufige Abstürze wie NULL-Zugriff und Zugriff außerhalb des Bereichs eines Arrays können durch Hinzufügen einer solchen Funktion behoben werden.

Tun Sie so viel wie möglich, auch wenn falsche Daten vorliegen

Selbst wenn ein Teil der Daten einen ungültigen oder unerwarteten Zustand aufweist, sollte der Teil, der kein Problem aufweist, so weit wie möglich wie gewohnt verarbeitet werden. Stellen Sie sich beispielsweise ein System vor, mit dem Sie Bankkontodaten anzeigen können.

Wenn man ein System vergleicht, in dem nichts zu sehen ist, wenn eine der Nutzungsdaten falsch ist, und ein System, in dem der Kontostand mit den anderen Details angezeigt werden kann, auch wenn eine der Nutzungsdaten falsch ist, ist letzteres für den Benutzer natürlich bequemer.

Im Idealfall verhält sich die Anwendung wie ein guter Butler, der den Benutzer unterstützt (zumindest denke ich das). Es ist alles andere als ein guter Diakon, alle Pflichten nur durch eine unerwartete Sache aufzugeben.

** Diese Richtlinie gilt jedoch nur für die Anzeige von Informationen. ** ** ** Im Falle einer irreversiblen Aktualisierungsverarbeitung ist es sicher, die Verarbeitung abzubrechen, sobald eine Abnormalität festgestellt wird.

[: Star: 3] Schreiben Sie nicht immer und immer wieder denselben Vorgang

Durch die Kombination derselben Prozesse, die an mehreren Stellen geschrieben wurden, ist es möglich, die Codemenge zu reduzieren, die Lesbarkeit zu verbessern und die Änderungskosten zu senken.

Schneiden Sie Allzweckfunktionen in allgemeine Klassen und Funktionen aus

Allzweckfunktionen, die nicht von den Geschäftsanforderungen betroffen sind, werden ausgeschnitten und als allgemeine Klassen und Funktionen verwendet. Im Folgenden wird beispielsweise eine Funktion zum URL-Codieren einer Zeichenfolge als allgemeine Funktion definiert.

extension String {
    var urlEncoded: String {
        var allowedCharacterSet = CharacterSet.alphanumerics
        allowedCharacterSet.insert(charactersIn: "-._~")
        return addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? ""
    }
}

Minimieren Sie die Beschreibung in if- und switch-Anweisungen

Wenn beim Verzweigen eines Prozesses mit einer if-Anweisung oder einer switch-Anweisung derselbe Prozess in der Verzweigung vorhanden ist, sollte der duplizierte Teil aus der if-Anweisung oder der switch-Anweisung entfernt werden. Im Folgenden wird beispielsweise der Teil von "label.text =" sowohl im if-Block als auch im else-Block dupliziert.

Bad


if flag {
    label.text = "aaa"
} else {
    label.text = "bbb"
}

In diesem Fall kann die if-Anweisung selbst durch Entfernen des duplizierten Teils gelöscht werden.

Good


label.text = flag ? "aaa" : "bbb"

Nachteile der Gemeinsamkeit

Die Standardisierung von Logik- und Datenstrukturen hat auch Nachteile. Das unnötige wahllose Teilen desselben Prozesses führt häufig zu einer Verringerung der Wartbarkeit.

Die Standardisierung der Verarbeitung hat die folgenden Nachteile

Insbesondere die Gemeinsamkeit durch Klassenvererbung führt zu einer starken engen Kopplung und kann die Flexibilität des Programms verringern. Seien Sie also vorsichtig.

In Verbindung stehender Artikel Apropos schlafen: "Erben wir, um den Code gemeinsam zu machen" Die Idee, dass Gemeinsamkeit nur Anti-Muster erzeugt

Was sollte und sollte nicht gemeinsam sein

Wann sollte es dann gemeinsam sein und wann sollte es nicht gemeinsam sein?

Wie eingangs erwähnt, werden wir uns aktiv bemühen, Allzweckfunktionen zu standardisieren, die nicht von den Geschäftsanforderungen betroffen sind. Im Fall von Logik und Daten im Zusammenhang mit Geschäftsanforderungen ist es eine Einzelfallentscheidung, ob standardisiert werden soll oder nicht. Die folgenden Anforderungen sind jedoch eine Richtlinie für die Standardisierung.

[: Star: 2] Erstellen Sie eine Skizze, bevor Sie Code schreiben

So wie Sie beim Erstellen einer Anwendung zuerst Drahtmodelle und grobe Skizzen erstellen, können Programme effizient vorgehen, indem Sie Drahtmodelle und grobe Skizzen erstellen, bevor Sie mit dem Schreiben von Code beginnen.

Wenn die Entwicklung beginnt, wollen wir sichtbare Fortschritte machen, daher neigen wir vorerst dazu, Code zu schreiben. Code, der ohne tiefes Nachdenken geschrieben wird, führt jedoch häufig zu Nacharbeiten, da Überlegungen, unzureichende Spezifikationen und unzureichendes Design fehlen. Das Schreiben von Code scheint vorerst Fortschritte zu machen, aber schlechter Code ist am Ende oft völlig nutzlos oder sogar schädlich.

Daher ist es effizienter, zunächst den Wunsch zu ertragen, Code zu schreiben und eine grobe Skizze des Programms aus der Perspektive des gesamten Projekts zu erstellen.

Was ist eine grobe Skizze eines Programms?

Die grobe Skizze des hier erwähnten Programms setzt ein Klassendiagramm und einen Gliederungscode voraus. Dies ist jedoch nicht immer der Fall, und eine andere Form kann verwendet werden, wenn Folgendes bekannt ist.

Schreiben Sie ein Klassendiagramm

Zunächst ist es leicht zu verstehen, ob Sie ein Klassendiagramm erstellen, um die Einschluss- und Referenzbeziehungen zwischen Klassen zu berücksichtigen. Das Klassendiagramm erfordert zu diesem Zeitpunkt keine Regeln, und Sie können nach Belieben skizzieren. Es ist nicht erforderlich, die Eigenschaften und Funktionen der Klasse abzudecken, und es reicht aus, mindestens den Klassennamen, die Einschlussbeziehung, die Referenzbeziehung und die Rolle zu kennen.

Es wird empfohlen, es von Hand zu machen, da es durch Ausprobieren neu geschrieben wird. Es ist einfacher zu verstehen, wenn Sie mehrere N Seiten eines Paares von N schreiben oder den Einschluss mit einer Linie einschließen, um das UML-Diagramm einfacher und intuitiver zu gestalten.

Bestimmen Sie die Daten und die Schnittstelle, die die Klasse enthält

Sobald die Struktur der Klasse festgelegt ist, besteht der nächste Schritt darin, die Schnittstelle zwischen den von jeder Klasse gespeicherten Daten und der für die Zusammenarbeit mit der Außenwelt erforderlichen Funktion zu bestimmen.

Die aufzubewahrenden Daten sind speziell eine Instanzvariable (Mitgliedsvariable). Im Gegensatz zum Klassendiagramm sollten die Instanzvariablen zu diesem Zeitpunkt so weit wie möglich abgedeckt werden.

Wie im ersten Abschnitt erwähnt, sollten Klasseninstanzvariablen so klein wie möglich sein. Machen Sie nur die erforderlichen zu Instanzvariablen und versuchen Sie, nach dem Entwerfen nicht so viele ungeplante Instanzvariablen wie möglich hinzuzufügen.

Schreiben Sie groben Code

Sobald die Klassenstruktur festgelegt ist, schreiben Sie groben Code ohne Details. Da der Zweck hier jedoch darin besteht, die Gliederung zu bestimmen, schreiben Sie auf eine Weise, die für Sie leicht verständlich ist, ohne an eine detaillierte Grammatik gebunden zu sein. Daher wird empfohlen, in einem einfachen Editor anstatt in einer IDE zu schreiben, die nach Kompilierungsfehlern sucht.

In grobem Code reicht es aus, wenn der Verarbeitungsablauf, das Aufrufen anderer Klassen und die Schnittstelle zum Übergeben von Informationen grob bestimmt sind.

Grobes Codebeispiel


func createSession(request) -> Session? {
    let user = userTable.getByUserName(request.userName)

    if user.Konto gesperrt{
Wirf einen Fehler
    }

    if user.passward != request.password {
        userTable.Zählen Sie die Anzahl der Anmeldefehler auf
        return nil
    }

Protokollausgabe der angemeldeten Benutzerinformationen
    return Session()
}

Verwenden Sie die Schnittstelle, um groben Code in Produktcode umzuwandeln

Über die Schnittstelle können Sie die Geschäftslogik implementieren, die das Rückgrat der detaillierten Implementierung und anstehender Probleme bildet. Mit anderen Worten, es kann kompiliert und grober Code erstellt werden, damit er im Produkt so verwendet werden kann, wie es ist. Diese Methode ist sehr effektiv für Backend-Programme mit DI-Containern.

Das Folgende ist ein Beispiel für groben Code (Sprache ist Java), der die Schnittstelle mit Spring Boot verwendet, das einen DI-Container hat.

Dieses Programm lädt Fotos auf einen Dateiserver hoch und sendet die URL der Datei per E-Mail an die von der Datenbank erhaltene E-Mail-Adresse des Benutzers. Mithilfe der Schnittstelle können Sie Geschäftslogik implementieren, ohne sich für eine Datenbank, einen Dateiserver, einen Mailserver oder eine Bibliothek zu entscheiden.

Anwendungsbeispiel für die Java-Schnittstelle


@Service
class FileService {
    private final IMailer mailer;
    private final IUserTable userTable;
    private final IFileUploader fileUploader;

    //In Spring wird der Wert automatisch vom DI-Container auf das Argument des Konstruktors gesetzt.
    public FileService(IMailer mailer, IUserTable userTable, IFileUploader fileUploader) {
        this.mailer = mailer;
        this.userTable = userTable;
        this.fileUploader = fileUploader;
    }

    void uploadUserPhoto(int userId, byte[] photoData) {
        String fileURL = fileUploader.upload(userId, photoData);
        User user = userTable.get(userId);
        mailer.send(user.emailAddress, "Upload abgeschlossen", fileURL);
    }
}

interface IMailer {
    void send(String toAddress, String title, String body);
}
interface IUserTable {
    User get(int userId);
}
interface IFileUploader {
    String upload(int userId, byte[] fileData);
}

Wenn Sie einen Systemprototyp erstellen, indem Sie eine einfache Scheinimplementierung in eine solche Schnittstelle einfügen, können Sie sich flexibel und schnell entwickeln, indem Sie die Datenbank und den Server vorerst ignorieren.

Finden Sie Mängel in den Spezifikationen

Das Auffinden von Mängeln in den Spezifikationen ist auch einer der Zwecke der Erstellung einer groben Skizze.

Wenn Sie ein Programm schreiben, das auf Spezifikationen und Konstruktionsdokumenten basiert, müssen Sie berücksichtigen, dass ** Spezifikationen und Konstruktionsdokumente immer falsch sind **. Wenn Sie nach der Implementierung einen Fehler in den Spezifikationen feststellen, wird die Arbeit bis zu diesem Punkt verschwendet.

Beachten Sie daher beim Schreiben einer groben Skizze, dass die Spezifikationen keine Mängel aufweisen.

[: Stern: 2] Bewahre verwandte Gegenstände in der Nähe auf

Wenn Sie verwandten Code in demselben Verzeichnis oder in derselben Datei ablegen, müssen Sie beim Lesen von Code keine Dateien mehr finden und wechseln. Legen Sie beispielsweise Dateien, die von derselben Funktion verwendet werden, in dasselbe Verzeichnis oder machen Sie eine Klasse, die nur innerhalb einer bestimmten Klasse verwendet wird, zu einer inneren Klasse, und platzieren Sie verwandten Code in der Nähe, um das Auffinden des Codes zu erleichtern.

Gibt an, ob Verzeichnisse nach Funktion oder Typ unterteilt werden sollen

Beim Gruppieren von Dateien in Verzeichnisse und Pakete gibt es zwei Methoden: Die eine besteht darin, sie nach Funktionen zu gruppieren, und die andere darin, sie nach Dateityp zu gruppieren.

  1. Beispiel für die Gruppierung nach Dateityp
Storyboard
├ LoginViewController.storyboard
└ TimeLineViewController.storyboard

View
├ LoginView.swift
└ TimeLineView.swift

Controller
├ LoginViewController.swift
└ TimeLineViewController.swift

Presenter
├ LoginViewPresenter.swift
└ TimeLineViewPresenter.swift

UseCase
├ LoginViewUseCase.swift
└ TimeLineViewUseCase.swift
  1. Beispiel für eine Zusammenfassung nach Funktionen
Login
├ LoginViewController.storyboard
├ LoginView.swift
├ LoginViewController.swift
├ LoginViewPresenter.swift
└ LoginViewUseCase.swift

TimeLine
├ TimeLineView.swift
├ TimeLineViewController.storyboard
├ TimeLineViewController.swift
├ TimeLineViewPresenter.swift
└ TimeLineViewUseCase.swift

Beide obigen Beispiele haben dieselben Dateien, jedoch unterschiedliche Verzeichnisunterteilungen.

Die Methode zum Gruppieren nach Dateityp ist für das Layer-Design leicht zu verstehen, und Dateien, die von mehreren Funktionen verwendet werden, können ohne Konsistenz angeordnet werden. Sie sind jedoch kompliziert, da nach Dateien in 5 Verzeichnissen gesucht werden muss, um den Code auf einem Bildschirm zu lesen. Es gibt auch Nachteile.

Es ist nicht der beste Weg, diese beiden Methoden zu unterteilen, und es ist notwendig, sie je nach Situation richtig zu verwenden. Wenn die Dateigruppe jedoch nicht von anderen Funktionen verwendet wird, ist es möglicherweise einfacher, sie zu entwickeln, indem die Verzeichnisse nach der Funktion gruppiert werden. Es gibt viele.

Balance zwischen lockerer und fester Kupplung

Im Allgemeinen sind lose gekoppelte Programme vielseitiger und wartbarer, aber die Richtlinie in diesem Abschnitt erhöht umgekehrt den Grad der Programmkopplung. Wenn der Grad der Kopplung zu hoch ist, wird eine Datei zu groß oder zu eng gekoppelt, was die Vielseitigkeit und Wartbarkeit beeinträchtigt. Eine unnötige und übermäßige lose Kopplung hat jedoch auch den Nachteil, dass die Kosten die Vorteile überwiegen. ..

Am besten ist ein Problem, das von Fall zu Fall nicht richtig beantwortet wird. Es ist jedoch erforderlich, den Code in einem angemessenen Gleichgewicht zusammenzustellen.

[: Stern: 2] Erstellen Sie einen Komponententest

UnitTest ist hier kein manueller, sondern ein Programmtest wie JUnit. Erstellen Sie UnitTest von Beginn der Entwicklung an aktiv für kleine Funktionen wie die Datenverarbeitung.

Vorteile von UnitTest

Reduzieren Sie die Gesamtkosten, indem Sie dies im Voraus überprüfen

Wenn im System ein Problem festgestellt wird, dauert die Untersuchung der Ursache umso länger, je größer die Funktion ist. Je später der Prozess, desto größer ist außerdem der Einfluss der Programmänderung auf andere Funktionen und die Punkte, die berücksichtigt werden müssen. Diese Kosten können reduziert werden, indem der Betrieb kleiner Funktionen mit UnitTest frühzeitig überprüft wird.

Überprüfen Sie den Betrieb von Prozessen, die sich nur schwer bewegen und testen lassen

Die Verarbeitung unregelmäßiger Daten und Zustände ist oft schwierig zu testen, indem die Anwendung tatsächlich ausgeführt wird. Selbst in solchen Tests können Sie mit UnitTest unregelmäßig Situationen programmgesteuert erstellen und testen.

Überprüfen Sie die Verwendbarkeit von Klassen und Funktionen

Durch das Schreiben von UnitTest verwendet der Programmierer tatsächlich die zu testende Klasse oder Funktion im Programm. Indem Sie die Benutzerfreundlichkeit von Klassen und Funktionen zu diesem Zeitpunkt kennenlernen, können Sie die Benutzeroberfläche von Klassen und Funktionen in einer benutzerfreundlicheren Form auffrischen. Als Nebeneffekt erfordert das Ausführen von UnitTest, dass das Programm lose gekoppelt ist, was eine gute Möglichkeit sein kann, sauberes Design zu studieren.

Spezifikationen klären

Sie müssen bestimmen, was richtig ist, um einen Test durchzuführen. Durch die Prüfung des Tests werden die Spezifikationen und Probleme, wie z. B. was im unregelmäßigen Fall zu tun ist, klarer.

UnitTest wird durchgeführt, um die Entwicklung zu beschleunigen

UnitTest dient nicht der Qualitätssicherung, sondern der Beschleunigung der Gesamtentwicklung.

Daher ist es nicht erforderlich, den gesamten Code abzudecken, und es ist nicht erforderlich, einen Test zu erzwingen, der schwer zu implementieren ist. Insbesondere wenn Benutzeroberfläche, Kommunikation, Datenbank usw. betroffen sind, muss UnitTest unterschiedlich berücksichtigt werden. Daher besteht das Risiko, dass die Entwicklungseffizienz abnimmt, wenn ein solcher UnitTest erstellt wird.

Sie sollten auch aufhören zu denken, dass Qualität garantiert ist, weil Sie UnitTest durchgeführt haben. Unabhängig davon, wie viel UnitTest Sie durchführen, müssen Sie eventuell einen manuellen Test durchführen.

Testen Sie kleine Funktionen

UnitTest wird grundsätzlich für kleine und unabhängige Funktionen durchgeführt. Manchmal erstellen Sie ein Programm, das automatisch eine große Anzahl von Vorgängen überprüft, aber einen anderen Zweck hat als UnitTest.

[: Star: 2] Verwenden Sie numerische Klassen, um die Geschäftslogik zu berechnen

Verwenden Sie keine grundlegenden Datentypen wie Int, Float und Double für Geschäftslogikberechnungen, sondern numerische Klassen wie BigDecimal für Java und NSDecimalNumber und Decimal für Swift.

Gleitkommazahlen wie Double und Float werden grundsätzlich nicht für die Geschäftslogik verwendet

Gleitkommazahlen wie Double und Float sollten nicht einfach verwendet werden, da sie Fehler verursachen. Gleitkommawerte werden im Allgemeinen nur für die Zeichnungsverarbeitung verwendet, z. B. für die Koordinatenberechnung und die wissenschaftliche Berechnung, bei der die Leistung im Vordergrund steht. Wenn Sie einen Fehler machen, verwenden Sie Double oder Float nicht, um den Betrag zu berechnen.

Verwenden Sie nicht 32bit Int, um den Betrag zu berechnen

Zum Beispiel ist Java Int 32 Bit und der maximale Wert liegt bei 2,1 Milliarden, was zu klein ist, um den Geldbetrag zu verarbeiten. 2,1 Milliarden Yen sind eine Menge Geld, aber wenn Sie ein Buchhalter auf Unternehmensebene sind, ist der Geldbetrag, der diesen Betrag übersteigt, normal und kein Betrag, der kein persönliches Gut sein kann.

Da integrierte Ganzzahltypen wie Int eine Obergrenze für die Anzahl der Ziffern haben, vermeiden Sie deren Verwendung in der Geschäftslogik so weit wie möglich. Überlegen Sie bei der Verwendung sorgfältig, ob es Fälle gibt, in denen Ziffern überlaufen. Für Geldbeträge sollte die Verwendung eines 64-Bit-Integer-Werts (Long-Typ für Java) fast ausreichend sein. Der Maximalwert eines 64-Bit-Integer-Werts beträgt ungefähr 900 K, sodass sogar das 100-fache des US-Staatshaushalts (ungefähr 400 Billionen Yen) problemlos berücksichtigt werden kann.

In Verbindung stehender Artikel Warum sollte ich BigDecimal verwenden

[: Star: 2] Erhöhen Sie die Anzahl der Klassen und Schnittstellen nicht zu stark

Sie sollten versuchen, die Klasse nicht zu groß zu machen, aber es gibt die folgenden Nachteile, wenn Sie die Klasse übermäßig teilen und die Anzahl der Klassen und Schnittstellen zu stark erhöhen.

Beim Entwerfen einer Klassen- oder Schichtstruktur müssen diese Nachteile berücksichtigt werden, und das Entwerfen der Vorteile überwiegt die Nachteile.

Zeigen Sie, um die Klasse auszuschneiden

Wenn Sie eine Funktion in eine Klasse ausschneiden, ist die Funktion wahrscheinlich eine der folgenden.

Im Gegenteil, es ist sehr wahrscheinlich, dass es besser ist, eine Gruppe von Funktionen bereitzustellen, die nicht wiederverwendbar sind und nicht unabhängig voneinander als eine Klasse getestet oder bereitgestellt werden, ohne sie in Klassen zu unterteilen. Lose Kopplungsfunktionen tragen zur Aufrechterhaltung der Systemwartbarkeit bei, es gibt jedoch Kosten und Nachteile, und lose Kopplungen sind eng gekoppelten nicht immer überlegen.

In einer iOS-App gibt es beispielsweise ein Entwurfsmuster, mit dem ein ViewController und eine benutzerdefinierte Ansicht für einen bestimmten Bildschirm erstellt werden. ViewController und View sind jedoch immer einzeln verknüpft und nicht wiederverwendbar. Da es nicht unabhängig getestet oder bereitgestellt wird, ist es häufig besser, es in einer Klasse zu kombinieren, als es in View und ViewController zu unterteilen.

Imitieren Sie berühmte Designmuster nicht mechanisch

Eine übermäßige Unterteilung der Klassen ist häufig das Ergebnis der mechanischen Nachahmung bekannter Entwurfsmuster wie DDD und sauberer Architektur. Bücher und Artikel, in denen Entwurfsmuster eingeführt werden, betonen in der Regel nur die guten Punkte des Entwurfsmusters und nicht die schlechten oder problematischen Punkte. Das optimale Design hängt von der Größe des Produkts und des Teams ab. Anstatt das vorhandene Designmuster mechanisch zu imitieren, reicht es aus, es auf unser Produkt anzuwenden und zu prüfen, ob die Vorteile die Nachteile überwiegen. Erwägen. Es ist jedoch schwierig, die Nachteile eines bestimmten Entwurfsmusters zu verstehen, ohne es einmal zu versuchen. Optimale Entwurfsmuster müssen schrittweise durch einen PDCA-Zyklus aus Überprüfung, Ausführung, Feedback und Verbesserung verfeinert werden.

Wenden Sie nicht gleichmäßig dieselbe Schichtstruktur auf die gesamte Anwendung an

Zum Beispiel ist es sinnlos, viele Klassen und komplexe Architekturen auf ein Programm anzuwenden, das nur Hello World ausgibt. Es gibt nicht viel zu tun, also gibt es Klassen und Ebenen, die nichts tun.

Welche Art von Klasse oder Schicht geeignet ist, hängt von der Art und Komplexität der zu implementierenden Funktion ab. ** Es gibt keine geeignete Klassenstruktur oder Architektur, die zu irgendetwas passt. ** ** **

Eine typische Anwendung verfügt über mehrere Bildschirme und Funktionen, von denen jeder unterschiedliche Eigenschaften und Komplexitäten aufweist. Wenn Sie also auf alle dieselbe Schichtstruktur anwenden, führt dies zu Verschwendung und Nichtübereinstimmung. Anstatt auf alle Funktionen dieselbe Konfiguration anzuwenden, ist es daher besser, flexibler zu sein und für jede Funktion das geeignete Design auszuwählen.

Selbst wenn die Funktionen identisch sind, ändert sich die beste Konfiguration, wenn sich die Anforderungen und Spezifikationen ändern. Mutiges Refactoring und sogar das vollständige Aufgeben von vorhandenem Code sind manchmal erforderlich, um ein gutes Design aufrechtzuerhalten.

Ebenenverknüpfungen

Eine Möglichkeit, die Anzahl der Dateien in einer Ebenenarchitektur, z. B. einer sauberen Architektur, zu verringern, besteht darin, Ebenenverknüpfungen zuzulassen. Selbst wenn das Programm die folgende Ebenenstruktur aufweist und nichts mit UseCase und Presenter zu tun hat, kann der Controller direkt auf die Entität verweisen.

Wie im vorherigen Abschnitt erwähnt, ist es nicht erforderlich, die Ebenenstruktur innerhalb der Anwendung zu vereinheitlichen, und ich denke, dass Sie sie je nach Funktion freier ändern können. Beziehen Sie sich jedoch, wie im Abschnitt "Kenntnis der Abhängigkeitsrichtung" beschrieben, nicht auf die Ansicht von der Entität in die entgegengesetzte Richtung.

Ersetzen Sie die Schnittstelle (Protokoll) durch Schließen

Wenn die Sprache Funktionen als Argumente übergeben kann, z. B. eine Sprache vom Funktionstyp, kann eine einfache Schnittstelle (Protokoll) mit nur einer Methode durch Übergabe oder Schließen von Funktionen ersetzt werden. Das Folgende ersetzt die Ausgabe des Präsentators vom Protokoll zur Funktionsübergabe.

Before


class LoginPresenter {
    let output: LoginOutput
    init(output: LoginOutput) {
        self.output = output
    }
    func login() {
        output.loginCompleted()
    }
}
protocol LoginOutput {
    func loginCompleted()
}

After


class LoginPresenter {
    func login(onComplete: () -> Void) {
        onComplete()
    }
}

Erstellen Sie nicht mehrere ähnliche Datenmodelle

Wenn Sie zu sehr von der Schichtentrennung besessen sind, erhalten Sie möglicherweise mehrere ähnliche Datenmodelle ohne Bedeutung. Zum Beispiel gibt es eine "User" -Klasse in der Domänenschicht, es gibt ein "UserDTO", um diese Informationen an UseCase zu übergeben, und es gibt ein "UserViewModel", das in View verwendet werden kann, aber die drei haben fast den gleichen Code. Es ist ein Fall. In einigen Fällen müssen sie getrennt werden, auch wenn sie fast gleich sind. In anderen Fällen können sie wiederverwendet werden, ohne getrennt zu werden. Wenn die folgenden Bedingungen erfüllt sind, kann das obere Datenmodell über mehrere Ebenen hinweg verwendet werden.

[: Star: 2] Fang die Ausnahme und drücke sie nicht

Grundsätzlich wird eine Ausnahme an den Anrufer gesendet, ohne sie abzufangen, und die Fehlerbehandlung wird gemeinsam in der oberen Schicht durchgeführt. Beachten Sie beim Abfangen einer Ausnahme Folgendes.

Der Inhalt in diesem Abschnitt stimmt etwas nicht mit dem überein, was im Abschnitt "Kenntnis der Verfügbarkeit von Diensten" beschrieben ist. Es ist jedoch am besten, wenn die oberen Ebenen Ausnahmen ordnungsgemäß verarbeiten können.

Die im Abschnitt "Kenntnis der Verfügbarkeit von Diensten" beschriebene Richtlinie besteht darin, das Auslösen von Ausnahmen zu vermeiden, wenn die oberen Ebenen Ausnahmen möglicherweise nicht ordnungsgemäß verarbeiten können, was zu einem systemweiten Absturz führen kann. Ding.

Verwenden Sie keine Ausnahmen für die Verzweigungslogik

Obwohl die Ausnahme in der oberen Ebene behandelt wird, verwenden wir die Ausnahme nicht mehr für bedingte Verzweigungen wie die if-Anweisung im normalen Kontrollfluss. Ausnahmen sind nur Ausnahmen, und Sie sollten versuchen, sie nur in Ausnahmesituationen zu verwenden.

So machen Sie Java Check Exceptions (Check Exceptions) leise

Die Java-Standardausnahme wird zu einer geprüften Ausnahme (geprüfte Ausnahme), und throw zwingt die aufrufende Funktion, entweder abzufangen oder erneut zu werfen. Wenn Sie versuchen, eine Ausnahme auf der obersten Ebene zu behandeln, müssen Sie dem Ausdruck, der den Aufrufer der Funktion aufruft, in der die Ausnahme auftritt, "throw" hinzufügen.

Manchmal ist dies ärgerlich, aber wenn Sie die Ausnahme nicht abfangen und ausblenden möchten, können Sie sie in eine "RuntimeException" einschließen und auslösen, ohne Änderungen am Aufrufer vorzunehmen.

try {
    "ABC".getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
    //IllegalStateException ist eine RuntimeException
    throw new IllegalStateException(e);
}

[: Star: 1] Machen Sie Variablen und Funktionen, auf die von außen nicht verwiesen wird, privat

Machen Sie Eigenschaften und Funktionen, die nicht von außerhalb der Klasse verwendet werden, privat oder geschützt.

Es ist wünschenswert zu verstehen, wie eine Klasse durch Code-Vervollständigung verwendet wird, ohne den Code oder die Dokumentation zu betrachten. Wenn Sie sie jedoch nicht privat oder geschützt machen, werden Eigenschaften und Funktionen, die nicht von außen verwendet werden sollen, als Kandidaten für die Code-Vervollständigung angezeigt, was die Verwendung erschwert ..

Einige Programmiersprachen verfügen jedoch nicht über Zugriffsmodifikatoren wie "privat". In diesem Fall ist es besser, "privat" aufzugeben, als "privat" mit einer speziellen Implementierung zu implementieren, wobei einfachem und Standardcode Vorrang eingeräumt wird. Wenn Sie wirklich privat einführen möchten, wird empfohlen, zunächst die Sprache zu ändern (z. B. von JavaScript zu TypeScript).

Da die private Methode häufig sauber implementiert wird, indem die Verarbeitung auf eine öffentliche Methode einer anderen Klasse reduziert wird, sollten Sie die Klassen trennen, wenn es sich um eine allgemeine Verarbeitung handelt oder die Klassengröße groß ist.

In Verbindung stehender Artikel Keine private Methode erforderlich

[: Star: 1] Fügen Sie keine komplizierten Beschreibungen in ternäre Operatoren ein

Das Verschachteln von ternären Operatoren ist verwirrend und sollte vermieden werden.

Bad


flag ? subFlag ? a : b : c

Wenn eine Verschachtelung erforderlich ist, ist es ratsam, das Operationsergebnis in der Mitte in eine lokale Variable (erklärende Variable) zu setzen, um die Verschachtelung zu beseitigen.

Good


let aOrB = subFlag ? a : b
flag ? aOrB : c

Wenn der ternäre Operator einen langen Ausdruck oder eine Kette langer Funktionen enthält, fügen Sie das Ergebnis von jedem in eine lokale Variable ein und verwenden Sie es dann im ternären Operator.

Bad


flag ? (1 + 2) * 3 - 4 : 5 % 6 * 7

Good


let a = (1 + 2) * 3 - 4
let b = 5 % 6 * 7
flag ? a : b

In Verbindung stehender Artikel Der ternäre Operator?: Ist böse.

[: Star: 1] Schreiben Sie nicht richtig wahr / falsch

Anfänger neigen dazu, "wahr" "falsch" in ihren Code wie folgt zu schreiben.

Bad


var isZero: Bool {
   if number == 0 {
      return true
   } else {
      return false
   }
}

Da oben "number == 0" Bool zurückgibt, ist es jedoch nicht erforderlich, true / false zu schreiben, und es kann wie folgt präziser geschrieben werden.

Good


var isZero: Bool {
   return number == 0
}

In Fällen, in denen true immer zurückgegeben wird oder in denen false immer zurückgegeben wird, muss true / false in Solid geschrieben werden. Wenn Sie jedoch ein Urteilsergebnis wie im obigen Beispiel zurückgeben, vermeiden Sie es, true / false in Solid zu schreiben.

Diese Schreibmethode ist jedoch für Anfänger schwer zu verstehen. Wenn Sie einen Anfänger in Ihrem Team haben, ist es eine gute Idee, dies zu erklären.

In Verbindung stehender Artikel Ich möchte eine leserfreundliche if-Erklärung schreiben

[: Star: 1] Verwenden Sie nicht den Codewert von enum

Wie Swifts enum rawValue hat enum oft einen Codewert. Wenn dieser Codewert jedoch zur Beurteilung verwendet wird, wird die Bedeutung der Existenz von Enum halbiert.

Enum Probe


enum Status: String {
   case success = "0"
   case error = "1"
}

Wenn es beispielsweise eine Aufzählung mit einem Codewert wie oben beschrieben gibt, verwenden Sie die Aufzählung direkt, anstatt den Codewert zur Beurteilung zu verwenden.

Bad


if status.rawValue == "0" {
}

Good


if status == .success {
}

Selbst bei der Übergabe als Argument wird der Codewert nicht verwendet und Enum wird direkt übergeben.

Bad


checkStatus(status.rawValue)

Good


checkStatus(status)

Sollte enum nicht überhaupt einen Codewert haben?

Dies steht nicht im Einklang mit dem Abschnitt "Codewert auf Aufzählung setzen". Wenn Aufzählung jedoch überhaupt keinen Codewert hat, tritt ein solcher Fehler nicht auf.

In Anbetracht der Tatsache, dass die Codewerte von DB und API externe Spezifikationen sind und von den Anwendungsspezifikationen getrennt werden sollten, ist es besser, die Codewerte in der Ebene "Repository" in "enum" zu konvertieren und die Codewerte nicht an die Aufzählung anzugeben. werden.

[: Star: 1] Verwenden Sie das Tool zur Überprüfung des statischen Codes

Verwenden Sie aktiv statische Codeprüfungen wie Lint.

Mit der Überprüfung des statischen Codes können Sie den Code kostengünstig und ohne menschliches Eingreifen überprüfen. Daher ist es besser, dies zu tun. Sie können nicht nur Probleme erkennen, sondern auch erfahren, welche Art von Code ein Problem aufweist.

Die Überprüfung des statischen Codes hat jedoch auch Nachteile, und es kann sehr lange dauern, bis alles ernsthaft überprüft wird, wenn eine große Anzahl von Warnungen ausgegeben wird. Nur weil eine Warnung ausgegeben wird, bedeutet dies nicht, dass ein unmittelbares Problem vorliegt. Daher ist es möglich, dass sich die Qualität der Anwendung nicht wesentlich ändert, selbst wenn die Warnung im Laufe der Zeit zerstört wird.

Es ist möglicherweise besser, an die Überprüfung des statischen Codes zu denken, um Probleme zu finden.

[: Star: 0] Andere verschiedene Praktiken

Ich werde nicht ins Detail gehen, sondern andere verschiedene Praktiken auflisten.

: information_source: Auftragsinformationen

Was haben Sie gedacht? Dieser Artikel ist die Codierungsrichtlinie von Altnotes Co., Ltd., die ich vertrete.

Wir sind ein Unternehmen mit 2 Mitarbeitern, einschließlich des Vertreters. Derzeit suchen wir Ingenieure für iOS / Android. Wenn Sie interessiert sind, schauen Sie sich bitte die Jobinformationen an!

Alto Notes Jobinformationen

Recommended Posts

Wie man guten Code schreibt
So schreiben Sie leicht verständlichen Code [Zusammenfassung 3]
Wie schreibe ich Rails
Wie schreibe ich Docker-Compose
Wie schreibe ich Mockito
So schreiben Sie eine Migrationsdatei
Wie man Code schreibt, der objektorientiertes Ruby denkt
So schreiben Sie Testcode mit Basic-Zertifizierung
Wie schreibe ich einen Java-Kommentar
[Refactoring] So schreiben Sie Routing
Wie schreibe ich Junit 5 organisiert
Wie schreibe ich Rails Seed
Wie schreibe ich Rails Routing
Java # 6 studieren (Wie man Blöcke schreibt)
[Rails] Wie schreibe ich eine Ausnahmebehandlung?
So schreiben Sie eine Java-Variablendeklaration
[Basic] So schreiben Sie ein Dockerfile Selbstlernend ②
[R Spec on Rails] So schreiben Sie Testcode für Anfänger von Anfängern
Zusammenfassung zum Schreiben von Anmerkungsargumenten
[Einführung in Java] So schreiben Sie ein Java-Programm
So rufen Sie Swift 5.3-Code von Objective-C auf
[Java] Wie man Dateien ausgibt und schreibt!
Schreiben Sie Code, der schwer zu testen ist
So schreiben Sie den Spring AOP Point Cut Specifier
[SpringBoot] So schreiben Sie einen Controller-Test
So zählen Sie UTF-8-Codepunkte schnell
AtCoder heißt TLE und spricht darüber, wie man schönen Code schreibt
JDBC Versprechen und Schreibbeispiel
Schienen: Wie man eine Rechenaufgabe schön schreibt
[Java FX] So schreiben Sie Eclipse-Berechtigungen in build.gradle
[Rails] Wie schreibe ich, wenn ich eine Unterabfrage mache?
Schreiben Sie einfach zu wartenden Code (Teil 1)
Grundlagen der Java-Entwicklung ~ Schreiben von Programmen * Übung 1 ~
Farbcodierung der Konsolenausgabe in Eclipse
Wie schreibe ich eine if-Anweisung, um die Lesbarkeit von Java zu verbessern?
JUnit 5: Wie man Testfälle in enum schreibt
Beispiel für die Implementierung der F06-Implementierung in Echtzeit
Schreiben Sie einfach zu pflegenden Code (Teil 4)
Wie schreibe ich React Native Bridge ~ Android-Version ~
[Java] Memo zum Schreiben der Quelle
Wie schreibe ich Java String # getBytes in Kotlin?
Hinweise zum Schreiben von Kommentaren auf Englisch
Schreiben Sie einfach zu wartenden Code (Teil 3)
So entwickeln Sie OpenSPIFe
So rufen Sie AmazonSQSAsync auf
Wie benutzt man rbenv?