JavaRush /مدونة جافا /Random-AR /كيف تعمل إعادة البناء في جافا

كيف تعمل إعادة البناء في جافا

نشرت في المجموعة
عند تعلم البرمجة، يتم قضاء الكثير من الوقت في كتابة التعليمات البرمجية. يعتقد معظم المطورين المبتدئين أن هذا هو نشاطهم المستقبلي. هذا صحيح جزئيًا، لكن مهام المبرمج تتضمن أيضًا صيانة التعليمات البرمجية وإعادة هيكلتها. اليوم سنتحدث عن إعادة البناء. كيف تعمل إعادة البناء في جافا - 1

إعادة البناء في دورة JavaRush

تغطي دورة JavaRush موضوع إعادة البناء مرتين: بفضل المهمة الكبيرة، هناك فرصة للتعرف على إعادة البناء الحقيقية في الممارسة العملية، وستساعدك محاضرة حول إعادة البناء في IDEA على فهم الأدوات التلقائية التي تجعل الحياة أسهل بشكل لا يصدق.

ما هي إعادة الهيكلة؟

هذا تغيير في بنية الكود دون تغيير وظيفته. على سبيل المثال، هناك طريقة تقارن بين رقمين وترجع صحيحًا إذا كان الأول أكبر، وخطأ إذا كان الرقم الأول :
public boolean max(int a, int b) {
    if(a > b) {
        return true;
    } else if(a == b) {
        return false;
    } else {
        return false;
    }
}
وكانت النتيجة كود مرهق للغاية. حتى المبتدئين نادرا ما يكتبون شيئا من هذا القبيل، ولكن هناك مثل هذا الخطر. يبدو أن سبب وجود كتلة هنا if-elseإذا كان بإمكانك كتابة طريقة أقصر بـ 6 أسطر:
public boolean max(int a, int b) {
     return a>b;
}
الآن تبدو هذه الطريقة بسيطة وأنيقة، على الرغم من أنها تفعل نفس الشيء كما في المثال أعلاه. هذه هي الطريقة التي تعمل بها إعادة البناء: فهي تغير بنية الكود دون التأثير على جوهره. هناك العديد من أساليب وتقنيات إعادة البناء، والتي سننظر فيها بمزيد من التفصيل.

لماذا هناك حاجة إلى إعادة الهيكلة؟

هناك عدة أسباب. على سبيل المثال، السعي وراء البساطة والإيجاز في التعليمات البرمجية. يعتقد أنصار هذه النظرية أن الكود يجب أن يكون موجزًا ​​قدر الإمكان، حتى لو كان يتطلب عشرات الأسطر من التعليقات لفهمه. يعتقد مطورون آخرون أنه يجب إعادة هيكلة الكود بحيث يكون مفهومًا مع أقل عدد ممكن من التعليقات. يختار كل فريق موقعه الخاص، لكن يجب أن نتذكر أن إعادة البناء ليست تخفيضًا . هدفها الرئيسي هو تحسين هيكل الكود. ويمكن إدراج عدة أهداف في هذا الهدف العالمي:
  1. تعمل إعادة البناء على تحسين فهم التعليمات البرمجية المكتوبة بواسطة مطور آخر؛
  2. يساعد في العثور على الأخطاء وإصلاحها؛
  3. يسمح لك بزيادة سرعة تطوير البرمجيات.
  4. بشكل عام يحسن تكوين البرامج.
إذا لم يتم تنفيذ إعادة البناء لفترة طويلة، فقد تنشأ صعوبات في التطوير، حتى التوقف الكامل للعمل.

"رائحة الكود"

عندما يتطلب الكود إعادة البناء يقولون أنه "رائح". بالطبع، ليس حرفيًا، لكن مثل هذا الكود لا يبدو جميلًا حقًا. أدناه سننظر في تقنيات إعادة الهيكلة الرئيسية للمرحلة الأولية.

عناصر كبيرة لا لزوم لها

هناك فئات وأساليب مرهقة يستحيل العمل معها بفعالية على وجه التحديد بسبب حجمها الضخم.

فئة كبيرة

