اشتراکها
اشتراکها
پروژه Regulex
اشتراکها
معرفی math.js
در قسمت قبل، مقدماتی از Pipeها را مورد برسی قرار دادیم؛ از جمله کاربرد Pipeها، نحوه استفاده از آنها، معرفی یکسری Pipe از پیش ساخته شده Angular، نحوه ارسال پارامتر به آنها و همچنین نحوه استفاده از آنها را در داخل Typescript، فراگرفتیم. در این قسمت نحوه ساخت Pipeهای سفارشی و همچنین نکات تکمیلی در مورد آنها را مورد بحث و بررسی قرار میدهیم.
نحوه ساخت Pipe سفارشی
علاوه بر استفاده از Pipeهای از پیش ساخته شده Angular، شما میتوانید Pipeهای سفارشی خود را نیز تعریف و استفاده کنید. به عنوان مثال میخواهیم Pipe ای را با نام perNumber تعریف کنیم، تا تمامی اعداد موجود در عبارت ورودی Pipe را به صورت اعداد فارسی نمایش دهد. یعنی با اعمال این Pipe به عدد 123456 خروجی ۱۲۳۴۵۶ مورد انتظار است. برای ایجاد Pipe سفارشی مراحل زیر را انجام دهید.
قدم اول - ساخت یک فایل با نام دلخواه
طبق Style Guide در Angular.io نام این فایل را per-number.pipe.ts انتخاب میکنیم.
قدم دوم – افزودن ماژولهای مورد نیاز
داخل فایل ایجاد شده ماژولهای Pipe و PipeTransform را با استفاده از دستور import از angular/core@ اضافه میکنیم.
قدم سوم – ساخت کلاس و مزین کردن آن به Pipe@
یک کلاس با نام دلخواه را مثلا به نام PerNumberPipe ایجاد میکنیم. این کلاس علاوه بر اینکه PipeTransform را پیاده سازی خواهد کرد، مزین به متادیتای Pipe@ نیز میباشد. متادیتای Pipe@ هنگام تزئین کلاس، یک نام را دریافت میکند. این نام قرار است به عنوان نام نهایی Pipe برای اعمال بر روی Template expressions مورد استفاده قرار بگیرد.
قدم چهارم – پیاده سازی متد transform
به واسطه اعمال اینترفیس PipeTransform، این کلاس باید متد transform را پیاده سازی کند. این متد در پارامتر اول خود، عبارت ورودی را که قرار است Pipe بر روی آن اعمال شود، دریافت میکند و در ادامه تعداد دلخواهی پارامتر ورودی Pipe را که میخواهد، میتواند دریافت کند.
نکته ۱: نام انتخابی برای Pipe در آذینگر Pipe@ بایستی یک شناسه معتبر در JavaScript باشد.
نکته ۲: متد transform برای Pipe اجباری است و حتما بایستی پیاده سازی شود. اینترفیس PipeTransform این متد را برای کلاس اجباری میکند؛ هرچند استفاده از این اینترفیس برای کلاس Pipe کاملا اختیاری است.
قدم آخر – نوشتن کد تبدیل اعداد
Pipe مورد نظر ما قرار است یک رشته عددی را از ورودی دریافت کند و تمامی اعداد لاتین آن را به فارسی تبدیل کند. همچنین این Pipe هیچگونه پارامتری را دریافت نمیکند. کد زیر نحوه پیاده سازی متد transform را نمایش میدهد.
نحوه معرفی Pipe سفارشی به برنامه
حالا جهت استفاده از Pipe سفارشی در کامپوننتهای خود کافی است آنرا یکبار در قسمت declarations در AppModule خود تعریف کنید.
نحوه استفاده از Pipeهای سفارشی
نحوه استفاده از Pipeهای سفارشی، دقیقا مشابه Pipeهای از قبل ساخته شده Angular میباشد.
Pipeها و تشخیص تغییرات
Angular برای اعمال Pipe بر روی Template expressions بایستی تمامی رخدادهای برنامه را تحت نظر قرار داده و با مشاهده هر تغییری بر روی عبارت ورودی Pipe، فراخوانی Pipe را آغاز کند. از جمله این رخدادها میتوان به رخداد mouse move، timer tick، server response و فشرده شدن کلیدهای ماوس و یا کیبورد اشاره کرد. واضح است که بررسی تغییرات عبارت در این همه رخداد میتواند مخرب باشد و بر روی کارآئی (Performance) تاثیر منفی خواهد گذاشت. اما Angular برای حل این مشکل و همچنین هنگام مشاهده سریع تغییرات هنگام استفاده از Pipeها، الگوریتمهای سریع و سادهای در نظر گرفته است.
در قسمت بعد با انواع Pipeها در Angular و همچنین نحوه پیاده سازی آنها، آشنا خواهیم شد.
نحوه ساخت Pipe سفارشی
علاوه بر استفاده از Pipeهای از پیش ساخته شده Angular، شما میتوانید Pipeهای سفارشی خود را نیز تعریف و استفاده کنید. به عنوان مثال میخواهیم Pipe ای را با نام perNumber تعریف کنیم، تا تمامی اعداد موجود در عبارت ورودی Pipe را به صورت اعداد فارسی نمایش دهد. یعنی با اعمال این Pipe به عدد 123456 خروجی ۱۲۳۴۵۶ مورد انتظار است. برای ایجاد Pipe سفارشی مراحل زیر را انجام دهید.
قدم اول - ساخت یک فایل با نام دلخواه
طبق Style Guide در Angular.io نام این فایل را per-number.pipe.ts انتخاب میکنیم.
قدم دوم – افزودن ماژولهای مورد نیاز
داخل فایل ایجاد شده ماژولهای Pipe و PipeTransform را با استفاده از دستور import از angular/core@ اضافه میکنیم.
import { Pipe, PipeTransform } from '@angular/core';
قدم سوم – ساخت کلاس و مزین کردن آن به Pipe@
یک کلاس با نام دلخواه را مثلا به نام PerNumberPipe ایجاد میکنیم. این کلاس علاوه بر اینکه PipeTransform را پیاده سازی خواهد کرد، مزین به متادیتای Pipe@ نیز میباشد. متادیتای Pipe@ هنگام تزئین کلاس، یک نام را دریافت میکند. این نام قرار است به عنوان نام نهایی Pipe برای اعمال بر روی Template expressions مورد استفاده قرار بگیرد.
import { Pipe, PipeTransform } from '@angular/core'; @Pipe({name: 'perNumber'}) export class PerNumberPipe implements PipeTransform { }
قدم چهارم – پیاده سازی متد transform
به واسطه اعمال اینترفیس PipeTransform، این کلاس باید متد transform را پیاده سازی کند. این متد در پارامتر اول خود، عبارت ورودی را که قرار است Pipe بر روی آن اعمال شود، دریافت میکند و در ادامه تعداد دلخواهی پارامتر ورودی Pipe را که میخواهد، میتواند دریافت کند.
import { Pipe, PipeTransform } from '@angular/core'; @Pipe({name: 'perNumber'}) export class PerNumberPipe implements PipeTransform { transform(value: any, ...args: any[]): any { } }
نکته ۱: نام انتخابی برای Pipe در آذینگر Pipe@ بایستی یک شناسه معتبر در JavaScript باشد.
نکته ۲: متد transform برای Pipe اجباری است و حتما بایستی پیاده سازی شود. اینترفیس PipeTransform این متد را برای کلاس اجباری میکند؛ هرچند استفاده از این اینترفیس برای کلاس Pipe کاملا اختیاری است.
قدم آخر – نوشتن کد تبدیل اعداد
Pipe مورد نظر ما قرار است یک رشته عددی را از ورودی دریافت کند و تمامی اعداد لاتین آن را به فارسی تبدیل کند. همچنین این Pipe هیچگونه پارامتری را دریافت نمیکند. کد زیر نحوه پیاده سازی متد transform را نمایش میدهد.
import { Pipe, PipeTransform } from '@angular/core'; @Pipe({name: 'perNumber'}) export class PerNumberPipe implements PipeTransform { transform(input: string): string{ if (input == undefined) return ''; var str = input.toString().trim(); if (str === "") return ""; str = str.replace(/0/g, '۰'); str = str.replace(/1/g, '۱'); str = str.replace(/2/g, '۲'); str = str.replace(/3/g, '۳'); str = str.replace(/4/g, '۴'); str = str.replace(/5/g, '۵'); str = str.replace(/6/g, '۶'); str = str.replace(/7/g, '۷'); str = str.replace(/8/g, '۸'); str = str.replace(/9/g, '۹'); return str; } }
نحوه معرفی Pipe سفارشی به برنامه
حالا جهت استفاده از Pipe سفارشی در کامپوننتهای خود کافی است آنرا یکبار در قسمت declarations در AppModule خود تعریف کنید.
import { PerNumberPipe } from './pipes/per-number.pipe.ts' ... declarations: [PerNumberPipe]
نحوه استفاده از Pipeهای سفارشی
نحوه استفاده از Pipeهای سفارشی، دقیقا مشابه Pipeهای از قبل ساخته شده Angular میباشد.
<h3>{{'12345679' | perNumber}}</h3>
یکسری از Pipeهای مربوط به زبان فارسی در گیتهاب بنده پیاده سازی شده است که نیازمند همکاری دوستان است. لطفا جهت همکاری برای ساخت یک ابزار جامع برای زبان فارسی در Angular به این لینک مراجعه کنید.
Pipeها و تشخیص تغییرات
Angular برای اعمال Pipe بر روی Template expressions بایستی تمامی رخدادهای برنامه را تحت نظر قرار داده و با مشاهده هر تغییری بر روی عبارت ورودی Pipe، فراخوانی Pipe را آغاز کند. از جمله این رخدادها میتوان به رخداد mouse move، timer tick، server response و فشرده شدن کلیدهای ماوس و یا کیبورد اشاره کرد. واضح است که بررسی تغییرات عبارت در این همه رخداد میتواند مخرب باشد و بر روی کارآئی (Performance) تاثیر منفی خواهد گذاشت. اما Angular برای حل این مشکل و همچنین هنگام مشاهده سریع تغییرات هنگام استفاده از Pipeها، الگوریتمهای سریع و سادهای در نظر گرفته است.
در قسمت بعد با انواع Pipeها در Angular و همچنین نحوه پیاده سازی آنها، آشنا خواهیم شد.
شکستن یک مسئله بزرگ به تعدادی مسئله کوچکتر راهکار موثری برای حل آن است. این امر در برنامه نویسی نیز که هدف آن چیزی جز حل یک مسئله نیست همواره مورد توجه بوده است. به همین دلیل روش هایی که به کمک آنها بتوان یک برنامه بزرگ را به قطعات کوچکتری تقسیم کرد تا هر قطعه کد مسئول انجام کار خاصی باشد پیشتر به زبانهای برنامه نویسی اضافه شده اند. یکی از این ساختارها تابع (Function) نام دارد. برنامه ای که از توابع برای تقسیم کدهای برنامه استفاده میکند یک برنامه ساختیافته میگوییم.
در مطلب پیشین به پیرامون خود نگاه کردیم و اشیاء گوناگونی را مشاهده کردیم که در حقیقت دنیای ما را تشکیل داده اند و فعالیتهای روزمره ما با استفاده از آنها صورت میگیرد. ایده ای به ذهنمان رسید. اشیاء و مفاهیم مرتبط به آن میتواند روش بهتر و موثرتری برای تقسیم کدهای برنامه باشد. مثلاً اگر کل کدهای برنامه که مسئول حل یکی از مسئلههای کوچک یاد شده است را یکجا بسته بندی کنیم و اصولی که از اشیاء واقعی پیرامون خود آموختیم را در مورد آن رعایت کنیم به برنامه بسیار با کیفیتتری از نظر خوانایی، راحتی در توسعه، اشکال زدایی سادهتر و بسیاری موارد دیگر خواهیم رسید.
توسعه دهندگان زبانهای برنامه نویسی که با ما در این مورد هم عقیده بوده اند دست به کار شده و دستورات و ساختارهای لازم برای پیاده کردن این ایده را در زبان برنامه نویسی قرار دادند و آن را زبان برنامه نویسی شیء گرا نامیدند. حتی جهت برخورداری از قابلیت استفاده مجدد از کد و موارد دیگر به جای آنکه کدها را در بسته هایی به عنوان یک شیء خاص قرار دهیم آنها را در بسته هایی به عنوان قالب یا نقشه ساخت اشیاء خاصی که در ذهن داریم قرار میدهیم. یعنی مفهوم کلاس یا رده که پیشتر اشاره شد. به این ترتیب یک بار مینویسیم و بارها استفاده میکنیم. مانند همان مثال بازیکن در بخش نخست. هر زمان که لازم باشد با استفاده از دستورات مربوطه از روی کدهای کلاس که نقشه یا قالب ساخت اشیاء هستند شیء مورد نظر را ساخته و در جهت حل مسئله مورد نظر به کار میبریم.
حال برای آنکه به طور عملی بتوانیم از ایده شیء گرایی در برنامه هایمان استفاده کنیم و مسائل بزرگ را حل کنیم لازم است ابتدا مقداری با جزییات و دستورات زبان در این مورد آشنا شویم.
تذکر: دقت کنید برای آنکه از ایده شیء گرایی در برنامهها حداکثر استفاده را ببریم مفاهیمی در مهندسی نرم افزار به آن اضافه شده است که ممکن است در دنیای واقعی نیازی به طرح آنها نباشد. پس لطفاً تلاش نکنید با دیدن هر مفهوم تازه بلافاصله سعی در تطبیق آن با محیط اطراف کنید. هر چند بسیاری از آنها به طور ضمنی در اشیاء پیرامون ما نیز وجود دارند.
زبان برنامه نویسی مورد استفاده برای بیان مفاهیم برنامه نویسی در این سری مقالات زبان سی شارپ است. اما درک برنامههای نوشته شده برای علاقه مندان به زبانهای دیگری مانند وی بی دات نت نیز دشوار نیست. چراکه اکثر دستورات مشابه است و تبدیل Syntax نیز به راحتی با اندکی جستجو میسر میباشد. لازم به یادآوری است زبان سی شارپ به بزرگی یا کوچکی حروف حساس است.
در این قطعه برنامه نکات زیر قابل توجه است:
برای ایجاد شیء از کلمه کلیدی new و به دنبال آن نام کلاسی که قصد داریم بر اساس آن یک شیء بسازیم استفاده میکنیم. همان طور که اشاره شد کلاس یک نوع را تعریف میکند. پس از آن میتوان همانند سایر انواع مانند int, string, … برای تعریف متغیر استفاده نمود. به مثال زیر توجه کنید.
در این مثال rectangle که با حرف کوچک شروع شده و میتوانست هر نام دلخواه دیگری باشد ارجاعی به شیء ساخته شده را به دست میدهد. وقتی نمونه ای از یک کلاس ایجاد میشود یک ارجاع به شیء تازه ساخته شده برای برنامه نویس برگشت داده میشود. در این مثال rectangle یک ارجاع به شیء تازه ساخته شده است یعنی به آن اشاره میکند اما خودش شامل دادههای آن شیء نیست. تصور کنید این ارجاع تنها دستگیره ای برای شیء ساخته شده است که دسترسی به آن را برای برنامه نویس میسر میکند. درک این مطلب از این جهت دارای اهمیت است که بدانید میشود یک دستگیره یا ارجاع دیگر بسازید بدون آنکه شیء جدیدی تولید کنید.
البته توصیه نمیکنم چنین ارجاعی را تعریف کنید چرا که به هیچ شیء خاصی اشاره نمیکند. و تلاش برای استفاده از آن منجر به بروز خطای معروفی در برنامه خواهد شد. به هر حال یک ارجاع میتوان ساخت چه با ایجاد یک شیء جدید و یا با نسبت دادن یک شیء موجود به آن.
در این کد دو ارجاع یا دستگیره ایجاد شده است که هر دو به یک شیء اشاره میکنند. بنابراین ما با استفاده از هر دو ارجاع میتوانیم به همان شیء واحد دسترسی پیدا کنیم و اگر مثلاً با rectangle1 در شیء مورد نظر تغییری بدهیم و سپس با rectangle2 شیء را مورد بررسی قرار دهیم تغییرات داده شده قابل مشاهده خواهد بود چون هر دو ارجاع به یک شیء اشاره میکنند. به همین دلیل کلاسها را به عنوان نوع ارجاعی میشناسیم در مقایسه با انواع داده دیگری که اصطلاحاً نوع مقداری هستند.
حالا میتوان شیء ساخته شده را با استفاده از ارجاعی که به آن داریم به کار برد.
ابتدا عرض و ارتفاع شیء چهارضلعی را مقدار دهی کرده و سپس مساحت را دریافت کرده ایم. از نقطه برای دسترسی به اعضای یک شیء استفاده میشود.
به متغیرهایی از هر نوع که مستقیماً درون کلاس تعریف شوند (و نه مثلاً داخل یک تابع درون کلاس) فیلد میگوییم. فیلدها از اعضای کلاس دربردارنده آنها محسوب میشوند.
تعریف فیلدها مستقیماً در بدنه کلاس با یک Access Modifier شروع میشود و به دنبال آن نوع فیلد و سپس نام دلخواه برای فیلد میآید.
تذکر: نامگذاری مناسب یکی از مهمترین اصولی است که یک برنامه نویس باید همواره به آن توجه کافی داشته باشد و به شدت در بالا رفتن کیفیت برنامه موثر است. به خاطر داشته باشید تنها اجرا شدن و کار کردن یک برنامه کافی نیست. رعایت بسیاری از اصول مهندسی نرم افزار که ممکن است نقش مستقیمی در کارکرد برنامه نداشته باشند موجب سهولت در نگهداری و توسعه برنامه شده و به همان اندازه کارکرد صحیح برنامه مهم هستند. بنابراین مجدداً شما را دعوت به خواندن مقاله یاد شده بالا در مورد اصول نامگذاری صحیح میکنم. هر مفهوم تازه ای که میآموزید میتوانید به اصول نامگذاری همان مورد در مقاله پیش گفته مراجعه نمایید. همچنین افزونه هایی برای Visual Studio وجود دارد که شما را در زمینه نامگذاری صحیح و بسیاری موارد دیگر هدایت میکنند که یکی از مهمترین آنها Resharper نام دارد.
مثال:
همان طور که در این قطعه کد به عنوان توضیح درج شده است استفاده از فیلدهایی با دسترسی عمومی توصیه نمیشود. علت آن واضح است. چون هیچ کنترلی برای مقداری که برای آن در نظر گرفته میشود نداریم. به عنوان مثال امکان دارد یک مقدار منفی برای عرض یا ارتفاع شیء درج شود حال آنکه میدانیم عرض یا ارتفاع منفی معنا ندارد. در قسمت بعدی این سری مقالات این مشکل را بررسی و حل خواهیم نمود.
فیلدها معمولاً با سطح دسترسی خصوصی و برای نگهداری از دادههایی که مورد نیاز بیش از یک متد (یا تابع) درون کلاس است و آن دادهها باید پس از خاتمه کار یک متد همچنان باقی بمانند استفاده میشود. بدیهی است در غیر اینصورت به جای تعریف فیلد میتوان از متغیرهای محلی (متغیری که درون خود تابع تعریف میشود) استفاده نمود.
همان طور که پیشتر اشاره شد برای دسترسی به یک فیلد ابتدا یک نقطه پس از نام شیء درج کرده و سپس نام فیلد مورد نظر را مینویسیم.
در هنگام تعریف یک فیلد در صورت نیاز میتوان برای آن یک مقدار اولیه را در نظر گرفت. مانند مثال زیر:
متدها نیز مانند فیلدها در داخل کلاس تعریف میشوند. ابتدا یک Access Modifier سطح دسترسی را تعیین مینماید. سپس به ترتیب نوع خروجی، نام متد و لیست پارامترهای آن در صورت وجود درج میشود. به مجموعه بخشهای یاد شده امضای متد میگویند.
پارامترهای یک متد داخل یک جفت پرانتز قرار میگیرند و با کاما (,) از هم جدا میشوند. یک جفت پرانتز خالی نشان دهنده آن است که متد نیاز به هیچ پارامتری ندارد.
بار دیگر به بخش تعریف متدهای کلاسی که ایجاد کردیم توجه نمایید.
در این کلاس دو متد به نامهای Area و Perimeter به ترتیب برای محاسبه مساحت و محیط چهارضلعی تعریف شده است. همانطور که پیشتر اشاره شد متدها برای پیاده سازی رفتار اشیاء یا همان کارکردهای آنها استفاده میشوند. در این مثال شیء ما قادر است مساحت و محیط خود را محاسبه نماید. چه شیء خوش رفتاری!
همچنین توجه نمایید این شیء برای محاسبه مساحت و محیط خود نگاهی به ویژگیهای خود یعنی عرض و ارتفاعش که در فیلدهای آن نگهداری میکنیم میاندازد.
فراخوانی متد یک شیء همانند دسترسی به فیلد آن است. ابتدا نام شیء سپس یک نقطه و به دنبال آن نام متد مورد نظر به همراه پرانترها. آرگومانهای مورد نیاز در صورت وجود داخل پرانتزها قرار میگیرند و با کاما از هم جدا میشوند. که البته در این مثال متد ما نیازی به آرگومان ندارد. به همین دلیل برای فراخوانی آن تنها یک جفت پرانتز خالی قرار میدهیم.
در این بخش به دو مفهوم پارامتر و آرگومان اشاره شد. تفاورت آنها چیست؟
در هنگام تعریف یک متد نام و نوع پارامترهای مورد نیاز را تعیین و درج مینماییم. حال وقتی قصد فراخوانی متد را داریم باید مقادیر واقعی که آرگومان نامیده میشود را برای هر یک از پارامترهای تعریف شده فراهم نماییم. نوع آرگومان باید با نوع پارامتر تعریف شده تطبیق داشته باشد. اما اگر یک متغیر را به عنوان آرگومان در هنگام فراخوانی متد استفاده میکنیم نیازی به یکسان بودن نام آن متغیر و نام پارامتر تعریف شده نیست.
متدها میتوانند یک مقدار را به کدی که آن متد را فراخوانی کرده است بازگشت دهند.
در این مثال مشاهده میکنید که پس از فراخوانی متد Perimeter مقدار بازگشتی آن در متغیری به نام p قرار گرفته است. اگر نوع خروجی یک متد که در هنگام تعریف آن پیش از نام متد قرار میگیرد void یا پوچ نباشد، متد میتواند مقدار مورد نظر را با استفاده از کلمه کلیدی return بازگشت دهد. کلمه return و به دنبال آن مقداری که از نظر نوع باید با نوع خروجی تعیین شده تطبیق داشته باشد، مقدار درج شده را به کد فراخوان متد بازگشت میدهد.
نکته: کلمه return علاوه بر بازگشت مقدار مورد نظر سبب پایان اجرای متد نیز میشود. حتی در صورتی که نوع خروجی یک متد void تعریف شده باشد استفاده از کلمه return بدون اینکه مقداری به دنبال آن بیاید میتواند برای پایان اجرای متد، در صورت نیاز و مثلاً برقراری شرطی خاص مفید باشد. بدون کلمه return متد زمانی پایان مییابد که به پایان قطعه کد بدنه خود برسد. توجه نمایید که در صورتی که نوع خروجی متد چیزی به جز void است استفاده از کلمه return به همراه مقدار مربوطه الزامی است.
مقدار خروجی یک متد را میتوان هر جایی که مقداری از همان نوع مناسب است مستقیماً به کار برد. همچنین میتوان آن را در یک متغیر قرار داد و سپس از آن استفاده نمود.
به عنوان مثال کلاس ساده زیر را در نظر بگیرید که متدی دارد برای جمع دو عدد.
و حال دو روش استفاده از این متد:
در روش اول مستقیماً خروجی متد مورد استفاده قرار گرفته است و در روش دوم ابتدا مقدار خروجی در یک متغیر قرار گرفته است و سپس از مقدار درون متغیر استفاده شده است. استفاده از متغیر برای نگهداری مقدار خروجی اجباری نبوده و تنها جهت بالا بردن خوانایی برنامه یا حفظ مقدار خروجی تابع برای استفادههای بعدی به کار میرود.
در بخشهای بعدی بحث ما در مورد سایر اعضای کلاس و برخی جزییات پیرامون اعضای پیش گفته خواهد بود.
در مطلب پیشین به پیرامون خود نگاه کردیم و اشیاء گوناگونی را مشاهده کردیم که در حقیقت دنیای ما را تشکیل داده اند و فعالیتهای روزمره ما با استفاده از آنها صورت میگیرد. ایده ای به ذهنمان رسید. اشیاء و مفاهیم مرتبط به آن میتواند روش بهتر و موثرتری برای تقسیم کدهای برنامه باشد. مثلاً اگر کل کدهای برنامه که مسئول حل یکی از مسئلههای کوچک یاد شده است را یکجا بسته بندی کنیم و اصولی که از اشیاء واقعی پیرامون خود آموختیم را در مورد آن رعایت کنیم به برنامه بسیار با کیفیتتری از نظر خوانایی، راحتی در توسعه، اشکال زدایی سادهتر و بسیاری موارد دیگر خواهیم رسید.
توسعه دهندگان زبانهای برنامه نویسی که با ما در این مورد هم عقیده بوده اند دست به کار شده و دستورات و ساختارهای لازم برای پیاده کردن این ایده را در زبان برنامه نویسی قرار دادند و آن را زبان برنامه نویسی شیء گرا نامیدند. حتی جهت برخورداری از قابلیت استفاده مجدد از کد و موارد دیگر به جای آنکه کدها را در بسته هایی به عنوان یک شیء خاص قرار دهیم آنها را در بسته هایی به عنوان قالب یا نقشه ساخت اشیاء خاصی که در ذهن داریم قرار میدهیم. یعنی مفهوم کلاس یا رده که پیشتر اشاره شد. به این ترتیب یک بار مینویسیم و بارها استفاده میکنیم. مانند همان مثال بازیکن در بخش نخست. هر زمان که لازم باشد با استفاده از دستورات مربوطه از روی کدهای کلاس که نقشه یا قالب ساخت اشیاء هستند شیء مورد نظر را ساخته و در جهت حل مسئله مورد نظر به کار میبریم.
حال برای آنکه به طور عملی بتوانیم از ایده شیء گرایی در برنامه هایمان استفاده کنیم و مسائل بزرگ را حل کنیم لازم است ابتدا مقداری با جزییات و دستورات زبان در این مورد آشنا شویم.
تذکر: دقت کنید برای آنکه از ایده شیء گرایی در برنامهها حداکثر استفاده را ببریم مفاهیمی در مهندسی نرم افزار به آن اضافه شده است که ممکن است در دنیای واقعی نیازی به طرح آنها نباشد. پس لطفاً تلاش نکنید با دیدن هر مفهوم تازه بلافاصله سعی در تطبیق آن با محیط اطراف کنید. هر چند بسیاری از آنها به طور ضمنی در اشیاء پیرامون ما نیز وجود دارند.
زبان برنامه نویسی مورد استفاده برای بیان مفاهیم برنامه نویسی در این سری مقالات زبان سی شارپ است. اما درک برنامههای نوشته شده برای علاقه مندان به زبانهای دیگری مانند وی بی دات نت نیز دشوار نیست. چراکه اکثر دستورات مشابه است و تبدیل Syntax نیز به راحتی با اندکی جستجو میسر میباشد. لازم به یادآوری است زبان سی شارپ به بزرگی یا کوچکی حروف حساس است.
تشخیص و تعریف کلاسهای برنامه
کار را با یک مثال شروع میکنیم. فرض کنید به عنوان بخشی از راه حل یک مسئله بزرگ، لازم است محیط و مساحت یک سری چهارضلعی را محاسبه کنیم و قصد داریم این وظیفه را به طور کامل بر عهده قطعه کدهای مستقلی در برنامه قرار دهیم. به عبارت دیگر قصد داریم متناظر با هر یک از چهارضلعیهای موجود در مسئله یک شیء در برنامه داشته باشیم که قادر است محیط و مساحت خود را محاسبه و ارائه نماید. کلاس زیر که با زبان سی شارپ نوشته شده امکان ایجاد اشیاء مورد نظر را فراهم میکند.public class Rectangle { public double Width; public double Height; public double Area() { return Width*Height; } public double Perimeter() { return 2*(Width + Height); } }
در این قطعه برنامه نکات زیر قابل توجه است:
- کلاس با کلمه کلیدی class تعریف میشود.
- همان طور که مشاهده میکنید تعریف کلاس با کلمه public آغاز شده است. این کلمه محدوده دسترسی به کلاس را تعیین میکند. در اینجا از کلمه public استفاده کردیم تا بخشهای دیگر برنامه امکان استفاده از این کلاس را داشته باشند.
- پس از کلمه کلیدی class نوبت به نام کلاس میرسد. اگرچه انتخاب نام مورد نظر امری اختیاری است اما در آینده حتماً اصول و قراردادهای نامگذاری در داتنت را مطالعه نمایید. در حال حاضر حداقل به خاطر داشته باشید تا انتخاب نامی مناسب که گویای کاربرد کلاس باشد بسیار مهم است.
- باقیمانده کد، بدنه کلاس را تشکیل میدهد. جاییکه ویژگی ها، رفتارها و ... یا به طور کلی اعضای کلاس تعریف میشوند.
ایجاد شیء از یک کلاس و نحوه دسترسی به شیء ایجاد شده
شیء و کلاس چیزهای متفاوتی هستند. یک کلاس نوع یک شیء را تعریف میکند. اما یک شیء یک موجودیت عینی و واقعی بر اساس یک کلاس است. در اصطلاح از شیء به عنوان یک نمونه (Instance) یا وهله ای از کلاس مربوطه یاد میکنیم. همچنین به عمل ساخت شیء نمونه سازی یا وهله سازی گوییم.برای ایجاد شیء از کلمه کلیدی new و به دنبال آن نام کلاسی که قصد داریم بر اساس آن یک شیء بسازیم استفاده میکنیم. همان طور که اشاره شد کلاس یک نوع را تعریف میکند. پس از آن میتوان همانند سایر انواع مانند int, string, … برای تعریف متغیر استفاده نمود. به مثال زیر توجه کنید.
Rectangle rectangle = new Rectangle();
Rectangle rectangle;
Rectangle rectangle1 = new Rectangle(); Rectangle rectangle2 = rectangle1;
حالا میتوان شیء ساخته شده را با استفاده از ارجاعی که به آن داریم به کار برد.
Rectangle rectangle = new Rectangle(); rectangle.Width = 10.5; rectangle.Height = 10; double a = rectangle.Area();
فیلدها
اگر به تعریف کلاس دقت کنید مشخص است که دو متغییر Width و Height را با سطح دسترسی عمومی تعریف کرده ایم.به متغیرهایی از هر نوع که مستقیماً درون کلاس تعریف شوند (و نه مثلاً داخل یک تابع درون کلاس) فیلد میگوییم. فیلدها از اعضای کلاس دربردارنده آنها محسوب میشوند.
تعریف فیلدها مستقیماً در بدنه کلاس با یک Access Modifier شروع میشود و به دنبال آن نوع فیلد و سپس نام دلخواه برای فیلد میآید.
تذکر: نامگذاری مناسب یکی از مهمترین اصولی است که یک برنامه نویس باید همواره به آن توجه کافی داشته باشد و به شدت در بالا رفتن کیفیت برنامه موثر است. به خاطر داشته باشید تنها اجرا شدن و کار کردن یک برنامه کافی نیست. رعایت بسیاری از اصول مهندسی نرم افزار که ممکن است نقش مستقیمی در کارکرد برنامه نداشته باشند موجب سهولت در نگهداری و توسعه برنامه شده و به همان اندازه کارکرد صحیح برنامه مهم هستند. بنابراین مجدداً شما را دعوت به خواندن مقاله یاد شده بالا در مورد اصول نامگذاری صحیح میکنم. هر مفهوم تازه ای که میآموزید میتوانید به اصول نامگذاری همان مورد در مقاله پیش گفته مراجعه نمایید. همچنین افزونه هایی برای Visual Studio وجود دارد که شما را در زمینه نامگذاری صحیح و بسیاری موارد دیگر هدایت میکنند که یکی از مهمترین آنها Resharper نام دارد.
مثال:
// public field (Generally not recommended.) public double Width;
فیلدها معمولاً با سطح دسترسی خصوصی و برای نگهداری از دادههایی که مورد نیاز بیش از یک متد (یا تابع) درون کلاس است و آن دادهها باید پس از خاتمه کار یک متد همچنان باقی بمانند استفاده میشود. بدیهی است در غیر اینصورت به جای تعریف فیلد میتوان از متغیرهای محلی (متغیری که درون خود تابع تعریف میشود) استفاده نمود.
همان طور که پیشتر اشاره شد برای دسترسی به یک فیلد ابتدا یک نقطه پس از نام شیء درج کرده و سپس نام فیلد مورد نظر را مینویسیم.
Rectangle rectangle = new Rectangle(); rectangle.Width = 10.5;
public class Rectangle { public double Width = 5; // ... }
متدها
متدها قطعه کدهایی شامل یک سری دستور هستند. این مجموعه دستورات با فراخوانی متد و تعیین آرگومانهای مورد نیاز اجرا میشوند. در زبان سی شارپ به نوعی تمام دستورات در داخل متدها اجرا میشوند. در این زبان تمامی توابع در داخل کلاسها تعریف میشوند و بنابراین همه متد هستند.متدها نیز مانند فیلدها در داخل کلاس تعریف میشوند. ابتدا یک Access Modifier سطح دسترسی را تعیین مینماید. سپس به ترتیب نوع خروجی، نام متد و لیست پارامترهای آن در صورت وجود درج میشود. به مجموعه بخشهای یاد شده امضای متد میگویند.
پارامترهای یک متد داخل یک جفت پرانتز قرار میگیرند و با کاما (,) از هم جدا میشوند. یک جفت پرانتز خالی نشان دهنده آن است که متد نیاز به هیچ پارامتری ندارد.
بار دیگر به بخش تعریف متدهای کلاسی که ایجاد کردیم توجه نمایید.
public class Rectangle { // ... public double Area() { return Width*Height; } public double Perimeter() { return 2*(Width + Height); } }
همچنین توجه نمایید این شیء برای محاسبه مساحت و محیط خود نگاهی به ویژگیهای خود یعنی عرض و ارتفاعش که در فیلدهای آن نگهداری میکنیم میاندازد.
فراخوانی متد یک شیء همانند دسترسی به فیلد آن است. ابتدا نام شیء سپس یک نقطه و به دنبال آن نام متد مورد نظر به همراه پرانترها. آرگومانهای مورد نیاز در صورت وجود داخل پرانتزها قرار میگیرند و با کاما از هم جدا میشوند. که البته در این مثال متد ما نیازی به آرگومان ندارد. به همین دلیل برای فراخوانی آن تنها یک جفت پرانتز خالی قرار میدهیم.
در این بخش به دو مفهوم پارامتر و آرگومان اشاره شد. تفاورت آنها چیست؟
در هنگام تعریف یک متد نام و نوع پارامترهای مورد نیاز را تعیین و درج مینماییم. حال وقتی قصد فراخوانی متد را داریم باید مقادیر واقعی که آرگومان نامیده میشود را برای هر یک از پارامترهای تعریف شده فراهم نماییم. نوع آرگومان باید با نوع پارامتر تعریف شده تطبیق داشته باشد. اما اگر یک متغیر را به عنوان آرگومان در هنگام فراخوانی متد استفاده میکنیم نیازی به یکسان بودن نام آن متغیر و نام پارامتر تعریف شده نیست.
متدها میتوانند یک مقدار را به کدی که آن متد را فراخوانی کرده است بازگشت دهند.
Rectangle rectangle = new Rectangle(); rectangle.Width = 10.5; rectangle.Height = 10; double p = rectangle.Perimeter();
نکته: کلمه return علاوه بر بازگشت مقدار مورد نظر سبب پایان اجرای متد نیز میشود. حتی در صورتی که نوع خروجی یک متد void تعریف شده باشد استفاده از کلمه return بدون اینکه مقداری به دنبال آن بیاید میتواند برای پایان اجرای متد، در صورت نیاز و مثلاً برقراری شرطی خاص مفید باشد. بدون کلمه return متد زمانی پایان مییابد که به پایان قطعه کد بدنه خود برسد. توجه نمایید که در صورتی که نوع خروجی متد چیزی به جز void است استفاده از کلمه return به همراه مقدار مربوطه الزامی است.
مقدار خروجی یک متد را میتوان هر جایی که مقداری از همان نوع مناسب است مستقیماً به کار برد. همچنین میتوان آن را در یک متغیر قرار داد و سپس از آن استفاده نمود.
به عنوان مثال کلاس ساده زیر را در نظر بگیرید که متدی دارد برای جمع دو عدد.
public class SimpleMath { public int AddTwoNumbers(int number1, int number2) { return number1 + number2; } }
SimpleMath obj = new SimpleMath(); Console.WriteLine(obj.AddTwoNumbers(1, 2)); int result = obj.AddTwoNumbers(1, 2); Console.WriteLine(result);
در بخشهای بعدی بحث ما در مورد سایر اعضای کلاس و برخی جزییات پیرامون اعضای پیش گفته خواهد بود.
پیشنهادها
async scripts؛ خوب یا بد؟
منابع پیشنهادی
HTML script async Attribute
Load Non-blocking JavaScript with HTML5 Async and Defer
Async Attribute and Scripts At The Bottom
Script-injected "async scripts" considered harmful
the underestimated problem about script async attribute
Use Asynchronous Scripts
Deep dive into the murky waters of script loading
HTML5 Async Scripts
Can I use async
HTML script async Attribute
Load Non-blocking JavaScript with HTML5 Async and Defer
Async Attribute and Scripts At The Bottom
Script-injected "async scripts" considered harmful
the underestimated problem about script async attribute
Use Asynchronous Scripts
Deep dive into the murky waters of script loading
HTML5 Async Scripts
Can I use async
Protocol Buffers are a language-neutral, platform-neutral, extensible way of serializing structured data for use in communications protocols, data storage, and more, originally designed at Google (see).
protobuf.js is a pure JavaScript implementation with TypeScript support for node.js and the browser. It's easy to use, blazingly fast and works out of the box with .proto files!