آموزش برنامه‌نویسی سوئیفت (Swift): پروتکل پایه در سوئیفت، اکستنشن ها و زیرنویس ها – بخش نهم

۹۳ بازدید
آخرین به‌روزرسانی: ۲۸ شهریور ۱۴۰۲
زمان مطالعه: ۱۲ دقیقه
آموزش برنامه‌نویسی سوئیفت (Swift): پروتکل پایه در سوئیفت، اکستنشن ها و زیرنویس ها – بخش نهم

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

سوئیفت

ما نهایت تلاش خود را خواهیم کرد که مسائل مختلف را به ترتیبی بررسی کنیم که بتوان از مجموع آن‌ها نتیجه‌ای گرفت و بدین ترتیب بدانید که از هر موضوعی در کجا و چه زمانی باید استفاده کنید. می‌دانیم که برخی راهنماها از این مرحله آغاز می‌کنند و به سرعت وارد موضوع اکستنشن‌ها می‌شوند؛ اما ما چنین کاری نکردیم. اکستنشن‌ها بلوک‌های ساختمانی هستند که با آن‌ها می‌توانیم بر اساس مفاهیمی که تاکنون آموخته‌ایم کار بیشتری انجام دهیم و به همین دلیل تصمیم داریم که کار خود را با پروتکل‌ها آغاز کنیم.

پروتکل

می‌دانیم که هم پروتکل و هم اکستنشن تعریف مبهمی دارند و تعریفی که از سوی اپل ارائه شده نیز به روشن‌تر شدن موضوع در آغاز کار کمک چندانی نمی‌کند. تعریفی که در کتاب اپل آمده چنین است:

یک پروتکل به تعریف نقشه اولیه‌ای برای متدها، مشخصه‌ها و دیگر الزامات می‌پردازد که برای اجرای یک وظیفه خاص یا بخشی از یک کارکرد لازم هستند. پروتکل می‌تواند از سوی کلاس، سازه یا enumeration جهت ارائه پیاده‌سازی واقعی از این الزامات مورد استفاده قرار گیرد. هر نوعی که الزامات یک پروتکل را تأمین کند، گفته می‌شود که مطابق پروتکل است.

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

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

1enum PitchStyle {
2    case fastBall, curveball, slider
3}
4enum BadPitchStyle {
5    case ball, walk
6}
7
8protocol Pitcher {
9    var energy: Int
10    
11    func throwBall(with style: PitchStyle)
12    func throwBadPitch(with style: BadPitchStyle)
13}
14
15struct Player: Pitcher {
16    var energy = 100
17    var team: String
18    var jerseyNumber: Int
19    
20    func throwBall(with style: PitchStyle) {
21         // logic to throw ball
22    }
23    
24    func throwBadPitch(with style: BadPitchStyle) {
25         // logic to throw bad pitch
26    }
27}

پروتکل سوئیفت

چند خط کد اضافی نیز ارائه کرده‌ایم تا متوجه شوید که همه چیز از کجا می‌آید. ما در ابتدا enum-هایی برای تعریف سبک‌های پرتاب توپ (pitching) تعریف کردیم. سپس یک پروتکل تعریف کرده‌ایم. این پروتکل برای موقعیت پرتاب کننده توپ است و روش‌های مختلف ممکن و یک مشخصه به نام pitchers تعریف می‌کند که مفید است. در ادامه یک struct به نام Pitcher ساخته‌ایم که می‌توانیم از آن برای ایجاد پرتاب‌کننده‌هایی برای بازی خود استفاده کنیم. درون این سازه همه متدها و مشخصه‌هایی که پرتابگر برای اجرای صحیح بازی نیاز دارد آمده‌اند.

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

با این که این وضعیت چندان مفید به نظر نمی‌رسد؛ اما استفاده از پروتکل‌ها بدین ترتیب در تیم‌های بزرگ بسیار مفید است. چون هیچ کس دیگری در تیم وقتی کلاسی را به صورت JSONManager نامگذاری می‌کنید نمی‌تواند بداند که کاربرد آن چیست. ممکن است این تصور مطرح شود که آیا این کلاس، داده‌های JSON را گرفته و آن‌ها را قالب‌بندی می‌کند؟ آیا آن‌ها را جایی ذخیره می‌کند؟ چه کار می‌کند؟ اگر اعلان کلاس را به صورت زیر بنویسیم:

1JSONManager: Downloadable، Uploadable، Storable، Retrievable