تحتوي هذه الفئة على عدد كبير من أسطر التعليمات البرمجية والعديد من الطرق المختلفة. عادةً ما يكون من الأسهل على المطور إضافة ميزة إلى فئة موجودة بدلاً من إنشاء فئة جديدة، وهذا هو سبب نموها. كقاعدة عامة، يتم تحميل وظائف هذه الفئة بشكل زائد. في هذه الحالة، يساعد فصل جزء من الوظيفة في فئة منفصلة. سنتحدث عن هذا بمزيد من التفصيل في قسم تقنيات إعادة البناء.

طريقة كبيرة

تحدث هذه "الرائحة" عندما يضيف المطور وظائف جديدة إلى إحدى الطرق. "لماذا يجب أن أضع فحص المعلمات في طريقة منفصلة إذا كان بإمكاني كتابتها هنا؟"، "لماذا من الضروري فصل الطريقة للعثور على الحد الأقصى للعنصر في المصفوفة، فلنتركها هنا. بهذه الطريقة يكون الكود أكثر وضوحًا،» وغيرها من المفاهيم الخاطئة. هناك قاعدتان لإعادة هيكلة طريقة كبيرة:
  1. إذا كنت تريد، عند كتابة طريقة، إضافة تعليق إلى الكود، فأنت بحاجة إلى فصل هذه الوظيفة إلى طريقة منفصلة؛
  2. إذا كانت الطريقة تستغرق أكثر من 10 إلى 15 سطرًا من التعليمات البرمجية، فيجب عليك تحديد المهام والمهام الفرعية التي تؤديها ومحاولة فصل المهام الفرعية في طريقة منفصلة.
عدة طرق للقضاء على طريقة كبيرة:
  • فصل جزء من وظيفة الطريقة إلى طريقة منفصلة؛
  • إذا كانت المتغيرات المحلية لا تسمح لك باستخراج جزء من الوظيفة، فيمكنك تمرير الكائن بأكمله إلى طريقة أخرى.

استخدام العديد من أنواع البيانات البدائية

عادةً ما تحدث هذه المشكلة عند زيادة عدد الحقول لتخزين البيانات في فئة ما مع مرور الوقت. على سبيل المثال، إذا كنت تستخدم الأنواع البدائية بدلاً من الكائنات الصغيرة لتخزين البيانات (العملة، التاريخ، أرقام الهاتف، إلخ) أو الثوابت لتشفير أي معلومات. من الممارسات الجيدة في هذه الحالة تجميع الحقول بشكل منطقي ووضعها في فئة منفصلة (تحديد فئة). يمكنك أيضًا تضمين طرق لمعالجة هذه البيانات في الفصل الدراسي.

قائمة طويلة من الخيارات

خطأ شائع إلى حد ما، خاصة بالاشتراك مع طريقة كبيرة. يحدث هذا عادةً إذا كانت وظيفة الطريقة مثقلة، أو كانت الطريقة تجمع بين عدة خوارزميات. من الصعب جدًا فهم القوائم الطويلة من المعلمات، كما أن استخدام مثل هذه الأساليب غير مناسب. لذلك، من الأفضل نقل الكائن بأكمله. إذا لم يكن الكائن يحتوي على بيانات كافية، فمن المفيد استخدام كائن أكثر عمومية أو تقسيم وظائف الطريقة بحيث تعالج البيانات المرتبطة منطقيًا.

مجموعات البيانات

غالبًا ما تظهر مجموعات البيانات المرتبطة منطقيًا في التعليمات البرمجية. على سبيل المثال، معلمات الاتصال بقاعدة البيانات (عنوان URL، اسم المستخدم، كلمة المرور، اسم المخطط، وما إلى ذلك). إذا لم يكن من الممكن إزالة حقل واحد من قائمة العناصر، فإن القائمة عبارة عن مجموعة من البيانات التي يجب وضعها في فئة منفصلة (اختيار الفئة).

الحلول التي تفسد مفهوم OOP

يحدث هذا النوع من "الرائحة" عندما ينتهك المطور تصميم OOP. يحدث هذا إذا لم يفهم تماما إمكانيات هذا النموذج، ويستخدمها بشكل غير كامل أو غير صحيح.

