خذ بعين الاعتبار وحدة بسيطة مسؤولة عن إضافة مستخدمين جدد.وبمثاله ، سنرى الاحتمالات التي يفتحها تطبيق مبدأ DRY.بالنسبة لي ، تم تجسيد مبدأ DRY (لا تكرر نفسك) دائمًا في تعريفين أساسيين:- ازدواجية المعرفة هي دائما انتهاك للمبدأ
- لا يعد تكرار التعليمات البرمجية انتهاكًا دائمًا للمبدأ
لنبدأ مع وحدات التحكم التي تحتوي على الحد الأدنى من المنطق.class UserController
{
public function create(CreateRequest $request)
{
$user = User::create($request->all());
return view('user.created', compact('user'));
}
}
class UserApiController
{
public function create(CreateRequest $request)
{
$user = User::create($request->all());
return response()->noContent(201);
}
}
في المرحلة الأولية ، يبدو مثل هذا التكرار للقانون غير ضار إلى حد ما.ولكن لدينا بالفعل ازدواجية في المعرفة ، ويحظر ازدواجية المعرفة.للقيام بذلك ، نقوم بتعميم إنشاء المستخدم في فئة UserServiceclass UserService
{
public function create(array $data): User
{
$user = new User;
$user->email = $data['email'];
$user->password = $data['password'];
$user->save();
return $user;
}
public function delete($userId): bool
{
$user = User::findOrFail($userId);
return $user->delete();
}
}
بعد نقل كل منطق العمل مع النموذج إلى الخدمة ، نتخلص من ازدواجيته في وحدة التحكم. لكن لدينا مشكلة أخرى. لنفترض أن علينا تعقيد عملية إنشاء مستخدم قليلاً.class UserService
{
protected $blogService;
public function __construct(BlogService $blogService)
{
$this->blogService = $blogService;
}
public function create(array $data): User
{
$user = new User;
$user->email = $data['email'];
$user->password = $data['password'];
$user->save();
$blog = $this->blogService->create();
$user->blogs()->attach($blog);
return $user;
}
}
بالتدريج ، ستبدأ فئة UserService في النمو ونواجه خطر الحصول على فئة فائقة مع عدد كبير من التبعيات.فئة CreateUser أحادية الإجراء
من أجل تجنب مثل هذه العواقب ، يمكنك تقسيم الخدمة إلى فئات لإجراء واحد.المتطلبات الأساسية لهذه الفئة:- الاسم الذي يمثل الإجراء الذي يتعين القيام به
- لديه طريقة عامة واحدة ( سأستخدم طريقة __invoke السحرية )
- لديه كل التبعيات اللازمة
- يضمن الامتثال لجميع قواعد العمل داخل نفسه ، ويولد استثناء عند انتهاكها
class CreateUser
{
protected $blogService;
public function __construct(BlogService $blogService)
{
$this->blogService = $blogService;
}
public function __invoke(array $data): User
{
$email = $data['email'];
if (User::whereEmail($email)->first()) {
throw new EmailNotUniqueException("$email should be unique!");
}
$user = new User;
$user->email = $data['email'];
$user->password = $data['password'];
$user->save();
$blog = $this->blogService->create();
$user->blogs()->attach($blog);
return $user;
}
}
لدينا بالفعل فحص حقل بريد إلكتروني في فئة CreateRequet ، ولكن من المنطقي إضافة شيك هنا أيضًا. يعكس هذا بشكل أكثر دقة منطق الأعمال لإنشاء مستخدم ، كما يبسط التصحيح.تحكم تحكم الشكل التاليclass UserController
{
public function create(CreateRequest $request, CreateUser $createUser)
{
$user = $createUser($request->all());
return view('user.created', compact('user'));
}
}
class UserApiController
{
public function create(CreateRequest $request, CreateUser $createUser)
{
$user = $createUser($request->all());
return response()->noContent(201);
}
}
نتيجة لذلك ، لدينا منطق معزول تمامًا لإنشاء مستخدم. من السهل التعديل والتوسيع.الآن دعونا نرى المزايا التي يمنحنا إياها هذا النهج.على سبيل المثال ، هناك مهمة لاستيراد المستخدمين.class ImportUser
{
protected $createUser;
public function __construct(CreateUser $createUser)
{
$this->createUser = $createUser;
}
public function handle(array $rows): Collection
{
return collect($rows)->map(function (array $row) {
try {
return $this->createUser($row);
} catch (EmailNotUniqueException $e) {
}
});
}
}
نحصل على فرصة إعادة استخدام الكود عن طريق تضمينه في طريقة Collection :: map (). ونعالج أيضًا احتياجات المستخدمين الذين لا تكون عناوين بريدهم الإلكتروني فريدة.خلع الملابس
افترض أننا بحاجة إلى تسجيل كل مستخدم جديد في ملف.للقيام بذلك ، لن نقوم بتضمين هذا الإجراء في فئة CreateUser نفسها ، ولكن استخدم نمط الديكور.class LogCreateUser extends CreateUser
{
public function __invoke(array $data)
{
Log::info("A new user has registered: " . $data['email']);
parent::__invoke($data);
}
}
بعد ذلك ، باستخدام حاوية Laravel IoC ، يمكننا ربط فئة LogCreateUser بفئة CreateUser ، وسيتم تنفيذ الأول في كل مرة نحتاج فيها إلى مثيل من الثانية.class AppServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind(CreateUser::class, LogCreateUser::class);
}
لدينا أيضًا فرصة لجعل إعداد إنشاء المستخدم باستخدام متغير في ملف التكوين.class AppServiceProvider extends ServiceProvider
{
public function register()
{
if (config("users.log_registration")) {
$this->app->bind(CreateUser::class, LogCreateUser::class);
}
}
استنتاج
اليك مثال بسيط. تبدأ الفوائد الحقيقية في الظهور مع بدء التعقيد في النمو. نعلم دائمًا أن الشفرة في مكان واحد وحدودها محددة بوضوح.نحصل على المزايا التالية: يمنع التكرار ويبسط الاختبارويفتح الطريق لتطبيق مبادئ وأنماط التصميم الأخرى.