CodeGym /وبلاگ جاوا /Random-FA /نحوه عملکرد refactoring در جاوا
John Squirrels
مرحله
San Francisco

نحوه عملکرد refactoring در جاوا

در گروه منتشر شد
همانطور که برنامه نویسی را یاد می گیرید، زمان زیادی را صرف نوشتن کد می کنید. اکثر توسعه دهندگان مبتدی بر این باورند که این همان کاری است که در آینده انجام خواهند داد. این تا حدی درست است، اما وظیفه برنامه نویس شامل حفظ و بازسازی کد نیز می شود. امروز قصد داریم در مورد بازسازی مجدد صحبت کنیم. نحوه عملکرد refactoring در جاوا - 1

Refactoring در CodeGym

Refactoring دو بار در دوره CodeGym پوشش داده شده است: وظیفه بزرگ فرصتی را برای آشنایی با بازسازی واقعی از طریق تمرین فراهم می‌کند و درس بازآفرینی در IDEA به شما کمک می‌کند در ابزارهای خودکاری که زندگی شما را به طرز باورنکردنی آسان‌تر می‌کنند، شیرجه بزنید.

Refactoring چیست؟

این در حال تغییر ساختار کد بدون تغییر عملکرد آن است. به عنوان مثال، فرض کنید متدی داریم که 2 عدد را با هم مقایسه می کند و اگر عدد اول بزرگتر و نادرست باشد، درست را برمی گرداند .

    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. Refactoring درک کد نوشته شده توسط توسعه دهندگان دیگر را بهبود می بخشد.
  2. به یافتن و رفع اشکال کمک می کند.
  3. می تواند سرعت توسعه نرم افزار را افزایش دهد.
  4. به طور کلی، طراحی نرم افزار را بهبود می بخشد.
اگر بازسازی برای مدت طولانی انجام نشود، توسعه ممکن است با مشکلاتی از جمله توقف کامل کار مواجه شود.

"بوی کد"

زمانی که کد نیاز به refactoring داشته باشد، گفته می شود که "بو" دارد. البته نه به معنای واقعی کلمه، اما چنین کدهایی واقعاً جذاب به نظر نمی رسند. در زیر، تکنیک‌های اساسی بازسازی را برای مرحله اولیه بررسی خواهیم کرد.

کلاس ها و روش های بی دلیل بزرگ

کلاس‌ها و روش‌ها می‌توانند دست و پا گیر باشند و کار کردن با آنها به‌طور مؤثر دقیقاً به دلیل اندازه بزرگ غیرممکن است.

کلاس بزرگ

چنین کلاسی دارای تعداد زیادی خط کد و روش های مختلف است. معمولاً برای یک توسعه دهنده آسان تر است که یک ویژگی را به یک کلاس موجود اضافه کند تا اینکه یک کلاس جدید ایجاد کند، به همین دلیل است که کلاس رشد می کند. به عنوان یک قاعده، عملکرد بیش از حد در چنین کلاسی فشرده می شود. در این مورد، به انتقال بخشی از عملکرد به یک کلاس جداگانه کمک می کند. در بخش تکنیک‌های بازسازی در این مورد با جزئیات بیشتری صحبت خواهیم کرد.

روش طولانی

این «بو» زمانی به وجود می‌آید که یک توسعه‌دهنده عملکرد جدیدی به یک متد اضافه می‌کند: «چرا باید یک بررسی پارامتر را در یک متد جداگانه قرار دهم اگر بتوانم کد را اینجا بنویسم؟»، «چرا برای یافتن حداکثر به یک روش جستجوی جداگانه نیاز دارم.» عنصر در یک آرایه؟ بیایید آن را در اینجا نگه داریم. کد به این ترتیب واضح تر خواهد شد" و سایر تصورات غلط دیگر.

