Was bedeutet "hoher Zusammenhalt"?

28

Ich bin ein Student, der kürzlich als Praktikant bei einer Softwareentwicklungsfirma angefangen hat. Zurück an der Universität sagte einer meiner Professoren, dass wir uns bemühen müssen, "geringe Kopplung und hohe Kohäsion" zu erreichen.

Ich verstehe die Bedeutung der niedrigen Kopplung. Es bedeutet, den Code von separaten Komponenten getrennt zu halten, damit eine Änderung an einer Stelle den Code an einer anderen Stelle nicht beschädigt.

Aber was ist mit hohem Zusammenhalt gemeint? Wenn es bedeutet, die verschiedenen Teile derselben Komponente gut miteinander zu integrieren, verstehe ich nicht, wie das vorteilhaft wird.

Was ist mit hoher Kohäsion gemeint? Kann ein Beispiel erklärt werden, um seine Vorteile zu verstehen?

Max
quelle
1
Beantwortet der Wikipedia-Artikel Ihre Frage nicht ausreichend? en.wikipedia.org/wiki/Cohesion_(computer_science)
Eoin Carroll
Es gibt einen guten Artikel dazu unter: msdn.microsoft.com/en-us/magazine/cc947917.aspx
NoChance
1
@EoinCarroll: Leider enthält der Wikipedia-Artikel im Moment keine konkreten Beispiele , mit denen neue Programmierer arbeiten können. Die Theorie ist gut und alles, aber sie bleibt erst dann wirklich bestehen, wenn Sie die Fehler im Zusammenhang mit der geringen Kohäsion begangen haben. Hohe Kohäsion ist eines dieser Themen, bei denen ich mehrere Jahre lang programmiert habe, um zu verstehen, warum es wichtig ist und wie ich es erreichen kann.
Spoike
Ich habe Cohesion erst richtig verstanden, als ich Clean Code gelesen habe. Du solltest auch.
Sebastian Redl

Antworten:

25

Eine Möglichkeit, die Kohäsion in Bezug auf OO zu betrachten, besteht darin, dass die Methoden in der Klasse eines der privaten Attribute verwenden. Mithilfe von Metriken wie LCOM4 (Mangel an kohäsiven Methoden), auf die gnat in dieser Antwort hingewiesen hat , können Sie Klassen identifizieren, die umgestaltet werden könnten. Der Grund, warum Sie Methoden oder Klassen umgestalten möchten, um eine größere Kohäsion zu erzielen, besteht darin, dass das Code-Design für andere einfacher zu verwenden ist . Vertrau mir; Die meisten Techniker und Wartungsprogrammierer werden Sie lieben, wenn Sie diese Probleme beheben.

Sie können in Ihrem Erstellungsprozess Tools wie Sonar verwenden , um eine geringe Kohäsion in der Codebasis zu identifizieren. Es gibt ein paar sehr häufig Fälle , dass ich mir vorstellen kann , wo Methoden sind niedrig in „Kohäsionskraft“ :

Fall 1: Die Methode ist überhaupt nicht mit der Klasse verbunden

Betrachten Sie das folgende Beispiel:

public class Food {
   private int _foodValue = 10;

   public void Eat() {
     _foodValue -= 1;
   }

   public void Replenish() {
     _foodValue += 1;
   }

   public void Discharge() {
     Console.WriteLine("Nnngghhh!");
   }
}

Einer der Methoden Discharge()fehlt der Zusammenhalt, da sie keines der privaten Mitglieder der Klasse berührt. In diesem Fall gibt es nur ein privates Mitglied: _foodValue. Wenn es mit den Interna nichts zu tun hat, gehört es dann wirklich dorthin? Die Methode könnte in eine andere Klasse verschoben werden, die zB benannt werden könnte FoodDischarger.

// Non-cohesive function extracted to another class, which can
// be potentially reused in other contexts
public FoodDischarger {
  public void Discharge() {
    Console.WriteLine("Nnngghhh!");
  }
}

Wenn Sie dies in Javascript tun, kann die Entladung eine freie Funktion sein, da Funktionen erstklassige Objekte sind:

function Food() {
    this._foodValue = 10;
}
Food.prototype.eat = function() {
    this._foodValue -= 1;
};
Food.prototype.replenish = function() {
    this._foodValue += 1;
};

// This
Food.prototype.discharge = function() {
    console.log('Nnngghhh!');
};
// can easily be refactored to:
var discharge = function() {
    console.log('Nnngghhh!');
};
// making it easily reusable without creating a class

Fall 2: Gebrauchsklasse

Dies ist tatsächlich ein häufiger Fall, der den Zusammenhalt bricht. Jeder liebt Utility-Klassen, aber diese weisen normalerweise auf Designfehler hin und machen die Pflege der Codebasis in den meisten Fällen schwieriger (aufgrund der hohen Abhängigkeit, die mit Utility-Klassen verbunden ist). Betrachten Sie die folgenden Klassen:

public class Food {
    public int FoodValue { get; set; }
}