رفض الميراث

إذا كانت فئة فرعية تستخدم جزءًا صغيرًا من وظائف الفئة الأصلية، فستبدو وكأنها تسلسل هرمي غير صحيح. عادةً، في هذه الحالة، لا يتم تجاوز الأساليب غير الضرورية أو يتم طرح الاستثناءات. إذا تم توريث فئة من فئة أخرى، فهذا يعني الاستخدام الكامل تقريبًا لوظائفها. مثال على التسلسل الهرمي الصحيح: كيف تعمل إعادة البناء في جافا - 2 مثال على التسلسل الهرمي غير الصحيح: كيف تعمل إعادة البناء في جافا - 3

بيان التبديل

ما يمكن أن يكون الخطأ مع المشغل switch؟ إنه أمر سيء عندما يكون تصميمه معقدًا للغاية. يتضمن هذا أيضًا العديد من الكتل المتداخلة if.

فئات بديلة مع واجهات مختلفة

العديد من الفئات تفعل الشيء نفسه بشكل أساسي، ولكن يتم تسمية أساليبها بشكل مختلف.

المجال المؤقت

إذا كان الفصل يحتوي على حقل مؤقت لا يحتاجه الكائن إلا في بعض الأحيان، عندما يكون مليئًا بالقيم، وبقية الوقت يكون فارغًا أو لا سمح الله، nullفإن الكود "رائحة"، ومثل هذا التصميم مشكوك فيه قرار.

الروائح التي تجعل التعديل صعبًا

هذه "الروائح" أكثر خطورة. أما الباقي فيؤدي بشكل رئيسي إلى إضعاف فهم الكود، في حين أن هذه لا تجعل من الممكن تعديله. عند تقديم أي ميزات، سيستقيل نصف المطورين، وسيصاب النصف الآخر بالجنون.

التسلسلات الهرمية الميراث الموازية

عندما تقوم بإنشاء فئة فرعية لفئة ما، يجب عليك إنشاء فئة فرعية أخرى لفئة أخرى.

توزيع التبعية الموحد

عند إجراء أي تعديلات، عليك البحث عن جميع التبعيات (الاستخدامات) لهذه الفئة وإجراء العديد من التغييرات الصغيرة. تغيير واحد - تعديلات في العديد من الفئات.

شجرة التعديل المعقدة

هذه الرائحة هي عكس الرائحة السابقة: فالتغيرات تؤثر على عدد كبير من الأساليب من نفس الفئة. كقاعدة عامة، تكون التبعية في مثل هذا الكود متتالية: بعد تغيير طريقة واحدة، تحتاج إلى إصلاح شيء ما في طريقة أخرى، ثم في طريقة ثالثة، وما إلى ذلك. فئة واحدة - العديد من التغييرات.

"روائح القمامة"

فئة من الروائح الكريهة إلى حد ما تسبب الصداع. رمز قديم عديم الفائدة وغير ضروري. لحسن الحظ، تعلمت IDEs وLinters الحديثة التحذير من هذه الروائح.

عدد كبير من التعليقات في الطريقة

تحتوي الطريقة على الكثير من التعليقات التوضيحية في كل سطر تقريبًا. يرتبط هذا عادةً بخوارزمية معقدة، لذا من الأفضل تقسيم الكود إلى عدة طرق أصغر ومنحهم أسماء ذات معنى.

ازدواجية الكود

تستخدم الفئات أو الأساليب المختلفة نفس مجموعات التعليمات البرمجية.

فئة كسول

يأخذ الفصل وظائف قليلة جدًا، على الرغم من التخطيط للكثير منه.

كود غير مستخدم

لا يتم استخدام فئة أو طريقة أو متغير في الكود وهو "وزن ساكن".

اقتران المفرط

تتميز هذه الفئة من الروائح بعدد كبير من الاتصالات غير الضرورية في الكود.

أساليب الطرف الثالث

تستخدم الطريقة بيانات كائن آخر في كثير من الأحيان أكثر من استخدام البيانات الخاصة بها.

العلاقة الحميمة غير المناسبة

يستخدم الفصل حقول الخدمة وطرق فئة أخرى.