دو قانون برای بازسازی یک روش طولانی وجود دارد:

  1. اگر هنگام نوشتن یک متد تمایل به اضافه کردن نظر دارید، باید عملکرد را در یک روش جداگانه قرار دهید.
  2. اگر یک متد بیش از 10-15 خط کد می گیرد، باید وظایف و کارهای فرعی را که انجام می دهد شناسایی کنید و سعی کنید وظایف فرعی را در یک متد جداگانه قرار دهید.

چند راه برای حذف یک روش طولانی وجود دارد:

  • بخشی از عملکرد متد را به یک متد جداگانه منتقل کنید
  • اگر متغیرهای محلی از انتقال بخشی از عملکرد جلوگیری می کنند، می توانید کل شی را به روش دیگری منتقل کنید.

استفاده از بسیاری از انواع داده های اولیه

این مشکل معمولاً زمانی رخ می دهد که تعداد فیلدهای یک کلاس در طول زمان افزایش یابد. به عنوان مثال، اگر همه چیز (واحد پول، تاریخ، شماره تلفن و غیره) را به جای اشیاء کوچک در انواع اولیه یا ثابت ذخیره کنید. در این مورد، یک تمرین خوب انتقال یک گروه بندی منطقی از فیلدها به یک کلاس جداگانه (کلاس استخراج) خواهد بود. همچنین می توانید متدهایی را برای پردازش داده ها به کلاس اضافه کنید.

پارامترهای خیلی زیاد

این یک اشتباه نسبتا رایج است، به خصوص در ترکیب با یک روش طولانی. معمولاً اگر یک روش دارای عملکرد بیش از حد باشد، یا اگر یک روش چندین الگوریتم را پیاده سازی کند، این اتفاق می افتد. درک لیست های طولانی پارامترها بسیار دشوار است و استفاده از روش هایی با چنین لیست هایی ناخوشایند است. در نتیجه، بهتر است یک شیء کامل را رد کنید. اگر یک شی داده کافی ندارد، باید از یک شی کلی تر استفاده کنید یا عملکرد روش را به گونه ای تقسیم کنید که هر روش داده های منطقی مرتبط را پردازش کند.

گروه های داده

گروه هایی از داده های منطقی مرتبط اغلب در کد ظاهر می شوند. به عنوان مثال، پارامترهای اتصال پایگاه داده (URL، نام کاربری، رمز عبور، نام طرح و غیره). اگر نمی توان یک فیلد را از لیست فیلدها حذف کرد، این فیلدها باید به یک کلاس جداگانه (کلاس استخراج) منتقل شوند.

راه حل هایی که اصول OOP را نقض می کنند

این «بوها» زمانی اتفاق می‌افتد که یک توسعه‌دهنده طراحی OOP مناسب را نقض کند. این زمانی اتفاق می‌افتد که او توانایی‌های OOP را کاملاً درک نمی‌کند و نتواند به طور کامل یا درست از آنها استفاده کند.

عدم استفاده از ارث

اگر یک زیر کلاس فقط از یک زیرمجموعه کوچک از توابع کلاس والد استفاده کند، آنگاه بوی سلسله مراتب اشتباهی می دهد. وقتی این اتفاق می‌افتد، معمولاً روش‌های زائد به سادگی نادیده گرفته نمی‌شوند یا استثناهایی را ایجاد می‌کنند. یک کلاس دیگر را به ارث می برد به این معنی است که کلاس فرزند تقریباً از تمام عملکردهای کلاس والد استفاده می کند. مثالی از سلسله مراتب صحیح: نحوه عملکرد refactoring در جاوا - 2مثالی از سلسله مراتب نادرست: نحوه عملکرد refactoring در جاوا - 3

بیانیه سوئیچ

یک بیانیه چه اشکالی دارد switch؟ وقتی خیلی پیچیده می شود بد است. یک مشکل مرتبط، تعداد زیادی ifعبارات تودرتو است.

کلاس های جایگزین با رابط های مختلف

چندین کلاس یک کار را انجام می دهند، اما متدهای آنها نام های متفاوتی دارند.

میدان موقت

