الفرق بين المراجعتين ل"Refactoring/replace temp with query"
جميل-بيلوني (نقاش | مساهمات) ط |
جميل-بيلوني (نقاش | مساهمات) (مراجعة وتدقيق.) |
||
سطر 9: | سطر 9: | ||
=== قبل إعادة التصميم === | === قبل إعادة التصميم === | ||
− | نلاحظ في الشيفرة الآتية وجود متغيِّرٍ مؤقتٍ باسم <code>basePrice</code> لتخزين القيمة الناتجة عن تنفيذ التعبير الرياضيّ بمعامل الجداء (أي المعامل <code>*</code>)، وسيُستخدَم هذا المتغيِّر لاحقًا في الأجزاء الشرطيّة من الشيفرة:<syntaxhighlight lang="java"> | + | نلاحظ في الشيفرة الآتية وجود متغيِّرٍ مؤقتٍ باسم <code>basePrice</code> لتخزين القيمة الناتجة عن تنفيذ التعبير الرياضيّ بمعامل الجداء (أي المعامل <code>*</code>)، وسيُستخدَم هذا المتغيِّر لاحقًا في الأجزاء الشرطيّة من الشيفرة: |
+ | |||
+ | في لغة Java:<syntaxhighlight lang="java"> | ||
double calculateTotal() { | double calculateTotal() { | ||
double basePrice = quantity * itemPrice; | double basePrice = quantity * itemPrice; | ||
سطر 20: | سطر 22: | ||
} | } | ||
+ | </syntaxhighlight>في لغة #C:<syntaxhighlight lang="c#"> | ||
+ | double CalculateTotal() | ||
+ | { | ||
+ | double basePrice = quantity * itemPrice; | ||
+ | |||
+ | if (basePrice > 1000) | ||
+ | { | ||
+ | return basePrice * 0.95; | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | return basePrice * 0.98; | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight>في لغة PHP:<syntaxhighlight lang="php"> | ||
+ | $basePrice = $this->quantity * $this->itemPrice; | ||
+ | if ($basePrice > 1000) { | ||
+ | return $basePrice * 0.95; | ||
+ | } else { | ||
+ | return $basePrice * 0.98; | ||
+ | } | ||
+ | </syntaxhighlight>في لغة Python:<syntaxhighlight lang="python"> | ||
+ | def calculateTotal(): | ||
+ | basePrice = quantity * itemPrice | ||
+ | if basePrice > 1000: | ||
+ | return basePrice * 0.95 | ||
+ | else: | ||
+ | return basePrice * 0.98 | ||
+ | |||
+ | </syntaxhighlight>في لغة TypeScript:<syntaxhighlight lang="typescript"> | ||
+ | calculateTotal(): number { | ||
+ | let basePrice = quantity * itemPrice; | ||
+ | if (basePrice > 1000) { | ||
+ | return basePrice * 0.95; | ||
+ | } | ||
+ | else { | ||
+ | return basePrice * 0.98; | ||
+ | } | ||
+ | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | === بعد إعادة التصميم === | + | === <span> </span>بعد إعادة التصميم === |
− | تتمثَّل إعادة التصميم بالاستغناء عن المتغيِّر المؤقّت <code>basePrice</code> في الشيفرة السابقة واستدعاء التابع <code>basePrice()</code> بدلًا عنه، والذي سيعيد نتيجة التعبير الرياضيّ السابق لتصبح الشيفرة كما يلي:<syntaxhighlight lang="java"> | + | تتمثَّل إعادة التصميم بالاستغناء عن المتغيِّر المؤقّت <code>basePrice</code> في الشيفرة السابقة واستدعاء التابع <code>basePrice()</code> بدلًا عنه، والذي سيعيد نتيجة التعبير الرياضيّ السابق لتصبح الشيفرة كما يلي: |
+ | |||
+ | في لغة Java:<syntaxhighlight lang="java"> | ||
double calculateTotal() { | double calculateTotal() { | ||
if (basePrice() > 1000) { | if (basePrice() > 1000) { | ||
سطر 36: | سطر 79: | ||
} | } | ||
+ | </syntaxhighlight>في لغة #C:<syntaxhighlight lang="c#"> | ||
+ | double CalculateTotal() | ||
+ | { | ||
+ | if (BasePrice() > 1000) | ||
+ | { | ||
+ | return BasePrice() * 0.95; | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | return BasePrice() * 0.98; | ||
+ | } | ||
+ | } | ||
+ | double BasePrice() | ||
+ | { | ||
+ | return quantity * itemPrice; | ||
+ | } | ||
+ | </syntaxhighlight>في لغة PHP:<syntaxhighlight lang="php"> | ||
+ | if ($this->basePrice() > 1000) { | ||
+ | return $this->basePrice() * 0.95; | ||
+ | } else { | ||
+ | return $this->basePrice() * 0.98; | ||
+ | } | ||
+ | |||
+ | ... | ||
+ | |||
+ | function basePrice() { | ||
+ | return $this->quantity * $this->itemPrice; | ||
+ | } | ||
+ | </syntaxhighlight>في لغة Python:<syntaxhighlight lang="python"> | ||
+ | def calculateTotal(): | ||
+ | if basePrice() > 1000: | ||
+ | return basePrice() * 0.95 | ||
+ | else: | ||
+ | return basePrice() * 0.98 | ||
+ | |||
+ | def basePrice(): | ||
+ | return quantity * itemPrice | ||
+ | </syntaxhighlight>في لغة TypeScript:<syntaxhighlight lang="typescript"> | ||
+ | calculateTotal(): number { | ||
+ | if (basePrice() > 1000) { | ||
+ | return basePrice() * 0.95; | ||
+ | } | ||
+ | else { | ||
+ | return basePrice() * 0.98; | ||
+ | } | ||
+ | } | ||
+ | basePrice(): number { | ||
+ | return quantity * itemPrice; | ||
+ | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | |||
== لم إعادة التصميم؟ == | == لم إعادة التصميم؟ == | ||
لتمهيد الطريق لتطبيق تقنيّة الحلّ باستخراج التابع (extract method) على جزءٍ من تابعٍ طويلٍ نسبيًا، وإذا ما وُجِد التعبير ذاته في عدّة توابع أخرى في البرنامج فمن الأفضل إنشاء تابعٍ مشترك (common method). | لتمهيد الطريق لتطبيق تقنيّة الحلّ باستخراج التابع (extract method) على جزءٍ من تابعٍ طويلٍ نسبيًا، وإذا ما وُجِد التعبير ذاته في عدّة توابع أخرى في البرنامج فمن الأفضل إنشاء تابعٍ مشترك (common method). | ||
سطر 49: | سطر 140: | ||
== آلية الحل == | == آلية الحل == | ||
− | # التأكُّد من أن قيمة التعبير مُسندةٌ إلى المتغيِّر مرّةً واحدةً (وواحدة فقط) في التابع (method)، فإن لم يكن كذلك، فعليك الانتقال إلى تقنيّة تجزيء المتغيِّر المؤقَّت (split temporary variable) لضمان استخدام المتغيِّر لتخزين نتيجة التعبير فقط. | + | # التأكُّد من أن قيمة التعبير مُسندةٌ إلى المتغيِّر مرّةً واحدةً (وواحدة فقط) في التابع (method)، فإن لم يكن كذلك، فعليك الانتقال إلى تقنيّة [[Refactoring/split temporary variable|تجزيء المتغيِّر المؤقَّت]] (split temporary variable) لضمان استخدام المتغيِّر لتخزين نتيجة التعبير فقط. |
− | # تطبيق استخراج التابع (extract method) لوضع التعبير المطلوب في تابعٍ جديدٍ مستقلٍ، والتأكُّد من أنّ هذا التابع الجديد سيعيد قيمة التعبير دون أن يغيّر شيئًا من حالة الكائن (object state)، فإنْ لم يتحقَّق ذلك فيجب عندئذٍ تطبيق تقنيّة عزل الاستدعاء عن المُحدِّد (separate query from modifier). | + | # تطبيق [[Refactoring/extract method|استخراج التابع]] (extract method) لوضع التعبير المطلوب في تابعٍ جديدٍ مستقلٍ، والتأكُّد من أنّ هذا التابع الجديد سيعيد قيمة التعبير دون أن يغيّر شيئًا من حالة الكائن (object state)، فإنْ لم يتحقَّق ذلك فيجب عندئذٍ تطبيق تقنيّة [[Refactoring/separate query from modifier|عزل الاستدعاء عن المُحدِّد]] (separate query from modifier). |
# تبديل المتغيِّر إلى استدعاءٍ للتابع الجديد المُستحدَث. | # تبديل المتغيِّر إلى استدعاءٍ للتابع الجديد المُستحدَث. | ||
سطر 60: | سطر 151: | ||
* [https://refactoring.guru/replace-temp-with-query صفحة توثيق تبديل المتغيِّر المؤقَّت إلى استدعاء في موقع refactoring.guru.] | * [https://refactoring.guru/replace-temp-with-query صفحة توثيق تبديل المتغيِّر المؤقَّت إلى استدعاء في موقع refactoring.guru.] | ||
[[تصنيف:Refactoring]] | [[تصنيف:Refactoring]] | ||
+ | [[تصنيف:Refactoring Techniques]] | ||
+ | [[تصنيف:Refactoring Composing Methods]] |
المراجعة الحالية بتاريخ 08:23، 2 مارس 2019
المشكلة
تخزين نتيجة تعبيرٍ ما (expression) في متغيِّر محليٍّ (local variable) لاستخدامه لاحقًا في الشيفرة.
الحل
نقل التعبير بأكمله إلى تابعٍ (method) مستقلٍ يعيد نتيجته، وعندها سيكون استدعاء هذا التابع بديلًا عن استخدام المتغيِّر (variable)، ومن الممكن أيضًا دمج هذا التابع مع توابع أخرى عند الحاجة للقيام بذلك.
مثال
قبل إعادة التصميم
نلاحظ في الشيفرة الآتية وجود متغيِّرٍ مؤقتٍ باسم basePrice
لتخزين القيمة الناتجة عن تنفيذ التعبير الرياضيّ بمعامل الجداء (أي المعامل *
)، وسيُستخدَم هذا المتغيِّر لاحقًا في الأجزاء الشرطيّة من الشيفرة:
في لغة Java:
double calculateTotal() {
double basePrice = quantity * itemPrice;
if (basePrice > 1000) {
return basePrice * 0.95;
}
else {
return basePrice * 0.98;
}
}
في لغة #C:
double CalculateTotal()
{
double basePrice = quantity * itemPrice;
if (basePrice > 1000)
{
return basePrice * 0.95;
}
else
{
return basePrice * 0.98;
}
}
في لغة PHP:
$basePrice = $this->quantity * $this->itemPrice;
if ($basePrice > 1000) {
return $basePrice * 0.95;
} else {
return $basePrice * 0.98;
}
في لغة Python:
def calculateTotal():
basePrice = quantity * itemPrice
if basePrice > 1000:
return basePrice * 0.95
else:
return basePrice * 0.98
في لغة TypeScript:
calculateTotal(): number {
let basePrice = quantity * itemPrice;
if (basePrice > 1000) {
return basePrice * 0.95;
}
else {
return basePrice * 0.98;
}
}
بعد إعادة التصميم
تتمثَّل إعادة التصميم بالاستغناء عن المتغيِّر المؤقّت basePrice
في الشيفرة السابقة واستدعاء التابع basePrice()
بدلًا عنه، والذي سيعيد نتيجة التعبير الرياضيّ السابق لتصبح الشيفرة كما يلي:
في لغة Java:
double calculateTotal() {
if (basePrice() > 1000) {
return basePrice() * 0.95;
}
else {
return basePrice() * 0.98;
}
}
double basePrice() {
return quantity * itemPrice;
}
في لغة #C:
double CalculateTotal()
{
if (BasePrice() > 1000)
{
return BasePrice() * 0.95;
}
else
{
return BasePrice() * 0.98;
}
}
double BasePrice()
{
return quantity * itemPrice;
}
في لغة PHP:
if ($this->basePrice() > 1000) {
return $this->basePrice() * 0.95;
} else {
return $this->basePrice() * 0.98;
}
...
function basePrice() {
return $this->quantity * $this->itemPrice;
}
في لغة Python:
def calculateTotal():
if basePrice() > 1000:
return basePrice() * 0.95
else:
return basePrice() * 0.98
def basePrice():
return quantity * itemPrice
في لغة TypeScript:
calculateTotal(): number {
if (basePrice() > 1000) {
return basePrice() * 0.95;
}
else {
return basePrice() * 0.98;
}
}
basePrice(): number {
return quantity * itemPrice;
}
لم إعادة التصميم؟
لتمهيد الطريق لتطبيق تقنيّة الحلّ باستخراج التابع (extract method) على جزءٍ من تابعٍ طويلٍ نسبيًا، وإذا ما وُجِد التعبير ذاته في عدّة توابع أخرى في البرنامج فمن الأفضل إنشاء تابعٍ مشترك (common method).
فوائد تطبيق الحل
- تحسين قابلية قراءة الشيفرة (readability)، إذ إنّ فهمَ هدف التابع المُسمّى
getTax()
أسهل بكثيرٍ من محاولة فهم السطر التعبيريّorderPrice() * 0.2
. - الحدُّ من تكرار الشيفرة (duplication) وخاصّة عندما يكون التعبير المُستبدَل مُستخدَمًا في عدّة توابع (methods).
الأداء (Performance)
يتساءل الكثيرون: هل يسبِّب تطبيق هذا الحل مشكلةً بالأداء؟ والإجابة الصريحة: نعم! وذلك لأنّ الشيفرة الناتجة عن الحل ستعتمد على استدعاء تابعٍ جديدٍ بدلًا من اعتمادها على متغيِّرٍ محليٍّ، ولكن مع السرعة الفائقة في المعالجات (CPUs) المستخدَمة اليوم والمترجِمات (compilers) الفعّالة فالمشكلة يسيرة، إذ بالمقارنة مع الشيفرة سهلة القراءة والقدرة على إعادة استخدام التابع مرّاتٍ ومراتٍ في البرنامج (واللتان حصلنا عليهما من تطبيق الحل) فلم يعد هنالك أهمية لتلك المشكلة الطفيفة، أمّا إن كان المتغيِّر مستخدمًا لتخزين نتيجة تعبيرٍ مستهلكٍ للوقت ومكلفٍ حقًا فمن الجيّد العدول عن تطبيق تقنية الحل هذه.
آلية الحل
- التأكُّد من أن قيمة التعبير مُسندةٌ إلى المتغيِّر مرّةً واحدةً (وواحدة فقط) في التابع (method)، فإن لم يكن كذلك، فعليك الانتقال إلى تقنيّة تجزيء المتغيِّر المؤقَّت (split temporary variable) لضمان استخدام المتغيِّر لتخزين نتيجة التعبير فقط.
- تطبيق استخراج التابع (extract method) لوضع التعبير المطلوب في تابعٍ جديدٍ مستقلٍ، والتأكُّد من أنّ هذا التابع الجديد سيعيد قيمة التعبير دون أن يغيّر شيئًا من حالة الكائن (object state)، فإنْ لم يتحقَّق ذلك فيجب عندئذٍ تطبيق تقنيّة عزل الاستدعاء عن المُحدِّد (separate query from modifier).
- تبديل المتغيِّر إلى استدعاءٍ للتابع الجديد المُستحدَث.