Ich habe in letzter Zeit Clean Code und verschiedene Online-Artikel über SOLID gelesen. Je mehr ich darüber lese, desto mehr habe ich das Gefühl, nichts zu wissen.
Angenommen, ich erstelle eine Webanwendung mit ASP.NET MVC 3. Angenommen, ich habe eine UsersController
mit einer Create
Aktion wie dieser:
public class UsersController : Controller
{
public ActionResult Create(CreateUserViewModel viewModel)
{
}
}
Bei dieser Aktionsmethode möchte ich einen Benutzer in der Datenbank speichern, wenn die eingegebenen Daten gültig sind.
Nach dem Prinzip der Einzelverantwortung sollte ein Objekt nun eine Einzelverantwortung haben, und diese Verantwortung sollte vollständig von der Klasse eingekapselt werden. Alle seine Dienstleistungen sollten eng an dieser Verantwortung ausgerichtet sein. Da Validierung und Speichern in der Datenbank zwei separate Verantwortlichkeiten sind, sollte ich eine separate Klasse erstellen, um sie wie folgt zu behandeln:
public class UsersController : Controller
{
private ICreateUserValidator validator;
private IUserService service;
public UsersController(ICreateUserValidator validator, IUserService service)
{
this.validator = validator;
this.service= service;
}
public ActionResult Create(CreateUserViewModel viewModel)
{
ValidationResult result = validator.IsValid(viewModel);
if (result.IsValid)
{
service.CreateUser(viewModel);
return RedirectToAction("Index");
}
else
{
foreach (var errorMessage in result.ErrorMessages)
{
ModelState.AddModelError(String.Empty, errorMessage);
}
return View(viewModel);
}
}
}
Das macht etwas Sinn für mich, aber ich bin nicht sicher , dass dies der richtige Weg , um Griff Dinge wie diese. Es ist beispielsweise durchaus möglich, eine ungültige Instanz von CreateUserViewModel
an die IUserService
Klasse zu übergeben. Ich weiß, dass ich die integrierten DataAnnotations verwenden könnte, aber was ist, wenn sie nicht ausreichen? Bild, das ich in ICreateUserValidator
der Datenbank überprüfe, um festzustellen, ob es bereits einen anderen Benutzer mit demselben Namen gibt ...
Eine andere Möglichkeit besteht darin, IUserService
die Validierung wie folgt durchführen zu lassen :
public class UserService : IUserService
{
private ICreateUserValidator validator;
public UserService(ICreateUserValidator validator)
{
this.validator = validator;
}
public ValidationResult CreateUser(CreateUserViewModel viewModel)
{
var result = validator.IsValid(viewModel);
if (result.IsValid)
{
// Save the user
}
return result;
}
}
Aber ich habe das Gefühl, dass ich hier gegen das Prinzip der Einzelverantwortung verstoße.
Wie soll ich mit so etwas umgehen?
quelle
user
Klasse die Validierung nicht durchführen? SRP oder nicht, ich verstehe nicht, warum dieuser
Instanz nicht wissen sollte, wann sie gültig ist oder nicht, und verlasse mich auf etwas anderes, um dies zu bestimmen. Welche anderen Aufgaben hat die Klasse? Wenn sichuser
Änderungen ändern, ändert sich wahrscheinlich die Validierung. Wenn Sie diese also an eine andere Klasse auslagern, wird nur eine eng gekoppelte Klasse erstellt.Antworten:
Ich glaube wirklich nicht, dass Sie in Ihrem zweiten Beispiel gegen das Prinzip der Einzelverantwortung verstoßen.
Die
UserService
Klasse hat nur einen Grund, sich zu ändern: Wenn die Art und Weise, wie Sie einen Benutzer speichern, geändert werden muss.Die
ICreateUserValidator
Klasse hat nur einen Grund zur Änderung: Wenn die Art und Weise, wie Sie einen Benutzer validieren, geändert werden muss.Ich muss zugeben, dass Ihre erste Implementierung intuitiver ist. Die Validierung sollte jedoch von der Entität durchgeführt werden, die den Benutzer erstellt. Der Ersteller selbst sollte nicht für die Validierung verantwortlich sein. Es sollte die Verantwortung eher an eine Validator-Klasse delegieren (wie in Ihrer zweiten Implementierung). Ich glaube also nicht, dass dem zweiten Design SRP fehlt.
quelle
Es scheint mir, dass das erste Beispiel der wahren SRP "näher" ist; In Ihrem Fall liegt es in der Verantwortung des Controllers, zu wissen, wie die Dinge verkabelt und wie das ViewModel weitergegeben wird.
Wäre es nicht sinnvoller, wenn die gesamten IsValid / ValidationMessages Teil des ViewModel selbst wären? Ich habe mich nicht mit MVVM beschäftigt, aber es scheint wie das alte Model-View-Presenter-Muster, bei dem es für den Presenter in Ordnung war, solche Dinge zu handhaben, da es in direktem Zusammenhang mit der Präsentation stand. Wenn Ihr ViewModel sich selbst auf Gültigkeit prüfen kann, besteht keine Möglichkeit, ein ungültiges an die Create-Methode des UserService zu übergeben.
quelle
IsValid
Booleschen Wert und dasValidationMessages
Array verfügbar macht , kann es dennoch eine Validator-Klasse aufrufen und muss sich keine Gedanken darüber machen, wie die Validierung implementiert wird. Der Controller könnte dies zuerst überprüfen, z. B. sollteif (userViewModel.IsValid) { userService.Create(userViewModel); }
das ViewModel grundsätzlich wissen, ob es gültig ist, aber nicht, woher es weiß, dass es gültig ist.