اگر یک کلاس یک فیلد موقت داشته باشد که یک شی فقط گاهی اوقات زمانی که مقدارش تنظیم می شود به آن نیاز دارد و در nullبقیه زمان ها خالی یا خدای ناکرده باقی می ماند، آن وقت کد بو می دهد. این یک تصمیم طراحی مشکوک است.

بوهایی که اصلاح را دشوار می کند

این بوها جدی ترند. بوهای دیگر عمدتا درک کد را دشوارتر می کنند، اما اینها شما را از تغییر آن جلوگیری می کند. وقتی سعی می کنید هر ویژگی جدیدی را معرفی کنید، نیمی از توسعه دهندگان کار را ترک می کنند و نیمی دیگر دیوانه می شوند.

سلسله مراتب موازی وراثت

این مشکل زمانی خود را نشان می دهد که برای زیر کلاس یک کلاس نیاز دارید که یک زیر کلاس دیگر برای کلاس دیگری ایجاد کنید.

وابستگی های توزیع شده یکنواخت

هر گونه تغییری از شما می خواهد که به دنبال تمام کاربردهای یک کلاس (وابستگی ها) باشید و تغییرات کوچک زیادی ایجاد کنید. یک تغییر - ویرایش در بسیاری از کلاس ها.

درخت پیچیده تغییرات

این بو برعکس بوی قبلی است: تغییرات بر تعداد زیادی از روش ها در یک کلاس تأثیر می گذارد. به عنوان یک قاعده، چنین کدی وابستگی آبشاری دارد: تغییر یک روش مستلزم آن است که چیزی را در روش دیگر و سپس در روش سوم و غیره اصلاح کنید. یک کلاس - تغییرات زیادی.

"بوی زباله"

دسته نسبتاً ناخوشایندی از بوها که باعث سردرد می شود. کد بی فایده، غیر ضروری، قدیمی. خوشبختانه، IDE ها و لنترهای مدرن یاد گرفته اند که نسبت به چنین بوهایی هشدار دهند.

تعداد زیادی نظرات در یک روش

یک روش تقریباً در هر خط نظرات توضیحی زیادی دارد. این معمولا به دلیل یک الگوریتم پیچیده است، بنابراین بهتر است کد را به چندین روش کوچکتر تقسیم کنید و نام های توضیحی برای آنها بگذارید.

کد تکراری

کلاس ها یا روش های مختلف از بلوک های کد مشابهی استفاده می کنند.

کلاس تنبل

یک کلاس عملکرد بسیار کمی دارد، اگرچه برنامه ریزی شده بود که بزرگ باشد.

کد استفاده نشده

یک کلاس، متد یا متغیر در کد استفاده نمی شود و وزن مرده است.

اتصال بیش از حد

این دسته از بوها با تعداد زیادی از روابط ناموجه در کد مشخص می شود.

روش های خارجی

یک روش از داده های یک شی دیگر بسیار بیشتر از داده های خود استفاده می کند.

صمیمیت نامناسب

یک کلاس به جزئیات پیاده سازی کلاس دیگر بستگی دارد.

تماس های طولانی کلاس

یک کلاس کلاس دیگر را فراخوانی می کند که از سومی داده درخواست می کند، که از کلاس چهارم داده می گیرد و غیره. چنین زنجیره طولانی تماس به معنای وابستگی زیاد به ساختار کلاس فعلی است.

کلاس وظیفه فروشنده

یک کلاس فقط برای ارسال یک کار به کلاس دیگر مورد نیاز است. شاید باید حذف شود؟

تکنیک های بازسازی

در زیر به تکنیک‌های اولیه بازآفرینی می‌پردازیم که می‌تواند به حذف بوهای کد شرح داده شده کمک کند.

یک کلاس را استخراج کنید

