Saya telah membaca Kode Bersih dan berbagai artikel online tentang SOLID belakangan ini, dan semakin saya membaca tentangnya, semakin saya merasa tidak tahu apa-apa.
Katakanlah saya sedang membangun sebuah aplikasi web menggunakan katakanlah ASP.NET MVC 3. Mari saya memiliki UsersController
dengan Create
tindakan seperti ini:
public class UsersController : Controller
{
public ActionResult Create(CreateUserViewModel viewModel)
{
}
}
Dalam metode tindakan itu saya ingin menyimpan pengguna ke database jika data yang dimasukkan valid.
Sekarang, menurut Prinsip Tanggung Jawab Tunggal, sebuah objek harus memiliki tanggung jawab tunggal, dan tanggung jawab itu harus sepenuhnya dienkapsulasi oleh kelas. Semua layanannya harus selaras dengan tanggung jawab itu. Karena validasi dan penyimpanan ke database adalah dua tanggung jawab terpisah, saya kira saya harus membuat kelas terpisah untuk menangani mereka seperti ini:
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);
}
}
}
Yang membuat beberapa akal bagi saya, tapi saya sama sekali tidak yakin bahwa ini adalah cara yang tepat untuk menangani hal-hal seperti ini. Hal ini misalnya sepenuhnya mungkin untuk melewati sebuah instance valid dari CreateUserViewModel
ke IUserService
kelas. Saya tahu saya bisa menggunakan DataAnnotations bawaan, tetapi bagaimana ketika itu tidak cukup? Gambar yang saya ICreateUserValidator
periksa database untuk melihat apakah sudah ada pengguna lain dengan nama yang sama ...
Pilihan lain adalah membiarkan IUserService
validasi seperti ini:
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;
}
}
Tetapi saya merasa saya melanggar Prinsip Tanggung Jawab Tunggal di sini.
Bagaimana saya harus berurusan dengan hal seperti ini?
sumber
user
kelas menangani validasi? SRP atau tidak, saya tidak melihat mengapauser
instance tidak harus tahu kapan valid atau tidak dan bergantung pada sesuatu yang lain untuk menentukan itu. Apa tanggung jawab lain yang dimiliki kelas? Ditambah ketikauser
perubahan validasi mungkin akan berubah, jadi outsourcing yang ke kelas yang berbeda hanya akan membuat kelas yang digabungkan secara ketat.Jawaban:
Saya benar-benar tidak berpikir Anda melanggar Prinsip Tanggung Jawab Tunggal dalam contoh kedua Anda.
The
UserService
kelas hanya memiliki satu alasan untuk perubahan: jika ada kebutuhan untuk mengubah cara Anda menyimpan pengguna.The
ICreateUserValidator
kelas hanya memiliki satu alasan untuk mengubah: jika ada kebutuhan untuk mengubah cara Anda memvalidasi pengguna.Saya harus mengakui bahwa implementasi pertama Anda lebih intuitif. Namun, validasi harus dilakukan oleh entitas yang menciptakan pengguna. Pembuatnya sendiri seharusnya tidak bertanggung jawab atas validasi; lebih baik mendelegasikan tanggung jawab ke kelas validator (seperti dalam implementasi kedua Anda). Jadi, saya tidak berpikir desain kedua tidak memiliki SRP.
sumber
Tampak bagi saya bahwa contoh pertama adalah "lebih dekat" ke SRP sejati; itu adalah tanggung jawab Pengendali dalam kasus Anda untuk mengetahui cara menghubungkan semuanya dan bagaimana cara meneruskan ViewModel.
Bukankah lebih masuk akal jika seluruh IsValid / ValidationMessages menjadi bagian dari ViewModel itu sendiri? Saya belum mencoba-coba MVVM, tetapi sepertinya pola Model-View-Presenter yang lama, di mana tidak masalah bagi Presenter untuk menangani hal-hal seperti itu karena itu berhubungan langsung dengan presentasi. Jika ViewModel Anda dapat memeriksa sendiri validitasnya, tidak ada peluang untuk memberikan yang tidak valid ke metode Buat UserService.
sumber
IsValid
boolean danValidationMessages
array, itu masih bisa memanggil ke kelas Validator dan tidak perlu khawatir tentang bagaimana validasi sedang dilaksanakan. Pengontrol dapat memeriksa terlebih dahulu misalnyaif (userViewModel.IsValid) { userService.Create(userViewModel); }
Pada dasarnya ViewModel harus tahu apakah itu valid, tetapi tidak bagaimana ia tahu itu valid.