این اعلان به شما چه می‌گوید؟ این اعلان مشخص می‌کند که JSONManager می‌تواند JSON را دانلود، آن را آپلود و در جای دیگری ذخیره‌سازی کند یا JSON را از جایی که ذخیره شده است بازیابی کند. برای ما مهم نیست که این کلاس داده‌ها را کجا قرار می‌دهد یا از کجا بر می‌دارد، برای ما تنها نکته مهم این است که JSONManager قرار است به ما در اجرای این وظایف کمک کند. این کار «انتزاع» (abstraction) نامیده می‌شود. نکته بهتر آن است که اگر کلاس دیگری داشته باشیم که نام آن DataManager باشد، می‌توانیم از برخی یا همه این پروتکل‌ها استفاده کنیم و مطمئن باشیم که هر پروتکل که در آن به کار بگیریم، می‌توانیم به وسیله آن داده‌های خود را به دست بیاوریم.

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

1protocol Pitcher {}

سپس باید فکر کنیم که پرتابگر در بازی بیسبال چه نقشی دارد؟ مشخص است که وظیفه وی پرتاب توپ است، پس می‌نویسیم:

1func throwBall((

اما این پروتکل خیلی ساده است، پس انواع مختلفی برای پرتاب توپ تعریف می‌کنیم:

1func throwBall(with:)

می‌دانیم که ارسال رشته‌ها با استفاده از enum بسیار امن‌تر است، پس آن را به صورت زیر تغییر می‌دهیم:

1enum PitchStyle { ... }

بدین ترتیب تابع زیر پایان می‌یابد:

1func throwBall(with: PitchStyle)

همچنین می‌دانیم که هر پرتابگری در طی پرتاب‌های خود، برخی پرتاب‌های بد نیز دارد:

1enum BadPitchStyle { ... }

و یک تابع به این منظور اضافه می‌کنیم:

1func throwBadPitch(with: BadPitchStyle)

اما از کجا بدانیم که پرتابگری پرتاب بد داشته است؟

1var energy: Int

با استفاده از این منطق، در نهایت به پروتکلی دست می‌یابیم که در ادامه می‌توانیم طرز کار کلاس‌های خود را بر مبنای آن تعریف کنیم. اپل نیز تأکید دارد که کلاس‌ها را ابتدا با پروتکل‌ها آغاز کنیم. ما این نکته را در بخش اول این سری آموزش‌های سوئیفت نیز بیان کردیم و اینک مورد تأکید مجدد قرار می‌دهیم.

آغاز تعریف کلاس با پروتکل ممکن است خسته‌کننده و دشوار باشد، چون نتایج آنی ندارد؛ اما این امکان را به ما می‌دهد که همه مشخصه‌ها و متدهای مورد نیاز برای کارکرد صحیح کلاس یا strcut را برآورد کنیم. این کار شبیه به تحلیل اپلیکیشن پیش از شروع به کدنویسی است. بدین ترتیب اگر چیزی خراب بشود اصلاح آن دشوار نخواهد بود و کافی است نام یک متد یا مشخصه در پروتکل تغییر داده شود و تنها یک یا چند خط اصلاح می‌شود.

همان طور که می‌دانیم در هر تیم تنها یک پرتابگر وجود دارد و ما می‌توانیم از این struct برای ساخت دو پرتابگر استفاده کنیم. اما باید بدانیم که این متدها محدود به پرتابگرها است. در ادامه خواهیم دید که چگونه می‌توانیم این کار را برای «بازیکن آوتفیلد» (Outfielder) نیز اجرا کنیم.

1protocol Fielder {
2    var energy: Int
3    var jerseyNumber: Int
4    var team: String
5    var receivingPlayer: Player?
6    var hasBall: Bool
7  
8    func throwBall(to player: player, with energy: Int) {}
9    func findPlayerNearestToRunner() -> Player {}
10    func catchBall(using energy: Int) -> Bool {}
11    func getBall() {}
12}
13
14struct leftFieldPlayer: Fielder {
15    ...
16}
17
18struct rightFieldPlayer: Fielder {
19    ...
20}
21
22struct centerFieldPlayer: Fielder {
23    ...
24}
25
26struct shortStop: Fielder {
27    ...
28}
29
30struct baseman: Baseman, Fielder {
31    ...
32}
33
34struct Pitcher: Pitcher, Fielder {
35    ...
36}

همان طور که دیدیم پروتکل‌ها می‌توانند ما را ملزم سازند که از کد صحیح استفاده کنیم و مهم نیست که چند بار از آن استفاده کنیم. بدین ترتیب مقاصد مورد نظر برنامه‌نویس و تیمش شفاف‌تر می‌شوند. اما با بسط دادن پروتکل‌ها می‌توانیم بر قدرت آن‌ها بیفزاییم.

اکستنشن سوئیفت

اکستنشن

اکستنشن یک نام بزرگ برای مفهومی ساده است. اکستنشن در واقع به بسط کارکرد یک کلاس، struct یا پروتکل گفته می‌شود. برای این که بدانید از آن‌ها چگونه باید استفاده کنید، ارجاعاتی به کتابخانه Foundation در سوئیفت خواهیم داشت. Foundation یک کلاس به نام UITableView دارد. کلاس UITableView وظیفه‌ای ذاتی برای مدیریت نمایش نماهای جدولی دارد. با این وجود می‌توانیم کارکرد آن را با ارائه بسطی به نام UITableViewDataSource گسترش دهیم. بدین ترتیب کلاسی ارائه می‌کنیم که از این پروتکل بهره می‌گیرد و توانایی ارائه زمینه‌ای برای روش نمایش داده‌ها درون نمای جدولی می‌یابد.

تصویر فوق مثال مناسبی از یک اکستنشن است.

1class AftermarketUpgrade {
2    var hasSpoiler = true
3    var hasRacingEngine = true
4    var hasRaceTires = true
5    var hasWeightReduction = true
6}
7
8class Vehicle {
9    var numberOfWheels: Int
10    var numberOfSeats: Int
11}
12
13extension Vehicle: AfterMarketUpgrades {
14    // adopted all of the variables in their default state
15    // and separated AfterMarketUpgrades code into it's own
16    // section (typically another file)
17}

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

1class MySuperAwesomeNetworkingCode {
2    var data: Data?
3}
4
5extension MySuperAwesomeNetworkingCode {
6    // already starting to regret that long class name
7    // download code goes here
8    data = downloadData()    // data came from the class declaration
9}
10
11extension MySuperAwesomeNetworkingCode {
12    // upload code goes here
13}
14
15extension MySuperAwesomeNetworkingCode {
16    // parse and send data to another class that stores
17    // the data in a database
18}

با این که ممکن است این کار تا حدودی زیاده‌روی محسوب شود؛ اما بدین ترتیب می‌توانید از کامنت ها برای جداسازی کد به بخش‌های منطقی با استفاده از دستور زیر استفاده کنید:

1//MARK: title of section

همچنین می‌توانید با استفاده از این نشانگرها در قسمت فوقانی پنجره ویرایشگر Xcode به بخش‌های مختلف کد بروید.

پروتکل سوئیفت

با کلیک کردن روی «No Selection» می توانید درخت لایه جاری را باز کنید.

پروتکل سوئیفت

بدین ترتیب می‌توانیم با کلیک کردن روی هر بخش از درخت به آن بخش برویم و Xcode بی‌درنگ روی آن بخش متمرکز می‌شود. با استفاده از دستور زیر می‌توانیم یک خط افقی بالای مشخصه‌ها ایجاد کنیم:

1//MARK: — Properties

این وضعیت شبیه به استفاده از دستور زیر برای درج یک خط افقی زیر مشخصه‌ها است:

1//MARK: Properties —

همچنین می‌توانیم هر دو خط بالا و پایین را درج کنیم؛ اما بهتر است تنها از یکی از آن‌ها استفاده کنیم تا به بخش‌های مشخصی تقسیم شوند. در هر حال می‌توانید از رویه‌ای که ترجیح می‌دهید استفاده کنید.

در این بخش تنها به یک نکته دیگر اشاره می‌کنیم و به بررسی موضوع اکستنشن‌ها باز می‌گردیم. صرف‌نظر از افراز کد به بخش‌های منطقی این نشانگرها یک کار دیگر نیز انجام می‌دهند که باعث می‌شود عاشق پروتکل‌ها شوید و همه جا از آن‌ها استفاده کنید.

بنابراین دانستیم که پروتکل‌ها برای کار تیمی عالی هستند و این موضوع از آغاز مشخص بود؛ اما ممکن است از خود بپرسید که آیا اگر یک توسعه‌دهنده منفرد باشید، باید از پروتکل‌ها استفاده کنید یا نه؟ در این حالت شما می‌دانید که دقیقاً چه کاری دارید انجام می‌دهید و در صورتی که پس از یک سال برای یک به‌روزرسانی مجدداً کد خودتان مراجعه کنید هم نیاز به یادآوری نخواهید داشت. به خصوص این وضعیت در مواردی معنی بیشتری می‌یابد که مجبور باشید کد بیشتری بنویسید که از نظر اپلیکیشن یا تابعتان هیچ ضروری ندارد.

آیا پروتکل Fielder را که در بخش‌های قبلی مطرح کردیم به خاطر دارید؟ چه می‌شود اگر به شما بگوییم که در آنجا لازم نبود الزامات پروتکل را بنویسید و لازم نبود آیکون قرمزرنگ کنار پروتکل را هر بار کلیک کنید تا همه متدها در همه وهله‌ها که پروتکل استفاده شده بود پیاده‌سازی شوند؟ آیا در این صورت عاشق پروتکل نمی‌شوید؟

واقعیت محض این است که در مثال فوق ما 6 مشخصه و 4 متد را هر بار نوشته‌ایم که شاید کاری دیوانه‌وار به نظر برسد، چون در این حالت 42 مشخصه و 28 متد داریم و منطق استفاده شده درون هر متد را نیز حساب نکرده‌ایم. اکستنشن‌ها به همراه پروتکل‌ها قدرت بسیاری می‌یابند.

اینک به توضیح عملی گفته‌های خود می‌پردازیم. در کد زیر بخش‌هایی از کدهای فوق را کپی کرده و چسبانده‌ایم. تا مجدداً به کدهای فوق مراجعه نکنید:

1protocol Fielder {
2    var receivingPlayer: Player?
3    var energy: Int
4    var jerseyNumber: Int
5    var team: String
6    var hasBall: Bool
7    
8    func throwBall(to player: player, with energy: Int) {}
9    func findPlayerNearestToRunner() -> Player {}
10    func catchBall(using energy: Int) -> Bool {}
11    func getBall() {}
12}
13
14extension Fielder {
15    var energy = 100
16    var hasBall = false
17    
18    func throwBall(to player: player, with energy: Int) {
19         // throw logic
20    }
21    
22    func findPlayerNearestToRunner() -> Player {
23        // find logic
24    }
25    
26    func catchBall(using energy: Int) -> Bool {
27        // catch ball logic using boolean to return ball
28    }
29    
30    func getBall() {
31        // get the ball if it wasn't caught
32    }
33}
34
35struct leftFieldPlayer: Fielder {
36    // set jerseyNumber and team only
37}
38
39struct rightFieldPlayer: Fielder {
40    // set jerseyNumber and team only
41}
42
43struct centerFieldPlayer: Fielder {
44    // set jerseyNumber and team only
45}
46
47struct shortStop: Fielder {
48    // set jerseyNumber and team only
49}
50
51struct baseman: Baseman, Fielder {
52    // set jerseyNumber and team only
53}
54
55struct Pitcher: Pitcher, Fielder {
56    // set jerseyNumber and team
57    hasBall = true
58}

در این بخش قصد نداریم در مورد روش نوشتن پروتکل توضیح دهیم؛ بلکه در مورد اکستنشن پروتکل صحبت خواهیم کرد. این اکستنشن به تعریف مقادیر پیش‌فرض برای همه چیزهایی که از پروتکل Fielder استفاده می‌کنند می‌پردازد. این بدان معنی است که از آنجا که متد یک متغیر دارد، لازم نیست پیاده‌سازی آن را در هر وهله از شیء که یک Fielder است پیاده‌سازی کنیم. ما تنها باید آن را یک بار ایمپورت کنیم و این کار را کرده‌ایم. این به آن معنی است که اگر قصد داشتیم یک موقعیت دیگر به نام Catcher بسازیم، تنها کاری که باید انجام دهیم این است که آن را از Fielder دریافت کنیم. بدین ترتیب همه مقادیر مشخصه و متدهای پیش‌فرض درفت می‌شوند. ما تنها باید مشخصه‌های غیر آپشنال را که در اکستنشن پروتکل تعیین نشده‌اند پیاده‌سازی کنیم.

بنابراین اگر در مورد مثال JSON که قبلاً ارائه کردیم تأمل کنید، می‌توانید اکستنشن‌هایی برای هر کدام از پروتکل‌هایی که JSONManager یا DataManager استفاده می‌کنند بسازید و آن‌ها را در کلاس‌های جدید بگنجانید و نیازی به نوشتن کد اضافی به جز کد مورد نیاز برای مدیریت خاص آن کلاس یا سازه نیز وجود ندارد.

پروتکل

زیرنویس (Subscript)

منظور از یک زیرنویس تنها یک روش متفاوت برای نامگذاری اندیس است. در واقع کار کردن با زیرنویس به اندازه کار با آرایه‌ها و دیکشنری‌ها که در بخش‌های قبلی بیان کردیم آسان است. شما می‌توانید آرایه‌ها را با استفاده از [myArray[0 برای دریافت عنصر نخست آرایه، زیرنویس کنید.

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

دیکشنری‌ها از زیرنویس با استفاده از کلیدهایشان استفاده می‌کنند. به عنوان مثال کد زیر:

1myMovies[“gotg1”]

می‌تواند برای اشاره به مقدار “Gaurdians of the Galaxy Part 1” استفاده شود. ما از زیرنویس روی کلید برای دریافت مقدار آن استفاده کرده‌ایم. بنابراین چنان که گفتیم هیچ چیز جدیدی به جز یک نام تازه وجود ندارد.

زیرنویس‌ها زمانی که در ماتریس استفاده می‌شوند به مفهومی جذاب تبدیل می‌شوند. به مثال زیر توجه کنید:

1var matrix = [[1, 2],[1, 1]
2              
3// or
4              
5var legibleMatrix = 
6[
7 [1,2],
8 [1,1]
9]

بحث ارجاع دهی این ماتریس چگونه خواهد بود؟ ساختار آن به صورت زیر است:

1var myValue = matrix[0][1]   // my value is equal to 2, first row,
2                             // second column

اگر بخواهیم به همه مقادیر دسترسی داشته باشیم، می‌توانیم تعداد حلقه‌ها را دو برابر کنیم:

1for row in matrix {
2    for column in matrix {
3        print(matrix[row][column)
4    }
5}

بنابراین، استفاده از زیرنویس روشی کاملاً سرراست است و کافی است از اندیس هر مقداری که می‌خواهیم در ماتریس پیدا کنیم استفاده کنیم. در واقع بهتر است آن‌ها را به صورت [[[value]]] در نظر بگیریم و سپس از بیرون به سمت داخل حرکت کنیم تا مقداری را که به دنبالش هستیم بیابیم.

متأسفانه نکته یا ترفند خاصی برای کار کردن با ماتریس‌ها وجود ندارد و این کار صرفاً به تمرین و تجربه زیاد نیاز دارد.

سخن پایانی

در این نوشته با مطالب مفید زیادی آشنا شدیم. با استفاده از تعریف پروتکل‌ها به افزایش تعریف‌پذیری و به لطف اکستنشن‌ها به بهبود خوانایی کد کمک کردیم و با بهره‌گیری از هردوی آن‌ها قابلیت استفاده مجدد کد ارتقا می‌یابد. سپس چند مثال از زیرنویس‌ها ارائه کردیم که منتهی به معرفی ماتریس‌ها شد.

بنابراین در کدهای خود ابتدا از پروتکل‌ها آغاز کنید و سپس از اکستنشن‌ها به همراه پروتکل‌ها استفاده کنید تا مقدار کدی که باید بنویسید را کاهش دهید. از اکستنشن‌ها برای کمک به افراز کلاس‌های بزرگ به دو فایل می‌توان استفاده کرد. اگر همه مطالبی که در این نوشته خواندید را فراموش کردید، دست‌کم مطالبی که در این پاراگراف بیان کردیم را به خاطر داشته باشید.

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

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

اما باید بدانید که راهنماهایی که در زمینه پیاده‌سازی مفاهیم ارائه می‌شوند، به مسیری طولانی نیاز دارند و با ذهنیت آموزشی فاصله زیادی دارند. مطالعه این راهنماها باعث می‌شوند شما ذهنیت یک توسعه‌دهنده را بیابید یعنی متوجه شوید که شما هرگز به آن اندازه که می‌خواهید خوب نخواهید بود.

درک این نکته در همین مراحل ابتدایی بسیار ضروری است، به خصوص چون شما اینک آمادگی ساخت اپلیکیشن‌ها برای خودتان یا هر فرد دیگر را یافته‌اید، درک این ذهنیت موجب می‌شود که همواره انگیزه یادگیری موارد بیشتر را در خود حفظ کنید.

برای مطالعه قسمت بعدی این مجموعه مطلب آموزشی روی لینک زیر کلیک کنید:

اگر این نوشته برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۰ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
swift2go
نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *