Earn More by Sharing What You Love
Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!

Tim Corey
35m 50s
Design Patterns sind wiederverwendbare Lösungen für gängige Softwareentwicklungsprobleme und bieten Vorlagen, um objektorientierten Code effizienter und wartbarer zu strukturieren und zu implementieren. Sie helfen Entwicklern, Probleme bei der Erstellung, Strukturierung und Kommunikation von Objekten auf flexible und skalierbare Weise zu lösen. Design Patterns dienen als Best-Practice-Konzepte, die Entwickler beim Schreiben von besserem Code anleiten. Eines der grundlegenden Prinzipien bei der Softwareentwicklung ist das Single Responsibility Principle (SRP), das Teil der SOLID-Prinzipien ist.
In seinem Video "Design Patterns: Single Responsibility Principle Explained Practically in C# (The S in SOLID)" geht Tim Corey auf das Single Responsibility Principle (SRP) ein, hebt seine Bedeutung für das Softwaredesign hervor und gibt praktische Einblicke, wie es effektiv umgesetzt werden kann. Dieser Artikel bietet einen kurzen Überblick über die wichtigsten Erkenntnisse aus dem Video und unterstreicht die Bedeutung von SRP für die Erstellung von sauberem, wartbarem Code.
Bei der Softwareentwicklung sind die SOLID-Prinzipien entscheidend für die Erstellung von wartbarem und skalierbarem Code. Sie sorgen dafür, dass der Code leicht zu verstehen, zu testen und zu ändern ist. Die fünf Prinzipien - Single Responsibility Principle (SRP), Open/Closed Principle (OCP), Liskov Substitution Principle (LSP), Interface Segregation Principle (ISP) und Dependency Inversion Principle (DIP) - sind integraler Bestandteil des objektorientierten Designs und können innerhalb von Design Patterns angewendet werden, um Lösungen robuster zu machen.
Durch die Anwendung von Entwurfsmustern in C# können Entwickler gängige Probleme effektiver lösen. Ob es um die Erstellung von Objekten, die Definition von Baumstrukturen oder die Sicherstellung der Wiederverwendbarkeit mit einzelnen Instanzen geht, Design Patterns bieten vordefinierte Lösungen, die die Softwarearchitektur verbessern. Muster wie die Factory-Methode, Builder und Singleton bieten flexible, wiederverwendbare Lösungen, während Verhaltens- und Strukturmuster helfen, die Komplexität zu verwalten und die Kommunikation innerhalb von Systemen zu verbessern. Durch das Erlernen und die Nutzung dieser Muster können Entwickler Systeme aufbauen, die einfacher zu warten und zu erweitern sind.
Tim erörtert das Konzept von SRP und betont, dass es für Entwickler entscheidend ist, sicherzustellen, dass ihr Code den Best Practices entspricht. SRP besagt, dass eine Klasse nur eine Verantwortung oder einen Grund zum Ändern haben sollte. Dieser Grundsatz trägt dazu bei, einen sauberen, wartbaren und skalierbaren Code zu erhalten.
Tim richtet eine einfache Konsolenanwendung in C# ein, die nach dem Vor- und Nachnamen des Benutzers fragt, diese Namen validiert und dann einen Benutzernamen generiert. Die ursprüngliche Implementierung verstößt gegen SRP, was eine hervorragende Gelegenheit bietet, zu demonstrieren, wie man Code refaktorisieren kann, um diesen Grundsatz einzuhalten.
Tim erklärt SRP, indem er die verschiedenen Verantwortlichkeiten innerhalb der ursprünglichen Klasse hervorhebt:
Jede dieser Aufgaben stellt einen anderen Grund für die Änderung der Klasse dar und verstößt gegen SRP.
Tim zeigt, wie man den Code refaktorisiert, um SRP zu folgen, indem jede Verantwortung in eine eigene Klasse extrahiert wird. Dieser Ansatz stellt sicher, dass jede Klasse nur einen einzigen Grund für eine Änderung hat, wodurch der Code modularer und leichter zu pflegen ist.
Tim bietet ein praktisches Beispiel für Refactoring:
using System;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Welcome to my application");
Console.Write("Enter your first name: ");
string firstName = Console.ReadLine();
Console.Write("Enter your last name: ");
string lastName = Console.ReadLine();
if (string.IsNullOrWhiteSpace(firstName) || string.IsNullOrWhiteSpace(lastName))
{
Console.WriteLine("You did not give us valid information!");
Console.ReadLine();
return;
}
var userName = $"{firstName.Substring(0, 1)}{lastName}".ToLower();
Console.WriteLine($"Your username is {userName}");
Console.WriteLine("Press enter to close...");
Console.ReadLine();
}
}Imports System
Module Program
Sub Main(args As String())
Console.WriteLine("Welcome to my application")
Console.Write("Enter your first name: ")
Dim firstName As String = Console.ReadLine()
Console.Write("Enter your last name: ")
Dim lastName As String = Console.ReadLine()
If String.IsNullOrWhiteSpace(firstName) OrElse String.IsNullOrWhiteSpace(lastName) Then
Console.WriteLine("You did not give us valid information!")
Console.ReadLine()
Return
End If
Dim userName = $"{firstName.Substring(0, 1)}{lastName}".ToLower()
Console.WriteLine($"Your username is {userName}")
Console.WriteLine("Press enter to close...")
Console.ReadLine()
End Sub
End ModuleZunächst erstellt Tim eine Klasse, die Standardmeldungen an den Benutzer verarbeitet. Diese Klasse verwaltet Begrüßungs- und Endnachrichten.
public class StandardMessages
{
public static void WelcomeMessage()
{
Console.WriteLine("Welcome to my application");
}
public static void EndApplication()
{
Console.WriteLine("Press enter to close...");
Console.ReadLine();
}
public static void ShowValidationErrorMessage()
{
Console.WriteLine("You did not give us valid information!");
}
}Public Class StandardMessages
Public Shared Sub WelcomeMessage()
Console.WriteLine("Welcome to my application")
End Sub
Public Shared Sub EndApplication()
Console.WriteLine("Press enter to close...")
Console.ReadLine()
End Sub
Public Shared Sub ShowValidationErrorMessage()
Console.WriteLine("You did not give us valid information!")
End Sub
End ClassErsetzen Sie in der Klasse Program die direkten Aufrufe von Console.WriteLine und Console.ReadLine durch Aufrufe der Methoden der Klasse StandardMessages:
class Program
{
static void Main(string[] args)
{
StandardMessages.WelcomeMessage();
// Other code...
StandardMessages.EndApplication();
}
}Module Program
Sub Main(args As String())
StandardMessages.WelcomeMessage()
' Other code...
StandardMessages.EndApplication()
End Sub
End ModuleAls Nächstes erstellt Tim eine Klasse für die Erfassung des Vor- und Nachnamens der Person. Diese Klasse ist für die Erfassung von Benutzereingaben und die Rückgabe eines Person-Objekts verantwortlich.
public class PersonDataCapture
{
public static Person Capture()
{
Person output = new Person();
Console.Write("Enter your first name: ");
output.FirstName = Console.ReadLine();
Console.Write("Enter your last name: ");
output.LastName = Console.ReadLine();
return output;
}
}Public Class PersonDataCapture
Public Shared Function Capture() As Person
Dim output As New Person()
Console.Write("Enter your first name: ")
output.FirstName = Console.ReadLine()
Console.Write("Enter your last name: ")
output.LastName = Console.ReadLine()
Return output
End Function
End ClassSie benötigen außerdem eine Person-Klasse, die den Vor- und Nachnamen des Benutzers enthält.
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}Public Class Person
Public Property FirstName As String
Public Property LastName As String
End ClassErsetzen Sie in der Klasse Program die direkte Bearbeitung der Benutzereingabe durch einen Aufruf von PersonDataCapture.Capture:
class Program
{
static void Main(string[] args)
{
StandardMessages.WelcomeMessage();
Person user = PersonDataCapture.Capture();
// Other code...
StandardMessages.EndApplication();
}
}Module Program
Sub Main(args As String())
StandardMessages.WelcomeMessage()
Dim user As Person = PersonDataCapture.Capture()
' Other code...
StandardMessages.EndApplication()
End Sub
End ModuleAls Nächstes erstellt Tim eine Klasse, die die Validierung des Vor- und Nachnamens der Person übernimmt. Diese Klasse ist dafür verantwortlich, dass die Namen keine Nullen oder Leerzeichen enthalten.
public class PersonValidator
{
public static bool Validate(Person person)
{
if (string.IsNullOrWhiteSpace(person.FirstName))
{
StandardMessages.ShowValidationErrorMessage("first name");
return false;
}
if (string.IsNullOrWhiteSpace(person.LastName))
{
StandardMessages.ShowValidationErrorMessage("last name");
return false;
}
return true;
}
}Public Class PersonValidator
Public Shared Function Validate(person As Person) As Boolean
If String.IsNullOrWhiteSpace(person.FirstName) Then
StandardMessages.ShowValidationErrorMessage("first name")
Return False
End If
If String.IsNullOrWhiteSpace(person.LastName) Then
StandardMessages.ShowValidationErrorMessage("last name")
Return False
End If
Return True
End Function
End ClassErsetzen Sie in der Klasse Program den Validierungscode durch einen Aufruf von PersonValidator.Validate:
class Program
{
static void Main(string[] args)
{
StandardMessages.WelcomeMessage();
Person user = PersonDataCapture.Capture();
if (!PersonValidator.Validate(user))
{
StandardMessages.EndApplication();
return;
}
// Other code...
StandardMessages.EndApplication();
}
}Module Program
Sub Main(args As String())
StandardMessages.WelcomeMessage()
Dim user As Person = PersonDataCapture.Capture()
If Not PersonValidator.Validate(user) Then
StandardMessages.EndApplication()
Return
End If
' Other code...
StandardMessages.EndApplication()
End Sub
End ModuleTim verschiebt die Logik für die Generierung von Benutzernamen und die Erstellung von Konten in eine neue AccountGenerator-Klasse.
Erstellen der AccountGenerator-Klasse:
public class AccountGenerator
{
public static void CreateAccount(Person user)
{
string username = $"{user.FirstName.Substring(0, 1)}{user.LastName}".ToLower();
Console.WriteLine($"Your username is: {username}");
}
}Public Class AccountGenerator
Public Shared Sub CreateAccount(user As Person)
Dim username As String = $"{user.FirstName.Substring(0, 1)}{user.LastName}".ToLower()
Console.WriteLine($"Your username is: {username}")
End Sub
End ClassAktualisierung der Hauptklasse:
class Program
{
static void Main(string[] args)
{
StandardMessages.WelcomeMessage();
Person user = PersonDataCapture.Capture();
bool isUserValid = PersonValidator.Validate(user);
if (!isUserValid)
{
StandardMessages.EndApplication();
return;
}
AccountGenerator.CreateAccount(user);
StandardMessages.EndApplication();
}
}Module Program
Sub Main(args As String())
StandardMessages.WelcomeMessage()
Dim user As Person = PersonDataCapture.Capture()
Dim isUserValid As Boolean = PersonValidator.Validate(user)
If Not isUserValid Then
StandardMessages.EndApplication()
Return
End If
AccountGenerator.CreateAccount(user)
StandardMessages.EndApplication()
End Sub
End Module
In diesem abschließenden Abschnitt fasst Tim Corey die Vorteile und die Umsetzung des Single Responsibility Principle (SRP) durch den Refactoring-Prozess des Demo-Codes zusammen. Er unterstreicht die Vorteile einer Aufteilung der Anwendung in kleinere, fokussierte Klassen.
Vereinfachte Code-Wartung:
Jede Klasse hat eine einzige Zuständigkeit, so dass es einfacher ist, die Stellen zu finden, an denen Änderungen vorgenommen werden müssen. Die Logik der Benutzerdatenerfassung wird beispielsweise eindeutig unter PersonDataCapture eingeordnet.
Diese Struktur vereinfacht das Verständnis, da jeder, der die Benutzervalidierung ändern möchte, weiß, dass er PersonValidator überprüfen muss.
Verbesserte Lesbarkeit:
StandardMessages.WelcomeMessage();
Person user = PersonDataCapture.Capture();
bool isUserValid = PersonValidator.Validate(user);
if (!isUserValid)
{
StandardMessages.EndApplication();
return;
}
AccountGenerator.CreateAccount(user);
StandardMessages.EndApplication();StandardMessages.WelcomeMessage()
Dim user As Person = PersonDataCapture.Capture()
Dim isUserValid As Boolean = PersonValidator.Validate(user)
If Not isUserValid Then
StandardMessages.EndApplication()
Return
End If
AccountGenerator.CreateAccount(user)
StandardMessages.EndApplication()Reduzierte Komplexität:
Kleine Klassen mit konzentrierten Aufgaben haben in der Regel weniger Codezeilen, was sie leichter verständlich und wartbar macht.
Beispiel: Die Methoden der Klasse StandardMessages sind prägnant und dienen einem einzigen Zweck, z. B. der Anzeige einer Willkommensnachricht oder dem Beenden der Anwendung.
public class StandardMessages
{
public static void WelcomeMessage()
{
Console.WriteLine("Welcome to my application");
}
public static void EndApplication()
{
Console.WriteLine("Press enter to close...");
Console.ReadLine();
}
public static void ShowValidationErrorMessage(string fieldName)
{
Console.WriteLine($"You did not give us a valid {fieldName}!");
}
}Public Class StandardMessages
Public Shared Sub WelcomeMessage()
Console.WriteLine("Welcome to my application")
End Sub
Public Shared Sub EndApplication()
Console.WriteLine("Press enter to close...")
Console.ReadLine()
End Sub
Public Shared Sub ShowValidationErrorMessage(fieldName As String)
Console.WriteLine($"You did not give us a valid {fieldName}!")
End Sub
End Class
Einfache Code-Änderungen:
Da es für jede Klasse nur einen einzigen Änderungsgrund gibt, ist die Anpassung des Codes an neue Anforderungen sehr einfach.
Beispiel: Wenn die Anforderung darin besteht, die Endnachricht zu ändern, erfolgt die Änderung ausschließlich in der Methode StandardMessages.EndApplication.
Bessere Fehlersuche und Kollaboration:
Mit kleineren, klar definierten Klassen wird die Fehlersuche einfacher, da Sie den Ort eines Problems leicht ausfindig machen können.
Neue Entwickler können sich schneller einarbeiten, da sie die klare Struktur und die Zuständigkeiten der einzelnen Klassen verstehen.
Tim geht auf die häufige Sorge ein, dass durch die Anwendung von SRP zu viele Klassen entstehen, die das Projekt unübersichtlich machen:
Navigation und Verstehen:
Tools wie IntelliSense in Visual Studio machen die Navigation in mehreren Klassen einfach. Wenn Sie zum Beispiel F12 drücken, gelangen Sie direkt zur Definition von Methoden oder Klassen.
Viele kleine, überschaubare Teile können das Verständnis der gesamten Anwendung im Vergleich zu großen monolithischen Klassen erleichtern.
Leistung und Speicherung:
Ausgewogenheit und Überschuss :
Tim rät, ein Gleichgewicht zu finden. Wenn die Zuständigkeit einer Klasse zu groß wird, sollten Sie prüfen, ob es mehrere Gründe für eine Änderung gibt, was darauf hindeutet, dass eine weitere Unterteilung erforderlich sein könnte.
Er weist darauf hin, dass, wenn Sie in Visual Studio ausgiebig durch eine Klasse blättern müssen, diese möglicherweise zu groß ist und geteilt werden muss.
Tim ermutigt Entwickler, SRP schrittweise anzuwenden, insbesondere in bestehenden Codebasen. Beginnen Sie mit kleinen Änderungen und neuem Code, um den SRP-Prinzipien zu entsprechen. Dieser schrittweise Ansatz gewährleistet reibungslosere Übergänge und kontinuierliche Verbesserungen.
Das Refactoring-Beispiel von Tim Corey zeigt, wie die Einhaltung des Single-Responsibility-Prinzips (SRP) zu saubererem, besser wartbarem Code führt. Durch die Aufteilung von Verantwortlichkeiten in kleinere, fokussierte Klassen können Entwickler die Lesbarkeit, das Debugging und die Zusammenarbeit innerhalb ihrer Codebases verbessern. Dieses Grundprinzip der SOLID-Entwurfsmuster ebnet den Weg für fortgeschrittenere Prinzipien und Best Practices in der Softwareentwicklung.
Ausführlichere Informationen und Code-Beispiele finden Sie in seinem Video und in seinem Channel für weitere Design Patterns-Videos.
Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!
Join our newsletter, you’ll get exclusive access on article updates. We value your privacy