شروع برنامه نویسی با زبان دارت Dart - بخش دوم

قسمت اول این آموزش را میتوانید در شروع برنامه نویسی با زبان دارت Dart - قسمت اول مشاهده نمایید.در بخش اول این آموزش، با مقدمات زبان دارت یا همان Dart، متغیرها، انواع داده، عملگرهای اصلی و کنترل جریان در این زبان آشنا شدیم. همانطور که مشخص شده است، این زبان شباهت بسیار زیادی به زبان هایی چون c++، جاوا اسکریپت و ... دارد و اگر شخصی با زبان های مشابه کار کرده باشد مشکلی در روند یادگیری دارت نخواهد داشت.اکنون میخواهیم به توابع، کنترل خطا و کلاس ها بپردازیم.توابع Functions در برنامه نویسی میتوانیم از توابع استفاده کنیم تا هم به ساختار کد نظم ببخشند و هم اینکه از تکرار کد جلوگیری کنیم. هنگامی که یک تابع تعریف میشود ما میتوانیم بدون توجه به ساختار داخلی آن بارها از آن استفاده کنیم.شبه کد یک تابع در زبان دارت به شکل زیر است:returnType functionName(inputs){ ... return ... }اگر تابعی خروجی نداشته باشد returnType برابر void یا خالی بوده و خط return ... حذف میگردد. برای مثال در کد زیر تابع main هیچ ورودی و خروجی ای ندارد ولی تابع sum سه عدد گرفته و مجموع آنها را برمیگرداند. int sum(int x, int y, int z){ return x+y+z; }void main() { print(sum(1,2,3)); } پارامترهای اختیاری و مقادیر پیشفرض میرسیم به یکی از نقاط قوت زبان دارت به نام optional parameters and defaults. در ورودی توابع میتوانیم پارامترهایی را بصورت اختیاری تعریف کنیم. به این مفهوم که این پارامتر میتواند به تابع ارسال شود یا نشود، و اگر ارسال نشود مقدار پیشفرضی که در نظر گرفتیم استفاده خواهد شد. مثال زیر را ببینیم: // named parameters int sumNamed(int x, int y, {int z=1, int a=2}){ return x + y + z + a; } // positional parameters int sumPostitional(int x, int y, [int z=1, int a=2]){ return x + y + z + a; } void main() { // named parameters usage print(sumNamed(2, 3)); // 8 print(sumNamed(2, 3, z:9)); // 16 print(sumNamed(2, 3, a:10)); // 16 print(sumNamed(2, 3, a:4, z:5)); // 14 // positional parameters usage print(sumPostitional(2,3)); // 8 print(sumPostitional(2,3,5)); // 12 print(sumPostitional(2,3,5,7)); // 17 } در مثال فوق، پارامترهای مصطلح به named parameters داخل { } قرار میگیرند، و مفهومشان این است که هنگام صدا زدن تابع، میتوانند مقداری برایشان در نظر گرفته نشود(که در این صورت مقدار پیشفرض در تعریف بدنه ی تابع درنظر گرفته میشود)، ولی برای مقداردهی باید نام پارامتر به همراه مقدارش ذکر شود.همچنین پارامترهای مصطلح به positional parameters داخل [ ] قرار میگیرند، و مفهومشان این است که هنگام صدار زند تابع، میتوانند مقداری برایشان در نظر گرفته نشود (که در این صورت مقدار پیشفرض در تعریف بدنه ی تابع درنظر گرفته میشود)، ولی برای مقداردهی، با توجه به ترتیبِ قرار گیری، مقادیر برای پارامترها در نظر گرفته میشوند.توابع به عنوان پارامتراز دیگر ویژگی های مثبت زبان دارت Functions as parameter میباشد. به این مفهوم که استفاده از تابع میتواند به عنوان پارامتر در نظر گرفته بشود. در مثال زیر این مفهوم را بررسی میکنیم:void printElement(int element){ print('\n'); print(element); } main(){ var list = [1,2,3]; print(list); // به ازای هر کدام از مقادیر موجود در لیست، تابع پرینت صدا زده میشود list.forEach(printElement); var printEl = printElement; // پارامتر فوق حکم تابع را دارد و هربار استفاده شود به معنی صدا زده شدن تابع میباشد list.forEach(printEl); }توابع بدون نیاز به تعریفگاهی اوقات نیاز است که بدنه ی تابعی بدون نیاز به تعریف تابع، یا به عنوان ورودی، یا به هرصورتی دیگری نیاز به استفاده داشته باشد. در دارت به این مفهوم را با مثال زیر بررسی میکنیم: main(){ var list = ['a', 'bb', 'ccc']; // anomumouse function list.forEach((item){ // به ازای تمامی مقادیر روی لیست این بدنه اجرا خواهد شد print('${list.indexOf(item)}: $item'); print('\n'); }); // اگر نیاز به بدنه و کروشه نداشته باشیم => // fat arrow version list.forEach((item) => print('${list.indexOf(item)}: $item')); } مفهوم Lexical closuresیکی دیگر از نقاط جذاب زبان دارت. اجازه دهید این مفهوم را با مثال زیر متوجه شویم:Function makeAdder(num addBy){ return (num i) => addBy + i; } main(){ var add2 = makeAdder(2); print(add2(1)); // 3 print(makeAdder(4)(1)); // 5 } در خروجی تابع makeAdder خط زیر را میبینیم:return (num i) => addBy + i;این خط به این مفهوم است که وقتی این تابع میخواهد استفاده شود، باید ابتدا یک ورودی تحت عنوان addBy میگیرد، سپس هرگاه ورودی دیگری به نام i گرفت، عملیات جمع addBy + i را انجام داده و حاصل را برمیگرداند. به عبارت دیگر، زمانی که خطر زیر صدا زده میشود:var add2 = makeAdder(2);پارامتر add2 در حقیقت تابعی است که میخواهد مقداری گرفته و با 2 جمع کند. بنابراین زمانی که (1)add2 صدا زده میشود، ورودی جدید (که عدد 1 میباشد) با عدد 2 (که قبلاً به تابع makeAdder داده شده است) جمع شده و خروجی 3 مشاهده میگردد.از طرف دیگر هنگام صدا زدن (1)(4)makeAdder ، قسمت (4)makeAdder تابعی میسازد که آماده است هر ورودی ای گرفته و با عدد 4 جمع کند. و با مشاهده ی (1) عدد 1 را با 4 جمع کرده و خروجی 5 را میبینیم.اگر این مفهوم برایتان کمی دشوار است، با تمرین و پیاده سازی چند مثال ساده، تا حدود زیادی قابل فهم تر خواهد شد.توابع بدون خروجیاگر تابعی داریم که صرفاً عملیاتی را انجام داده و خروجی ای ندارد، هنگام تعریف نوعی برای خروجی مشخص نمیکنیم: foo(){} main(){ print(foo()); // null } استثناها Exceptions زبان دارت قابلیت ایجاد و کنترل کردن Exceptionها را دارد.واژه throwایجاد یک استثناint sum(int x, int y){ if(x==null || y==null){ throw new FormatException('argument is null'); } return x+y; } main(){ print(sum(2,3)); print(sum(null,3)); //Uncaught exception: //FormatException: argument is null } بدنه try-catch کنترل استثنا. به کمک بدنه ی {...}try و catch میتوان استثناها را کنترل کرد (اگر کنترل نشود برنامه به اصطلاح کِرَش خواهد کرد).int sum(int x, int y){ if(x==null || y==null){ throw new FormatException('argument is null'); } return x+y; } main(){ try{ sum(2,null); } on FormatException catch(e, s){ print(e); print(s); } } همچنین میتوان برای Exceptionهای مختلف بدنه ی catchهای مختص خودش را پیاده کرد.واژه Finally چه Exceptionی رخ دهد و چه ندهد، به عنوان آخرین قدم Finally اجرا میشود:try{ ... } finally{ // code that should always execute; irrespective of the exception } کلاس ها در دارت Classکلاس ها در دارت با واژه ی class شناخته میشوند. class className{ .... }مثال زیر را ببینیم: // class definition تعریف کلاس class Point{ num x, y, z=0; }در مثال فوق، با واژه ی class به زبان دارت میگوییم که یک کلاس تعریف میکنیم. پس از آن نام کلاس را گفته و بدنه ی کلاس را پیاده سازی میکینم. در مثال فوق (میخواهیم مختصات یک نقطه را پیاده سازی کنیم)، کلاس ما نامش Point بوده و دارای سه عضو به نام های x و y و z میباشد. هر هر شیء ای از این کلاس تعریف شود دارای این سه عضو است.مثالی از نحوه ی تعریف یک شیء از کلاس میبینیم:main(){ // initialize and usage var point = new Point(); print(point.x); // null print(point.z); // 0 } در زبان دارت به کمک واژه ی new میتوان یک شیء جدید از کلاس ایجاد کرد. خط زیر یک نقطه ی جدید به نام point از کلاس Point ایجاد میکند.var point = new Point();توابع سازنده Constructorsهمانطور که در مثال قبل دیدیم، point.x خروجی null برمیگرداند که منطقی هم هست (زیرا مقداردهی نشده است). توابع سازنده به این نیست استفاده میشوند که هنگام تعریف یک شیء از یک کلاس، مواردی که کلاس نیاز دارد را بگیرد. ساختار زیر یک تابع سازنده برای کلاس Point است که مقادیر x و y را موقع تعریف شیء میگیرد.class Point{ num x, y, z=0; Point(num x, num y){ this.x = x; this.y = y; } }یک کلاس میتواند هیچ، یک یا چند تابع سازنده داشته باشد. اما یکی از نقاط جالب توجه در زبان دارت این است که تابع سازنده ی فوق، میتواند تنها در یک خط و به صورت زیر نوشته شود (که بصورت مختصر بوده و تعداد خطوط کد را کاهش داده و آن را بسیار ساده تر میکند):Point(this.x, this.y); توابع سازنده پیشفرضیکی دیگر از ویژگی های زیان دارت این است که میتوانید توابع سازنده ی داخلی برای کلاس پیاده سازی کنید. برای مثال فرض کنید میخواهد هر زمان که خواستید به نقطه ی مبدأ مختصاب بروید. برای این کار کافیست تابع زیر را به کلاس خود اضافه کنید:Point.origin(){ this.x=0; this.y=0; this.z=0; } یا اگر بخواهید بروید روی محور z ها کافیست تابع زیر را به کلاس خود اضافه کنید (مقدار z تغییر نکرده و مقادیر x و y صفر میشوند):Point.originZ(){ this.x=0; this.y=0; } یا اگه بخواهید بروید به نقطه ی خاصی روی محور zها، تابع زیر را به کلاس خود اضافه کنید:Point.originZ(num z){ this.x=0; this.y=0; this.z = z; } کلاسی نسبتاً کامل با موارد فوق بصورت زیر پیاشده میشود:// class definition class Point{ num x, y, z=0; // constructor definition Point(this.x, this.y); Point.origin(){ this.x=0; this.y=0; } Point.originZ(num z){ this.x=0; this.y=0; this.z = z; } } برای استفاده از کلاس فوق کافیست به شکل زیر عمل کنید (new Point شیء جدیدی از کلاس ایجاد میکند):main(){ print(new Point.origin().x); // 0 print(new Point.originZ(10).z); // 10 } توابع در کلاسبا توابع که آشنا شدیم. بری آشنایی با نحوه ی استفاده از توابع در کلاس ها، تابع محاسبه ی فاصله بین دو یک نقطه تا نقطه ی فعلی را به کلاس Point اضافه میکنیم: import 'dart:math'; // برای استفاده از توابه ریاضیات در دارت باید کتابخانه اش را اضافه کنیم class Point{ num x, y, z=0; Point(this.x, this.y); // این تابع فاصله میان نقطه ی فعلی تا نقطه ای که دریافت میکند را برمیگرداند num distance(Point other){ var dx = this.x - other.x; var dy = this.y - other.y; return sqrt(dx*dx+dy*dy); // sqrt از زیرمجموعه توابع کتابخانه ی ریاضیات دارت میباشد } } main(){ var point1 = new Point(1,2); var point2 = new Point(4,3); print(point1.distance(point2)); // 3.1622776601683795 } خط زیر را ببینیم:point1.distance(point2)این قسمت، به تابع point1 میگوید که تابع distance خود را صدا بزن و به عنوان ورودی point2 را در نظر بگیر. اگر قبلا تجربه ی برنامه نویسی شیء گرا و کلاس ها را دارید که با این مفهوم مشکلی نخواهید داشت، اما اگر تجربه ی زیادی در این زمینه ندارید حتما با پیاده سازی مثال های متخلف این مفهوم ها را بررسی کنید و اجازه بدهید به خوبی برایتان جا بیفتد.کلاس های انتزاعی Abstract classes این کلاس ها، کلاس هایی هستند که توابعی درونشان وجود دارد که پیاده سازی نشده است. به عبارتی توابعشان باید بعداً (مثلاً هنگامی که بقیه کلاس ها از آنها ارثبری میکنند) پیاده سازی گردند. برخی زبان های اجازه ی تعریف مستقیم یک شیء از یک کلاس انتزاعی را نمیدهند ولی دارت این اجازه را میدهد. کلاس های انتزاعی با واژه کلیدی abstract تعریف میشوند:abstract class AbstractClass{ void updateChildren(); } main(){ var x = new AbstractClass(); } منتشر شده در ویرگول توسط محمد قدسیان https://virgool.io/@mohammad.ghodsianhttps://virgool.io/@mohammad.ghodsian/dart2-fvwjqjiyywie