مكالمات صفية طويلة

تستدعي إحدى الفئات فئة أخرى، والتي تطلب البيانات من الفئة الثالثة، ومن الفئة الرابعة، وهكذا. مثل هذه السلسلة الطويلة من المكالمات تعني مستوى عالٍ من الاعتماد على البنية الطبقية الحالية.

تاجر المهام الصفية

الفصل الدراسي مطلوب فقط لتمرير مهمة إلى فصل آخر. ربما ينبغي إزالته؟

تقنيات إعادة البناء

سنتحدث أدناه عن تقنيات إعادة البناء الأولية التي ستساعد في التخلص من روائح الكود الموصوفة.

اختيار الطبقة

يؤدي الفصل عددًا كبيرًا جدًا من الوظائف، ويحتاج بعضها إلى النقل إلى فصل آخر. على سبيل المثال، هناك فئة Humanتحتوي أيضًا على عنوان سكني وطريقة توفر العنوان الكامل:
class Human {
   private String name;
   private String age;
   private String country;
   private String city;
   private String street;
   private String house;
   private String quarter;

   public String getFullAddress() {
       StringBuilder result = new StringBuilder();
       return result
                       .append(country)
                       .append(", ")
                       .append(city)
                       .append(", ")
                       .append(street)
                       .append(", ")
                       .append(house)
                       .append(" ")
                       .append(quarter).toString();
   }
}
قد تكون فكرة جيدة وضع معلومات العنوان والطريقة (سلوك معالجة البيانات) في فئة منفصلة:
class Human {
   private String name;
   private String age;
   private Address address;

   private String getFullAddress() {
       return address.getFullAddress();
   }
}
class Address {
   private String country;
   private String city;
   private String street;
   private String house;
   private String quarter;

   public String getFullAddress() {
       StringBuilder result = new StringBuilder();
       return result
                       .append(country)
                       .append(", ")
                       .append(city)
                       .append(", ")
                       .append(street)
                       .append(", ")
                       .append(house)
                       .append(" ")
                       .append(quarter).toString();
   }
}

اختيار الطريقة

إذا كان من الممكن تجميع أي وظيفة في طريقة ما، فيجب وضعها في طريقة منفصلة. على سبيل المثال، طريقة لحساب جذور المعادلة التربيعية:
public void calcQuadraticEq(double a, double b, double c) {
    double D = b * b - 4 * a * c;
    if (D > 0) {
        double x1, x2;
        x1 = (-b - Math.sqrt(D)) / (2 * a);
        x2 = (-b + Math.sqrt(D)) / (2 * a);
        System.out.println("x1 = " + x1 + ", x2 = " + x2);
    }
    else if (D == 0) {
        double x;
        x = -b / (2 * a);
        System.out.println("x = " + x);
    }
    else {
        System.out.println("Equation has no roots");
    }
}
دعنا ننقل حساب الخيارات الثلاثة الممكنة إلى طرق منفصلة:
public void calcQuadraticEq(double a, double b, double c) {
    double D = b * b - 4 * a * c;
    if (D > 0) {
        dGreaterThanZero(a, b, D);
    }
    else if (D == 0) {
        dEqualsZero(a, b);
    }
    else {
        dLessThanZero();
    }
}

public void dGreaterThanZero(double a, double b, double D) {
    double x1, x2;
    x1 = (-b - Math.sqrt(D)) / (2 * a);
    x2 = (-b + Math.sqrt(D)) / (2 * a);
    System.out.println("x1 = " + x1 + ", x2 = " + x2);
}

public void dEqualsZero(double a, double b) {
    double x;
    x = -b / (2 * a);
    System.out.println("x = " + x);
}

public void dLessThanZero() {
    System.out.println("Equation has no roots");
}
أصبح الكود الخاص بكل طريقة أقصر وأكثر وضوحًا.

نقل الكائن بأكمله

عند استدعاء عملية ذات معلمات، يمكنك أحيانًا رؤية رمز مثل هذا:
public void employeeMethod(Employee employee) {
    // Некоторые действия
    double yearlySalary = employee.getYearlySalary();
    double awards = employee.getAwards();
    double monthlySalary = getMonthlySalary(yearlySalary, awards);
    // Продолжение обработки
}

