إزالة الإسناد إلى المعاملات (Remove Assignments to Parameters)
المشكلة
إسناد قيمةٍ ما إلى أحد المعاملات (parameter) داخل التابع (method body).
الحل
استخدام متغيِّرٍ محليٍّ (local variable) بدلًا من المعامل.
مثال
قبل إعادة التصميم
نلاحظ وجود عمليّة إسنادٍ (من بعد الإنقاص بمقدار 2) إلى معامل التابع الوارد باسم inputVal
:
في لغة Java:
int discount(int inputVal, int quantity) {
if (inputVal > 50) {
inputVal -= 2;
}
//...
}
في لغة #C:
int Discount(int inputVal, int quantity)
{
if (inputVal > 50)
{
inputVal -= 2;
}
// ...
}
في لغة PHP:
function discount($inputVal, $quantity) {
if ($inputVal > 50) {
$inputVal -= 2;
}
...
في لغة Python:
def discount(inputVal, quantity):
if inputVal > 50:
inputVal -= 2
# ...
في لغة TypeScript:
discount(inputVal: number, quantity: number): number {
if (inputVal > 50) {
inputVal -= 2;
}
// ...
}
بعد إعادة التصميم
يُعرَّف متغيِّرٌ محليٌّ جديدٌ باسم result
، وتُسنَد إليه قيمة المعامل المطلوب (وهو inputVal
) واستخدام المتغيِّر الجديد عوضًا عنه:
في لغة Java:
int discount(int inputVal, int quantity) {
int result = inputVal;
if (inputVal > 50) {
result -= 2;
}
//...
}
في لغة #C:
int Discount(int inputVal, int quantity)
{
int result = inputVal;
if (inputVal > 50)
{
result -= 2;
}
// ...
}
في لغة PHP:
function discount($inputVal, $quantity) {
$result = $inputVal;
if ($inputVal > 50) {
$result -= 2;
}
...
في لغة Python:
def discount(inputVal, quantity):
result = inputVal
if inputVal > 50:
result -= 2
# ...
في لغة TypeScript:
discount(inputVal: number, quantity: number): number {
let result = inputVal;
if (inputVal > 50) {
result -= 2;
}
// ...
}
لم إعادة التصميم؟
إنّ الهدف من إعادة التصميم بمثل هذه الحالة هو السبب ذاته وراء تجزئة المتغيِّر المؤقَّت (split temporary variable) والفرق بينهما هو التعامل مع المعاملات (parameters) هنا ومع المتغيِّرات المحليّة (variables) هناك، ويتلخَّص الهدف من إعادة التصميم بنقطتين:
- أولًا؛ إن مُرِّر المعامل عبر مرجعيّته (reference) فستُمرَّر قيمةُ المعامل (بعد تغييرها داخل التابع [method]) إلى المتغيِّر الوسيط (argument) الذي استدعى التابع، وغالبًا ما يحدث هذا دون قصدٍ ويسبِّب بعض الآثار الجانبيّة غير المرغوبة، أمّا إن كان تمرير المعامل -بلغة البرمجة المُستخدَمة- تمريرًا بالقيمة (pass by value) لا بالمرجعيّة فإنّ هذا سيُتعِب المبرمجين غير المعتادين على ذلك.
- ثانيًا؛ سيصبحُ من الصعب (بوجود الإسناداتُ المتعدِّدة وبقيمٍ مختلفةٍ إلى نفس المعامل) تحديدُ البيانات المُخزَّنة في المعامل عند نقطةٍ زمنيّةٍ مُحدَّدة، وتزداد هذه المشكلة سوءًا إن وُثِّق المعامل ومحتوياته مع اختلاف القيم الفعليّة عمّا هو متوقعٌ وجودُه في التابع.
فوائد تطبيق الحل
- سيصبح كلُّ جزءٍ من شيفرة البرنامج مسؤولًا عن مهمةٍ واحدةٍ ممّا يجعل من السهل صيانة (maintain) البرنامج، إذ من الممكن إجراء أيّ تبديلٍ دون القلق حيال الآثار الجانبية التي قد تنجم عن هذا التبديل.
- سيساعد على استخراج الشيفرات المتكرِّرة إلى توابع مستقلِّة.
آلية الحل
- إنشاء متغيِّرٍ محليٍّ (local vaiable) وإسناد قيمة المعامل الأوليّة (initial value) إليه.
- تبديل المعامل إلى المتغيِّر الجديد بأيِّ شيفرةٍ ترِد بعد تعليمة التصريح عنه (بالخطوة السابقة).