public static class FoodHelper {

    public static void EatFood(Food food) {
        food.FoodValue -= 1;
    }

    public static void ReplenishFood(Food food) {
        food.FoodValue += 1;
    }

}

Hier sehen wir, dass die Utility-Klasse auf eine Eigenschaft in der Klasse zugreifen muss Food. Die Methoden in der Utility-Klasse haben in diesem Fall überhaupt keine Kohäsion, da sie für ihre Arbeit externe Ressourcen benötigen. Wäre es in diesem Fall nicht besser, die Methoden in der Klasse zu haben, mit der sie arbeiten (ähnlich wie im ersten Fall)?

Fall 2b: Versteckte Objekte in Dienstprogrammklassen

Es gibt einen anderen Fall von Dienstprogrammklassen, in denen nicht realisierte Domänenobjekte vorhanden sind. Die erste Reaktion eines Programmierers beim Programmieren der String-Manipulation besteht darin, eine Utility-Klasse dafür zu schreiben. Wie die hier, die einige gebräuchliche Zeichenfolgendarstellungen überprüft:

public static class StringUtils {

  public static bool ValidateZipCode(string zipcode) {
    // validation logic
  }

  public static bool ValidatePhoneNumber(string phoneNumber) {
    // validation logic
  }

}

Was die meisten hier nicht erkennen, ist, dass eine Postleitzahl, eine Telefonnummer oder eine andere Zeichenfolgenrepräsentation selbst ein Objekt sein kann:

public class ZipCode {
    private string _zipCode;
    public bool Validates() {
      // validation logic for _zipCode
    }
}

public class PhoneNumber {
    private string _phoneNumber;
    public bool Validates() {
      // validation logic for _phoneNumber
    }
}

Die Idee, dass Sie nicht direkt mit Strings umgehen sollten, wird in diesem Blogpost von @codemonkeyism detailliert beschrieben , hängt jedoch eng mit der Kohäsion zusammen, da die Art und Weise, wie Programmierer Strings verwenden, indem Logik in Utility-Klassen eingefügt wird.

Spoike
quelle
Wenn wir jetzt nur unsere ORMs dazu bringen könnten, unsere benutzerdefinierten ZipCode- und PhoneNumber-Klassen richtig zu handhaben: |
Pete
+1 gute Beispiele. Für den letzten Punkt siehe auch sourcemaking.com/refactoring/primitive-obsession
AlexFoxGill
@Pete Ihr ORM wird dies tun, wenn Sie Konvertierungsoperatoren vom String zum definierten Typ (ZipCode, PhoneNumber) bereitstellen
Marcus
9

Hoher Zusammenhalt bedeutet, ähnliche und verwandte Dinge zusammenzuhalten, Teile zu verbinden oder zu verschmelzen, die Inhalt, Funktionalität, Grund oder Ziel gemeinsam haben . Mit anderen Worten, eine geringe Kohäsion könnte beispielsweise eine Funktion / Klasse / Code-Entität bedeuten, die mehreren Zwecken dient, anstatt " auf den Punkt " zu kommen. Eine der tragenden Ideen ist, eine Sache zu tun und sie gut zu machen . Andere könnten die offensichtliche Tatsache beinhalten, dass Sie ähnliche Funktionen an vielen Stellen nicht replizieren. Dies verbessert auch die Lokalität der Codebasis. Bestimmte Dinge befinden sich an einem bestimmten Ort (Datei, Klasse, Satz von Funktionen, ...), anstatt verstreut zu sein.

Stellen Sie sich als Beispiel eine Klasse vor, die zwei oder drei Zwecken dient: Lädt / speichert Ressourcen (z. B. eine Datei) und analysiert dann den Inhalt und zeigt ihn an. Eine solche Klasse weist eine geringe Kohäsion auf, da sie mindestens zwei separate Aufgaben verwaltet, die überhaupt nicht zusammenhängen (Datei-E / A, Analyse und Anzeige). Bei einem Entwurf mit hoher Kohäsion können unterschiedliche Klassen zum Laden und Speichern der Ressource, zum Analysieren und zum Anzeigen der Ressource verwendet werden.

Andererseits zielt eine niedrige Kopplung darauf ab, verschiedene Dinge getrennt zu halten, so dass sie so wenig wie möglich miteinander interagieren, was die Komplexität verringert und das Design vereinfacht.

zxcdw
quelle
7

Dies bedeutet, dass die Teile eines bestimmten Objekts in enger Beziehung zur Funktion des Objekts stehen. Dies bedeutet, dass im Objekt nur sehr wenig oder gar keine Verschwendung in Bezug auf Funktion oder Verantwortung vorhanden ist. Dies kann wiederum das Verständnis dafür verbessern, wofür das betreffende Objekt verwendet werden soll.

Weltingenieur
quelle
Gilt das nicht auch für eine höhere Ebene als nur für Objekte? zB Gruppieren von Objekten / Funktionen, die sich auf eine Aufgabe beziehen, in Namespaces?
Stijn