الگوهای طراحی در زبان گو (Design patterns in go)-Builder

فهرست

سلام علی فرهادنیا هستم :)

امیدوارم حالتون خوب باشه.

خب توی این قسمت میخوام درباره الگوی طراحی سازنده یا همون Builder صحبت کنم.

این الگو جز دسته الگوهای طراحی سازنده (Creational Design Patterns) حساب میشه و به ما توی ساخت اشیا کمک میکنه.

تعریف کلی

الگوی builder اساساً در مورد ارائه نوعی API برای ساختن یک شی به صورت گام به گام است .

کاربرد

کاربرد این الگو وقتی مطرح میشه که ما یه شی پیچیده داریم که میخوایم مقدار دهیش کنیم و اونو بسازیم.

مثلا فرض کنید یه struct با 10 تا یا بیشتر متغیر دارین، خب اینکه وقت تعریف کردنش بخوایم همه اینارو مقدار دهی کنیم گاهی اوقات کار جالبی نیست و در بعضی موارد کار رو مخی هم هست:/ .

خب اینجا راه حل استفاده از builder ها و مقدار دهی گام به گام این struct هست.

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

حالا جلو تر با مثال بهتر متوجه میشین.

مثال

یکی از کاربرد های جالبی که من اخیرا دیدم توی این پکیج هست که کارش اینه توی ساختن کوئری های sql کمک کنه.

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

اول خواستم از روی خود کدهای پکیج توضیح بدم مثالو ولی به نظرم اومد بهتره یه نمونه ساده سازی شدشو خودم پیداه سازی کنم.

فقط دقت کنید که توی این مثال از یه کوئری خیلی ساده استفاده میکنم که خیلی مقاله طولانی نشه ولی همونطور که میدونید کوئری میتونه خیلی پیچیده ترو طولانی تر بشه که تا حد زیادی استفاد از builder هارو اینجا توجیه میکنه.

فرض کنید میخوایم یه همچین کوئری بسازیم:

SELECT fields FROM table_name;

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

  • SELECT
  • FROM

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

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

type selectQuery struct {
        fields                 string
        table_name      string
}

خب حالا بریم تو کار ساختن یه builder برای selectQuery که اسمشو میذاریم SelectQueryBuilder :

type SelectQueryBuilder struct {
        select_query     selectQuery
}


خب کار این SelectQueryBuilder اینه که اون select_query داخلشو مرحله به مرحله بسازه.

پس یه سری تابع باید براش در نظر بگیریم که این کارو انجام بدن:

func (b *SelectQueryBuilder) SELECT(fields string) *SelectQueryBuilder {
        b.select_query.fields = fields
        return b
}

این تابع fields رو مقدار دهی میکنه.

func (b *SelectQueryBuilder) FROM(table_name string) *SelectQueryBuilder {
        b.select_query.table_name = table_name
        return b
}

این تابع table_name رو ست میکنه.


یه نکته هم درباره اینکه چرا این توابع SelectQueryBuilder رو بعد اعمال اون تغییر برمیگردون بگم :

این کار باعث میشه وقت استفاده ازش بتونیم به صورت یه دنباله ازش استفاده کنیم و یه فرم جالبی داره که توی تابع main میبینید منظورم چیه.

خب تقریبا کار تمومه ولی خب در اخر ما خود این SelectQueryBuilder رو که نمیخوایم!

ما درواقع اون کوئری داخلشو میخوایم.

برای استفاده از اون کوئری داخلش راه های متنوعی وجود داره که از حوصله بحث خارجه من در ادامه به دوتاش اشاره میکنم:

راه اول اینه که میتونیم یه تابع دیگه با عنوان GetFinalQuery بنویسیم که همشو سر هم کنه و اون کوئری که خواستیمو به عنوان یه string برگردونه.

راه دوم اینه که مثلا یه تابع با نام Exec بنویسیم که یه کلاینت دیتابیسو به عنوان ورودی میگیره و خودش این کوئری رو به اون دیتابیس میزنه و نتیجرو برمیگردونه.

حالا اینجا چون نمیخوام خیلی وارد بحثای دیتابیس بشم مورد اولو مینویسم:

func (b *SelectQueryBuilder) GetFinalQuery() string {
        return fmt.Sprintf(&quotSELECT %s FROM %s;&quot,
            b.select_query.fields, b.select_query.table_name)
}


اها یادم رفت بهتره یه تابع هم برای ساخت SelectQueryBuilder جدید هم بنویسیم:

func NewSelectQueryBuilder() *SelectQueryBuilder {
         return &SelectQueryBuilder{select_query: selectQuery{}}
}

خب دیگه وقت نوشتن تابع main و امتحان کردن چیزاییه که نوشتیم:

func main() {
        squery := NewSelectQueryBuilder() 
        final_query := squery.SELECT(&quot*&quot).FROM(&quotbooks&quot).GetFinalQuery()

        fmt.Println(final_query)
}

توی تابع main میتونید اون حالت دنباله طوری که گفتمو ببینید اینکه میشه دستوراتو پشت سر هم استفاده کرد و این قابلیت به خاطر اینه که توابع SELECT و FROM در اخر SelectQueryBuilder رو برمیگردونن.

و اینم کوئری که تولید میکنه و در خروجی چاپ میکنه:

SELECT * FROM books;

بازم میگم که توی این مثال خیلی قضیرو ساده کردیم ولی خب خیلی کوئری های پیچیده تر و طولانی تری وجود داره که این builder ها واقعا میتونن کارو خیلی تمیزو راحت تر کنن.

امیدوارم از این قسمت لذت برده باشین.

کد هایی که نوشتمو میتونید توی این ریپازیتوری گیتهابم ببینید.

ممنون به خاطر وقتی که گذاشتین :)

فهرست