Installation

Der IOSP Analyzer muss als NuGet Paket zu jedem Projekt hinzugefügt werden, welches analysiert werden soll. Es folgen kurze Anleitungen zu den gängigen IDEs. Weiter unten gibt’s dazu auch ein Video.

Rider

In der folgenden Abbildung ist der NuGet Manager von JetBrains Rider zu sehen. Wählt man als Scope Solution, kann man den Analyzer mit einem Klick zu allen enthaltenen Projekten hinzufügen.

NuGet

VSCode (Visual Studio Code)

In VSCode fügt man NuGet Pakete am besten über die Kommandozeile hinzu:

Mit „cd“ in das Verzeichnis wechseln, in dem eine .csproj Datei liegt, zu dem das Paket hinzugefügt werden soll.

				
					dotnet add package CleanCodeDeveloper.Analyzers


				
			

Alternativ kann ein Verzeichnis angegeben werden, in dem die .csproj Datei liegt:

				
					dotnet add <directory> package CleanCodeDeveloper.Analyzers
				
			

Das NuGet Paket verwenden

Installieren Sie das Paket in Ihre Implementierungsprojekte. Auch in Testprojekten kann es hilfreich sein. Der Analyzer ignoriert Aufrufe aus dem Testframework, so dass Fehlmeldungen reduziert werden.

Das folgende Bild zeigt den Analyzer in Aktion. Hier wird JetBrains Rider als IDE verwendet. Der Methodenname ist markiert, weil die Methode das IOSP verletzt. Wenn Sie mit der Maus über den Methodennamen fahren, wird eine Beschreibung angezeigt. Die Beschreibung zeigt, warum das IOSP verletzt wird, in dem Integrations- und Operationsbestandteile angezeigt werden. Im Beispiel ruft die Methode HandleUseCase einerseits die Methode Insert auf. Dies ist Integration, weil die Methode uns gehört. Andererseits werden die Methoden ToString und NewGuid aufgerufen, die zum .NET-Framework gehören. Darüber hinaus enthält die Methode den Ausdruck customer.Name == „“, der ebenfalls nur in Operationen erlaubt ist.

IOSP Analyzer Example 1

Ausnahmen

Der Analyzer ignoriert derzeit folgende Methodenaufrufe:

  • Aufrufe aus dem Namespace Microsoft.Extensions.Logging. Logging kann sowohl in Integration als auch Operation stattfinden.
  • Aufrufe von Task.Run. Diese dienen dazu, eine Aufgabe auf einen Thread auszulagern. Das kann sowohl in Integration als auch Operation sinnvoll sein.
  • Aufrufe von ConfigureAwait. Auch diese können sowohl in Integration als auch Operation sinnvoll sein.
  • Aufrufe aus dem Namespace NUnit.Framework. In Tests wird immer eine eigene Methode aufgerufen (Integration) und ein oder mehrere Asserts ausgeführt. Es käme hier andernfalls zu einer IOSP Verletzung.
  • Aufruf von base.xxxx in einer override Methode. Werden Methoden überschrieben, muss häufig die Basismethode aufgerufen werden. Das kann sowohl in Integration als auch Operation notwendig sein.

Hintergrund: das Integration Operation Segregation Principle (IOSP)

Dieses Projekt begann als Artikel für das deutsche Magazin dotnetpro. Es enthält einen Roslyn Analyzer für das Integration Operation Segregation Principle (IOSP).

Das Paket analysiert, ob Methoden das Integration Operation Segregation Principle verletzen. Das Prinzip besagt, dass eine Methode entweder eine Integrationsmethode oder eine Operation ist. Integrationsmethoden rufen andere Methoden auf, die in Ihrer Lösung definiert und implementiert sind. Wenn eine Ihrer Methoden eine andere Ihrer Methoden aufruft, ist es Aufgabe dieser Methode, die aufgerufenen Methoden zu integrieren. Andererseits darf eine Operation keine Ihrer eigenen Methoden aufrufen. Eine Operation ruft APIs (Framework- und Laufzeitmethoden) auf und enthält Ausdrücke wie „x + 2 < 42“. API-Aufrufe sind Aufrufe an fremde Methoden, die nicht in Ihrem Code definiert sind. Zum Beispiel sind Console.WriteLine oder object.ToString API-Aufrufe.

Um Integration und Operation klar zu trennen, können wir folgende einfache Regeln aufstellen:

  • Integration:
    • Ruft nur Ihre eigenen Methoden auf
  • Operation:
    • Ruft nur fremde Methoden aus Runtime und Frameworks auf
    • Kann Ausdrücke enthalten

Die Begründung hinter dem IOSP ist folgende: Wenn wir Integration von Operation strikt trennen, führt dies zu einem besseren Verständnis der Code-Struktur. Dies liegt daran, dass das Abstraktionsniveau klar in High-Level vs. Low-Level-Code getrennt ist. Integrationsmethoden befinden sich auf einer hohen Abstraktionsebene. Sie rufen andere Methoden nur auf, um sie zu integrieren. Daher ist es einfach, eine solche High-Level-Integrationsmethode zu verstehen. Andererseits ist eine Operation leicht zu lesen, weil sie nur Code auf einer niedrigen Ebene enthält. Angesichts dessen, dass eine Operation auch dem Single Responsibility Principle (SRP) entsprechen muss, ist eine Operation leicht zu lesen und zu verstehen, weil sie nur Low-Level-Code enthält, der ein spezifisches Problem löst. Um eine Operation zu verstehen, ist es nicht notwendig, andere Methoden aus der Lösung zu verstehen. Es ist nur notwendig, Aufrufe an Laufzeit- oder Framework-Funktionalitäten zu verstehen.

Der zweite Vorteil der Konformität mit dem IOSP ist die Testbarkeit. Wenn wir Integration von Operation strikt trennen, können wir die Operationen isoliert testen. Da Operationen keine anderen Methoden aus unserer Lösung aufrufen, ist jede Operation eine separate funktionale Einheit. Daher benötigen wir keine Mocks, um eine Operation von ihren Abhängigkeiten zu trennen. Sie hat keine Abhängigkeiten, sonst würde sie operationellen Code mit Integration vermischen und somit gegen das IOSP verstoßen. Die Integrationsmethoden enthalten keine Logik. Daher ist es nicht notwendig, die Integrationsmethoden von ihren Abhängigkeiten zu trennen. Die einzige Aufgabe der Integrationsmethoden ist es, Aufrufe an andere Methoden zu integrieren. Also testen wir die Integrationsmethoden mit Integrationstests.

Integrationsmethoden können Kontrollstrukturen wie foreach, for, while, if oder switch/case-Anweisungen enthalten. Sie dürfen lediglich keine Ausdrücke enthalten. Es ist in Ordnung, eine Funktion aufzurufen, die den Ausdruck enthält:

if(x + 2 == 42) { ... }  // violates IOSP if contained in an integration

vs.

if(ThisIsTheAnswer(x)) { ... }  // ok because it calls other method
de_DEGerman