public double getMonthlySalary(double yearlySalary, double awards) {
     return (yearlySalary + awards)/12;
}
في الطريقة employeeMethodيتم تخصيص ما يصل إلى سطرين للحصول على القيم وتخزينها في متغيرات بدائية. في بعض الأحيان تستغرق هذه التصاميم ما يصل إلى 10 أسطر. من الأسهل بكثير تمرير الكائن نفسه إلى الطريقة، حيث يمكنك استخراج البيانات الضرورية:
public void employeeMethod(Employee employee) {
    // Некоторые действия
    double monthlySalary = getMonthlySalary(employee);
    // Продолжение обработки
}

public double getMonthlySalary(Employee employee) {
    return (employee.getYearlySalary() + employee.getAwards())/12;
}
بسيطة وقصيرة وموجزة.

التجميع المنطقي للحقول ووضعها في فئة منفصلة

على الرغم من أن الأمثلة المذكورة أعلاه بسيطة للغاية، وعند النظر إليها قد يطرح الكثيرون السؤال "من يفعل هذا بالفعل؟"، إلا أن العديد من المطورين، بسبب عدم الانتباه، أو عدم الرغبة في إعادة بناء الكود، أو ببساطة "سيفعل ذلك"، يجعلون أخطاء هيكلية مماثلة.

لماذا تعتبر إعادة الهيكلة فعالة

نتيجة إعادة البناء الجيدة هي برنامج يسهل قراءة كوده، ولا تشكل التعديلات على منطق البرنامج تهديدًا، ولا يتحول إدخال الميزات الجديدة إلى جحيم لتحليل التعليمات البرمجية، بل إلى نشاط ممتع لبضعة أيام . لا ينبغي استخدام إعادة البناء إذا كان من الأسهل إعادة كتابة البرنامج من الصفر. على سبيل المثال، يقدر الفريق أن تكاليف العمالة لتحليل التعليمات البرمجية وتحليلها وإعادة هيكلتها أعلى من تكلفة تنفيذ نفس الوظيفة من البداية. أو أن الكود الذي يحتاج إلى إعادة هيكلته يحتوي على الكثير من الأخطاء التي يصعب تصحيحها. إن معرفة كيفية تحسين بنية الكود أمر إلزامي في عمل المبرمج. حسنًا، من الأفضل أن تتعلم برمجة Java في JavaRush - وهي دورة عبر الإنترنت مع التركيز على الممارسة. أكثر من 1200 مهمة مع التحقق الفوري، وحوالي 20 مشروعًا صغيرًا، ومهام الألعاب - كل هذا سيساعدك على الشعور بالثقة في البرمجة. أفضل وقت للبدء هو الآن :) Как устроен рефакторинг в Java - 4

موارد لمزيد من الغوص في إعادة البناء

أشهر كتاب عن إعادة البناء هو "إعادة البناء". "تحسين تصميم الكود الموجود" بقلم مارتن فاولر. هناك أيضًا منشور مثير للاهتمام حول إعادة البناء، مكتوب استنادًا إلى كتاب سابق بعنوان "إعادة البناء باستخدام الأنماط" بقلم جوشوا كيريوسكي. الحديث عن القوالب. عند إعادة البناء، من المفيد دائمًا معرفة أنماط تصميم التطبيق الأساسية. هذه الكتب الرائعة ستساعد في هذا:
  1. "أنماط التصميم" - بقلم إريك فريمان، وإليزابيث فريمان، وكاثي سييرا، وبيرت بيتس من سلسلة The Head First؛
  2. "الكود المقروء، أو البرمجة كفن" - داستن بوزويل، تريفور فوشر.
  3. "الكود المثالي" لستيف ماكونيل، والذي يحدد مبادئ الكود الجميل والأنيق.
حسنًا، بعض المقالات حول إعادة البناء:
  1. مهمة صعبة: فلنبدأ في إعادة هيكلة الكود القديم ؛
  2. إعادة بناء التعليمات البرمجية ؛
  3. إعادة هيكلة للجميع .
    تعليقات
    TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
    GO TO FULL VERSION