یک کلاس عملکردهای زیادی را انجام می دهد. برخی از آنها باید به کلاس دیگری منتقل شوند. برای مثال، فرض کنید کلاسی داریم 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) {
     // Some actions
     double yearlySalary = employee.getYearlySalary();
     double awards = employee.getAwards();
     double monthlySalary = getMonthlySalary(yearlySalary, awards);
     // Continue processing
 }
 
 public double getMonthlySalary(double yearlySalary, double awards) {
      return (yearlySalary + awards)/12;
 }
دارای 2 خط کامل است employeeMethodکه به دریافت مقادیر و ذخیره آنها در متغیرهای اولیه اختصاص داده شده است. گاهی اوقات چنین ساختارهایی می توانند تا 10 خط طول بکشند. انتقال خود شی و استفاده از آن برای استخراج داده های لازم بسیار ساده تر است:

 public void employeeMethod(Employee employee) {
     // Some actions
     double monthlySalary = getMonthlySalary(employee);
     // Continue processing
 }
 
 public double getMonthlySalary(Employee employee) {
     return (employee.getYearlySalary() + employee.getAwards())/12;
 }

ساده، مختصر و مختصر.

گروه بندی منطقی فیلدها و انتقال آنها به یک مجزا، classDespiteاین واقعیت است که مثال های بالا بسیار ساده هستند، و وقتی به آنها نگاه می کنید، ممکن است بسیاری از شما بپرسید "چه کسی این کار را انجام می دهد؟"، بسیاری از توسعه دهندگان به دلیل بی دقتی چنین خطاهای ساختاری را مرتکب می شوند. عدم تمایل به اصلاح مجدد کد، یا صرفاً نگرش "به اندازه کافی خوب است".

چرا refactoring موثر است

در نتیجه بازسازی خوب، یک برنامه دارای کدهای خوانا است، احتمال تغییر منطق آن ترسناک نیست، و معرفی ویژگی های جدید تبدیل به جهنم تجزیه و تحلیل کد نمی شود، بلکه تجربه ای خوشایند برای چند روز است. . اگر نوشتن یک برنامه از ابتدا آسان تر است، نباید اصلاح کنید. به عنوان مثال، فرض کنید تیم شما تخمین می زند که کار مورد نیاز برای درک، تجزیه و تحلیل، و کد refactor بیشتر از اجرای همان عملکرد از ابتدا خواهد بود. یا اگر کدی که قرار است دوباره فاکتور شود مشکلات زیادی دارد که اشکال زدایی آنها دشوار است. دانستن چگونگی بهبود ساختار کد در کار یک برنامه نویس ضروری است. و یادگیری برنامه نویسی در جاوا در CodeGym به بهترین شکل انجام می شود، دوره آنلاینی که بر تمرین تاکید دارد. بیش از 1200 کار با تأیید فوری، حدود 20 پروژه کوچک، وظایف بازی - همه اینها به شما کمک می کند تا در کدنویسی اطمینان داشته باشید. بهترین زمان برای شروع همین الان است :)

منابعی برای غوطه ور شدن بیشتر در بازسازی

معروف ترین کتاب در مورد بازسازی مجدد، "بازسازی. بهبود طراحی کدهای موجود" نوشته مارتین فاولر است. همچنین یک انتشار جالب در مورد بازسازی مجدد بر اساس کتاب قبلی وجود دارد: «بازسازی با استفاده از الگوها» نوشته جاشوا کریوسکی. صحبت از الگوها شد... هنگام بازسازی، دانستن الگوهای اولیه طراحی همیشه بسیار مفید است. این کتاب‌های عالی به این موضوع کمک می‌کنند: صحبت از الگوها... هنگام بازسازی، دانستن الگوهای اولیه طراحی همیشه بسیار مفید است. این کتاب های عالی به این امر کمک خواهند کرد:
  1. "الگوهای طراحی" توسط اریک فریمن، الیزابت رابسون، کتی سیرا و برت بیتس، از سری Head First
  2. «هنر کد خواندنی» نوشته داستین باسبول و ترور فوچر
  3. "Code Complete" نوشته استیو مک کانل، که اصول کدهای زیبا و ظریف را بیان می کند.
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION