دیزاین‌پترن Builder در اندروید چیست؟

دیزاین‌پترن (Design Pattern) ها یا در فارسی الگو های طراحی، الگو های تست شده و مطمئنی هستند که توسط بهترین توسعه دهندگان نرم‎افزار در دنیا به عنوان یک روش امن برای حل مشکلات متداول در روند توسعه و مهندسی نرم‌افزار ساخته شدن. الگو های طراحی لزوما قطعه کد نیست که شما بتونید به راحتی و با کپی و پیست کردنش داخل کد نرم افزارتون مشکل مورد نظر رو حل کنید، در واقع شما با رعایت یک سری چارچوب مشخص شده، از پیش اومدن خطاها و مشکلات احتمالی جلوگیری و به روند توسعه کارتون سرعت میدین.

A design that doesn’t take change into account risks major redesign in the future. - Erich Gamma
A design that doesn’t take change into account risks major redesign in the future. - Erich Gamma

دیزاین پترن ها میتونن به سه دسته کلی تقسیم بشن:

  • ساختنی: ارائه‌ی راهکارهایی برای ساخت کلاس ها و اشیاء ( Singleton, Factory, Builder و... )
  • ساختاری: به منظور سازمان دهی و مرتب سازی اشیاء و کلاس ها ( Composite, Facade, Adapter و...)
  • رفتاری: ارائه راهکار برای ایجاد ارتباط بین اشیاء و کلاس ها ( Command, Observer, Strategy و... )

حالا که با مفهوم کلی دیزاین‌پترن ها آشنا شدیم میخوام بهتون یک دیزاین پترن محبوب در اندروید رو معرفی کنم، دیزاین پترن Builder. اگر تا به حال با کلاس AlertDialog کار کرده باشید، میدونید که برای ساختن یک دیالوگ جدید میتونید از این روش استفاده کنید:

new AlertDialog.Builder(this)
        .setTitle("دیزاین پترن ها")
        .setMessage("Builder دیزاین پترن")
        .create();

در واقع شما بدون اینکه نیاز باشه به سازنده کلاس AlertDialog چندین ورودی بدین، با فراخوانی چند متد به صورت متوالی یک شی از کلاس AlertDialog میسازید، هم کدتون خوانا تر میشه و هم اگر درحال ساختن یک کتابخانه برای استفاده بقیه برنامه‌نویس ها باشید اونها هم خیلی راحت تر میتونند با کلاس های شما کار کنند و تعامل بقیه با کدتون خیلی بهتر میشه. در واقع در تکه کد بالا از دیزاین‌پترن Builder استفاده شده.

حالا سوال اینجاست ما چطور میتونیم توی نرم‌افزارمون این قابلیت رو پیاده سازی کنیم؟

مثال:

اولین قدم برای پیاده سازی دیزاین‌پترن Builder در این مثال اینه که من یک کلاس User ایجاد کنم:

public class User {
    private String firstName;
    private String lastName;
    private int age;
}

هدف ما اینه کاربرمون به جای اینکه با دادن ورودی های مختلف، یک شی از این کلاس بسازه، با فراخوانی چند متد در نهایت بتونه یک نمونه از این کلاس داشته باشه، به این صورت:

new User.Builder()
        .setFirstName("حمید")
        .setLastName("ادیب زاده")
        .setAge(21)
        .create(); 

دومین قدم ایجاد یک کلاس static در در داخل کلاس User به نام Builder هست، که کاربرمون با فراخوانی این کلاس، شی رو ایجاد کنه.

static class Builder {
    private String firstName;
    private String lastName;
    private int age;

    public Builder setFirstName(final String firstName) {
        this.firstName = firstName;
        return this;
    }

    public Builder setLastName(final String lastName) {
        this.lastName = lastName;
        return this;
    }

    public Builder setAge(final int age) {
        this.age = age;
        return this;
    }

    public User create() {
        return new User(this);
    }
}

در واقع برای هر فیلد در کلاسمون، یک setter ایجاد کردیم که نوع بازگشتیش هم خود کلاس Builder هست. زمانی که آخرین متد یعنی create فراخوانی میشه، یک شی یوزر با اطلاعات داده شده به متدهای setter ساخته شده و به عنوان خروجی متد تحویل متغیر ما داده میشه.

بدون مقررات و طراحی، برنامه‌نویسی هنر قرار دادن باگ در یک فایل متنی خالی است - Louis Srygley
بدون مقررات و طراحی، برنامه‌نویسی هنر قرار دادن باگ در یک فایل متنی خالی است - Louis Srygley

قدم آخر این هست که سازنده‌ کلاس User رو private کنیم تا کاربر نتونه به صورت مستقیم یک نمونه از کلاس بسازه. و به بیانی دیگه در واقع فقط کلاس Builder بتونه این سازنده رو فراخوانی کنه.

public class User {
    private String firstName;
    private String lastName;
    private int age;

    private User(final Builder builder) {
        firstName = builder.firstName;
        lastName = builder.lastName;
        age = builder.age;
    }
}

به همین راحتی! :) الان میتونید با فراخوانی کلاس Builder و متد هاش از کلاس User یک شی جدید رو بسازید.

نکته بعدی اینکه اگر خواستید بعضی از فیلدهاتون رو اجباری کنید، میتونید این کار رو در متد create از کلاس Builder انجام بدید:

public User create() {
    User user = new User(firstName, lastName, age);
    if (user.firstName.isEmpty()) {
        throw new IllegalStateException("You forgot the name");
    }
    return user;
}

به عنوان آخرین نکته و نتیجه گیری میتونم به صورت کلی مزایای این دیزاین‌پترن رو به این صورت براتون شرح بدم:

  • زمانی که مدل/کلاس شما دارای فیلد ها و پارامتر های زیادی باشه که با فراخوانی اونها از طریق سازنده خوانایی و تمیز بودن کدتون به شدت کم بشه
  • زمانی که میخواید از یک کلاس بزرگ و پیچیده به روش ساده یک شی بسازید و کدتون بیشتر انتزاعی بشه
  • کنترل بیشتر روی مراحل اسمبل شدن اون شی داخل کلاستون و کپسوله سازی بهتر به کمک کلاس داخلی Builder

با این روش میتونید کلاس های بسیار خوانا تر و کاربرپسند تری بنویسید :)

امیدوارم این مطلب براتون مفید بوده باشه :)