الفرق بين المراجعتين ل"Refactoring/extract method"

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث
(أنشأ الصفحة ب' <noinclude>{{DISPLAYTITLE:استخراج التوابع (Extract Methods)}}</noinclude> == المشكلة == وجود أجزاء من الشيفرة يُمكن عزله...')
 
(مراجعة وتدقيق)
 
سطر 1: سطر 1:
 
 
<noinclude>{{DISPLAYTITLE:استخراج التوابع (Extract Methods)}}</noinclude>
 
<noinclude>{{DISPLAYTITLE:استخراج التوابع (Extract Methods)}}</noinclude>
 
== المشكلة ==
 
== المشكلة ==
سطر 10: سطر 9:
  
 
=== قبل إعادة التصميم ===
 
=== قبل إعادة التصميم ===
نلاحظ وجود جزء من الشيفرة لطباعة بعض البيانات (التفاصيل)، والتي يمكن عزلها بتابعٍ جديد، الشيفرة قبل إعادة التصميم بالشكل:<syntaxhighlight lang="java">
+
نلاحظ وجود جزء من الشيفرة لطباعة بعض البيانات (التفاصيل)، والتي يمكن عزلها بتابعٍ جديد، الشيفرة قبل إعادة التصميم بالشكل:
 +
 
 +
في لغة Java:<syntaxhighlight lang="java">
 
void printOwing() {
 
void printOwing() {
 printBanner();
+
  printBanner();
 //طباعة التفاصيل
+
 
 System.out.println("name: " + name);
+
  // طباعة التفاصيل
 System.out.println("amount: " + getOutstanding());
+
  System.out.println("name: " + name);
 +
  System.out.println("amount: " + getOutstanding());
 +
}
 +
</syntaxhighlight>في لغة #C:<syntaxhighlight lang="c#">
 +
void PrintOwing()
 +
{
 +
  PrintBanner();
 +
 
 +
  // طباعة التفاصيل
 +
  Console.WriteLine("name: " + name);
 +
  Console.WriteLine("amount: " + GetOutstanding());
 +
}
 +
</syntaxhighlight>في لغة PHP:<syntaxhighlight lang="php">
 +
function printOwing() {
 +
  $this->printBanner();
 +
 
 +
  // طباعة التفاصيل
 +
  print("name:  " . $this->name);
 +
  print("amount " . $this->getOutstanding());
 
}
 
}
 +
</syntaxhighlight>في لغة Python:<syntaxhighlight lang="python">
 +
def printOwing(self):
 +
    self.printBanner()
  
 +
    # طباعة التفاصيل
 +
    print("name:", self.name)
 +
    print("amount:", self.getOutstanding())
 +
</syntaxhighlight>في لغة TypeScript:<syntaxhighlight lang="typescript">
 +
