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


قسمت اول این آموزش را میتوانید در شروع برنامه نویسی با زبان دارت Dart – قسمت اول مشاهده نمایید.


برای شما

در بخش اول این آموزش، با مقدمات زبان دارت یا همان Dart، متغیرها، انواع داده، عملگرهای اصلی و کنترل جریان در این زبان آشنا شدیم. همانطور که مشخص شده است، این زبان شباهت بسیار زیادی به زبان هایی چون c++، جاوا اسکریپت و … دارد و اگر شخصی با زبان های مشابه کار کرده باشد مشکلی در روند یادگیری دارت نخواهد داشت.

اکنون میخواهیم به توابع، کنترل خطا و کلاس ها بپردازیم.


توابع Functions

در برنامه نویسی میتوانیم از توابع استفاده کنیم تا هم به ساختار کد نظم ببخشند و هم اینکه از تکرار کد جلوگیری کنیم. هنگامی که یک تابع تعریف میشود ما میتوانیم بدون توجه به ساختار داخلی آن بارها از آن استفاده کنیم.

شبه کد یک تابع در زبان دارت به شکل زیر است:

1234returnType functionName(inputs){
    ...
    return ...
}

اگر تابعی خروجی نداشته باشد returnType برابر void یا خالی بوده و خط return … حذف میگردد. برای مثال در کد زیر تابع main هیچ ورودی و خروجی ای ندارد ولی تابع sum سه عدد گرفته و مجموع آنها را برمیگرداند.

123 int sum(int x, int y, int z){
  return x+y+z;
}
123void main() {
  print(sum(1,2,3));
} 

پارامترهای اختیاری و مقادیر پیشفرض

میرسیم به یکی از نقاط قوت زبان دارت به نام optional parameters and defaults. در ورودی توابع میتوانیم پارامترهایی را بصورت اختیاری تعریف کنیم. به این مفهوم که این پارامتر میتواند به تابع ارسال شود یا نشود، و اگر ارسال نشود مقدار پیشفرضی که در نظر گرفتیم استفاده خواهد شد. مثال زیر را ببینیم:

1234567891011121314151617181920212223 // 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 میباشد. به این مفهوم که استفاده از تابع میتواند به عنوان پارامتر در نظر گرفته بشود. در مثال زیر این مفهوم را بررسی میکنیم:

12345678910111213141516void printElement(int element){
    print('\n');
    print(element);
}

main(){
    var list = [1,2,3];
    print(list);
 
    // به ازای هر کدام از مقادیر موجود در لیست، تابع پرینت صدا زده میشود
    list.forEach(printElement);
 
    var printEl = printElement;
    // پارامتر فوق حکم تابع را دارد و هربار استفاده شود به معنی صدا زده شدن تابع میباشد
    list.forEach(printEl);
}

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

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

1234567891011121314 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

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

123456789Function 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 را میبینیم.

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

توابع بدون خروجی

اگر تابعی داریم که صرفاً عملیاتی را انجام داده و خروجی ای ندارد، هنگام تعریف نوعی برای خروجی مشخص نمیکنیم:

12345 foo(){}
 
main(){
    print(foo()); // null
} 

استثناها Exceptions

زبان دارت قابلیت ایجاد و کنترل کردن Exceptionها را دارد.

واژه throw

ایجاد یک استثنا

12345678910111213int 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 میتوان استثناها را کنترل کرد (اگر کنترل نشود برنامه به اصطلاح کِرَش خواهد کرد).

123456789101112131415int 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 اجرا میشود:

12345try{
    ...
} finally{
    // code that should always execute; irrespective of the exception 
} 

کلاس ها در دارت Class

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

123class className{
        ....
}

مثال زیر را ببینیم:

1234 // class definition تعریف کلاس
class Point{
    num x, y, z=0;
}

در مثال فوق، با واژه ی class به زبان دارت میگوییم که یک کلاس تعریف میکنیم. پس از آن نام کلاس را گفته و بدنه ی کلاس را پیاده سازی میکینم. در مثال فوق (میخواهیم مختصات یک نقطه را پیاده سازی کنیم)، کلاس ما نامش Point بوده و دارای سه عضو به نام های x و y و z میباشد. هر هر شیء ای از این کلاس تعریف شود دارای این سه عضو است.

مثالی از نحوه ی تعریف یک شیء از کلاس میبینیم:

123456main(){
    // 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 را موقع تعریف شیء میگیرد.

1234567class Point{
    num x, y, z=0; 
    Point(num x, num y){
        this.x = x;
        this.y = y;
    }
}

یک کلاس میتواند هیچ، یک یا چند تابع سازنده داشته باشد. اما یکی از نقاط جالب توجه در زبان دارت این است که تابع سازنده ی فوق، میتواند تنها در یک خط و به صورت زیر نوشته شود (که بصورت مختصر بوده و تعداد خطوط کد را کاهش داده و آن را بسیار ساده تر میکند):

1Point(this.x, this.y); 

توابع سازنده پیشفرض

یکی دیگر از ویژگی های زیان دارت این است که میتوانید توابع سازنده ی داخلی برای کلاس پیاده سازی کنید. برای مثال فرض کنید میخواهد هر زمان که خواستید به نقطه ی مبدأ مختصاب بروید. برای این کار کافیست تابع زیر را به کلاس خود اضافه کنید:

12345Point.origin(){
    this.x=0;
    this.y=0;
    this.z=0;
} 

یا اگر بخواهید بروید روی محور z ها کافیست تابع زیر را به کلاس خود اضافه کنید (مقدار z تغییر نکرده و مقادیر x و y صفر میشوند):

1234Point.originZ(){
    this.x=0;
    this.y=0;
} 

یا اگه بخواهید بروید به نقطه ی خاصی روی محور zها، تابع زیر را به کلاس خود اضافه کنید:

12345Point.originZ(num z){
    this.x=0;
    this.y=0;
    this.z = z;
} 

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

123456789101112131415161718// 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 شیء جدیدی از کلاس ایجاد میکند):

1234main(){
    print(new Point.origin().x); // 0
    print(new Point.originZ(10).z); // 10
} 

توابع در کلاس

با توابع که آشنا شدیم. بری آشنایی با نحوه ی استفاده از توابع در کلاس ها، تابع محاسبه ی فاصله بین دو یک نقطه تا نقطه ی فعلی را به کلاس Point اضافه میکنیم:

1234567891011121314151617181920 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 تعریف میشوند:

1234567abstract class AbstractClass{
    void updateChildren();
}

main(){
    var x = new AbstractClass();
} 


منتشر شده در ویرگول توسط محمد قدسیان https://virgool.io/@mohammad.ghodsian

https://virgool.io/@mohammad.ghodsian/dart2-fvwjqjiyywie


نویسنده مطلب: Mohammad Ghodsian

منبع مطلب

به فکر سرمایه‌گذاری هستی؟

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

ثبت نام و دریافت جایزه
ممکن است شما بپسندید
نظر شما درباره این مطلب

آدرس ایمیل شما منتشر نخواهد شد.