printOwing(): void {
 +
  printBanner();
 +
 +
  // طباعة التفاصيل
 +
  console.log("name: " + name);
 +
  console.log("amount: " + getOutstanding());
 +
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 
=== بعد إعادة التصميم ===
 
=== بعد إعادة التصميم ===
تصبح الشيفرة بعد إعادة التصميم بالشكل:<syntaxhighlight lang="java">
+
تصبح الشيفرة بعد إعادة التصميم بالشكل:
 +
 
 +
في لغة Java:<syntaxhighlight lang="java">
 
void printOwing() {
 
void printOwing() {
 
 printBanner();
 
 printBanner();
سطر 35: سطر 70:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
إذ نُقلت تعليمتَي الطباعة إلى تابعٍ جديدٍ باسم <code>printDetails</code> (وهو اسم معبِّر ذو دلالة) مع تمرير معاملٍ (ناتجٍ عن الاستدعاء <code>getOutstanding()‎</code>) لهذا التابع الذي يحتاج القيمة لطباعتها.
+
في لغة #C:<syntaxhighlight lang="c#">
 +
void PrintOwing()
 +
{
 +
  PrintBanner();
 +
  PrintDetails(GetOutstanding());
 +
}
 +
 
 +
void PrintDetails(double outstanding)
 +
{
 +
  Console.WriteLine("name: " + name);
 +
  Console.WriteLine("amount: " + outstanding);
 +
}
 +
</syntaxhighlight>في لغة PHP:<syntaxhighlight lang="php">
 +
function printOwing() {
 +
  $this->printBanner();
 +
  $this->printDetails($this->getOutstanding());
 +
}
 +
 
 +
function printDetails($outstanding) {
 +
  print("name:  " . $this->name);
 +
  print("amount " . $outstanding);
 +
}
 +
</syntaxhighlight>في لغة Python:<syntaxhighlight lang="python">
 +
def printOwing(self):
 +
    self.printBanner()
 +
    self.printDetails(self.getOutstanding())
 +
 
 +
def printDetails(self, outstanding):
 +
    print("name:", self.name)
 +
    print("amount:", outstanding)
 +
</syntaxhighlight>في لغة TypeScript:<syntaxhighlight lang="typescript">
 +
printOwing(): void {
 +
  printBanner();
 +
  printDetails(getOutstanding());
 +
}
 +
 
 +
printDetails(outstanding: number): void {
 +
  console.log("name: " + name);
 +
  console.log("amount: " + outstanding);
 +
}
 +
</syntaxhighlight>إذ نُقلت تعليمتَي الطباعة إلى تابعٍ جديدٍ باسم <code>printDetails</code> (وهو اسم معبِّر ذو دلالة) مع تمرير معاملٍ (ناتجٍ عن الاستدعاء <code>getOutstanding()‎</code>) لهذا التابع الذي يحتاج القيمة لطباعتها.
  
 
== لم إعادة التصميم؟ ==
 
== لم إعادة التصميم؟ ==
سطر 58: سطر 133:
 
== مصادر ==
 
== مصادر ==
 
* [https://refactoring.guru/extract-method صفحة توثيق استخراج التوابع في موقع refactoring.guru.]
 
* [https://refactoring.guru/extract-method صفحة توثيق استخراج التوابع في موقع refactoring.guru.]
 +
[[تصنيف:Refactoring]]
 +
[[تصنيف:Refactoring Techniques]]
 +
[[تصنيف:Refactoring Composing Methods]]

المراجعة الحالية بتاريخ 08:23، 2 مارس 2019

المشكلة

وجود أجزاء من الشيفرة يُمكن عزلها وتجميعها سويةً.

الحل

نقل الشيفرة إلى تابعٍ (method) أو دالةٍ (function) جديدة والاستعاضة عن الجزء (بمكانه السابق) باستدعاءٍ لهذا التابع الجديد.

مثال

قبل إعادة التصميم

نلاحظ وجود جزء من الشيفرة لطباعة بعض البيانات (التفاصيل)، والتي يمكن عزلها بتابعٍ جديد، الشيفرة قبل إعادة التصميم بالشكل:

في لغة Java:

void printOwing() {
  printBanner();

  // طباعة التفاصيل
  System.out.println("name: " + name);
  System.out.println("amount: " + getOutstanding());
}

في لغة #C:

void PrintOwing() 
{
  PrintBanner();

  // طباعة التفاصيل
  Console.WriteLine("name: " + name);
  Console.WriteLine("amount: " + GetOutstanding());
}

في لغة PHP:

function printOwing() {
  $this->printBanner();

  // طباعة التفاصيل
  print("name:  " . $this->name);
  print("amount " . $this->getOutstanding());
}

في لغة Python:

def printOwing(self):
    self.printBanner()

    # طباعة التفاصيل
    print("name:", self.name)
    print("amount:", self.getOutstanding())

في لغة TypeScript:

printOwing(): void {
  printBanner();

  // طباعة التفاصيل
  console.log("name: " + name);
  console.log("amount: " + getOutstanding());
}

بعد إعادة التصميم

تصبح الشيفرة بعد إعادة التصميم بالشكل:

في لغة Java:

void printOwing() {
 printBanner();
 printDetails(getOutstanding());
}

void printDetails(double outstanding) {
 System.out.println("name: " + name);
 System.out.println("amount: " + outstanding);
}

في لغة #C:

void PrintOwing()
{
  PrintBanner();
  PrintDetails(GetOutstanding());
}

void PrintDetails(double outstanding)
{
  Console.WriteLine("name: " + name);
  Console.WriteLine("amount: " + outstanding);
}

في لغة PHP:

function printOwing() {
  $this->printBanner();
  $this->printDetails($this->getOutstanding());
}

function printDetails($outstanding) {
  print("name:  " . $this->name);
  print("amount " . $outstanding);
}

في لغة Python:

def printOwing(self):
    self.printBanner()
    self.printDetails(self.getOutstanding())

def printDetails(self, outstanding):
    print("name:", self.name)
    print("amount:", outstanding)

في لغة TypeScript:

printOwing(): void {
  printBanner();
  printDetails(getOutstanding());
}

printDetails(outstanding: number): void {
  console.log("name: " + name);
  console.log("amount: " + outstanding);
}

إذ نُقلت تعليمتَي الطباعة إلى تابعٍ جديدٍ باسم printDetails (وهو اسم معبِّر ذو دلالة) مع تمرير معاملٍ (ناتجٍ عن الاستدعاء getOutstanding()‎) لهذا التابع الذي يحتاج القيمة لطباعتها.

لم إعادة التصميم؟

تصعُب معرفة ما ينفِّذه التابع (method) باذدياد عدد أسطره وهذا هو السبب الرئيسيُّ لإعادة تصميمه، كما وتُعدُّ تقنية استخراج التابع (extract method) خطوةً جيّدةُ لتغيير الشيفرة لما هو أفضل.

فوائد تطبيق الحل

  • الحصول على شيفرةٍ مقروءةٍ أكثر، وخاصّة بوجود اسمٍ معبِّرٍ ذي دلالةٍ بهدف التابع، مثل: createOrder()‎ و renderCustomerInfo()‎ و ...إلخ.
  • الحدّ من تكرار الشيفرة (duplication) في البرنامج، إذ من الممكن الاستفادة من الشيفرة الموجودة في التابع المُستحدَث في عدّة أجزاء أخرى من البرنامج عبر استدعاءاتٍ بسيطة تحلِّ محلها.
  • عزل الأجزاء المستقلَّة من الشيفرة، وهذا يعني نسبةً أخفض من الأخطاء (وخاصّةً عند تعديل المتغيِّر الخطأ [wrong variable]).

آلية الحل

  1. إنشاء تابعٍ (method) جديدٍ وتسميته تسميةً معبِّرةً تدلُّ على محتواه والهدف منه.
  2. نسخ الشيفرة المطلوبة إلى ذلك التابع وحذفها من موضعها السابق وتبديلها هناك إلى استدعاءٍ للتابع الجديد.
  3. الانتباه للمتغيِّرات (variables) المُستخدَمة في تلك الشيفرة، فإن كانت مُعرَّفة بها وغير مُستخدَمةٍ خارجها فستبقى كما هي دون أيّ تعديلٍ لتصبح متغيِّرات محليّة (local variables) للتابع الجديد. أمّا إن كانت المتغيِّرات مُعرَّفة قبل الشيفرة المُستخرَجة فيجب عندئذٍ تمريرها كمعاملاتٍ (parameters) للتابع الجديد بهدف الحصول على قيمها المُخزَّنة مسبقًا، ومن الأفضل -ببعض الحالات- التخلُّص منها عبر تبديل المتغيِّر المؤقَّت (temp) إلى استدعاء (query).
  4. عندما تطرأ بعض التعديلات على المتعيِّر المحليّ (local variable) في الشيفرة المُستخرَجة فهذا يعني أن القيمة الجديدة المعدَّلة ضروريّة في مكانٍ ما آخر في التابع الرئيسيّ (main method)، فكن يقظًا بمثل تلك الحالة؛ إذ يجب أن تعيد القيمة الجديدة للمتغيِّر إلى التابع الرئيسي للحفاظ على سلامة أداء البرنامج.

انظر أيضًا

مصادر