فلاتر

الگوی طراحی Factory در برنامه نویسی چیست + مثال عملی در فلاتر

الگوی طراحی Factory که گاهی اوقات به عنوان الگوی سازنده مجازی Virtual Constructor نیز شناخته می‌شود، راهی برای پنهان کردن منطق ایجاد یک شی را ارائه می‌دهد، اما تضمین می‌شود که شیء بازگردانده شده به یک رابط شناخته شده پایبند است.

این الگو روشی را ارائه می‌دهد که به شما امکان می‌دهد اشیا را بدون نیاز به دانستن دقیقاً کدام نوع شیء را می‌سازید، ایجاد کنید.

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

این روش تضمین می‌کند که همه اشیایی که ایجاد می‌کند با یک رابط مشترک مطابقت دارند، بنابراین می‌توانید بدون نگرانی از جزئیات پیاده‌سازی، با آنها کار کنید.

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

الگوهای طراحی ایجاد چی هستند؟

الگوهای طراحی ایجادی creational design patterns، همانطور که از اسم آنها مشخص است، دستیارهای ما برای ساختن شی و هرچیزی که به آن ربط دارد هستند.

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

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

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

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

بعدش هم می‌بینیم که چطور می‌تونیم از این الگو برای ساخت ساده‌ی المان‌های UI برای پلتفرم‌های مختلف در فلاتر استفاده کنیم.

مثال کلاسیک از الگوی طراحی Factory

خب، برای اینکه ساختار الگوی روش Factory براتون کاملا جا بیفته، از یه مثال خیلی ساده ولی کلاسیک شروع می‌کنیم. می‌خواهیم یه کارخانه‌ی شکل Shape درست کنیم که بتونه مثل یه جادوگر هم مثلث triangle  خلق کنه و هم مستطیل rectangle.

طرحِ پایین، اجزای اصلی و ارتباطات بینشون رو نشون می‌ده:

  • کارخانه‌ی شکل (Shape Factory): این رئیس کله‌گنده‌ی کارخونه‌ست که دستور تولید شکل‌ها رو صادر می‌کنه.
  • روش‌های ساخت (Create Methods): این جادوگرهای داخل کارخونه هستن که با گرفتن یه ورودی ساده (مثلا یه رشته‌ی “مثلث” یا “مستطیل”)، خودشون می‌دونن چجوری یه شکل از اون نوع بسازن.
  • محصولات (Products): خب، خروجی‌های کارخونه چی هستن؟ همون شکل‌های قشنگی که با دست جادوگرها ساخته می‌شن، مثلث و مستطیل.

کلاس Shape به عنوان کارخانه و رابط در این مثال عمل می‌کند، در حالی که Triangle و Rectangle نمونه‌هایی از محصولات عینی کارخانه هستند. این محصولات رابط کارخانه را پیاده‌سازی می‌کنند و پیاده‌سازی‌های ملموسی از متد draw() ارائه می‌دهند.

اما نوبت به پیاده سازی این عملیات به زبان دارت میباشد.

enum ShapeType {
  triangle,
  rectangle
}

abstract class Shape {
  factory Shape(ShapeType type) {
    switch (type) {
      case ShapeType.triangle: return Triangle();
      case ShapeType.rectangle: return Rectangle();
      default: return null;
    }
  }

  void draw();
}

class Triangle implements Shape {
  @override
  void draw() {
    print("TRIANGLE");
  }
}

class Rectangle implements Shape {
  @override
  void draw() {
    print("RECTANGLE");
  }
}

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

در مرحله بعد، کارخانه اشیا قرار دارد که در این مورد به شکل یک کلاس انتزاعی به نام Shape است.

کلاس Shape یک constructor از نوع Factory دارد که به عنوان متد کارخانه برای این الگو عمل می‌کند.

این متد مسئول ایجاد اشکال از نوع درخواستی است.

کلاس به صورت Abstract پیاده سازی شده است تا از نمونه‌سازی مستقیم Shape جلوگیری شود، زیرا کلاس هیچ پیاده‌سازی برای متد draw() ندارد.

constructorهای کارخانه‌ای زبان دارت مانند توابع static عمل می‌کنند که اتفاقاً نامی مشابه کلاس دارند و لزوماً نمونه‌ای از همان کلاس را بر نمی‌گردانند (اگرچه باید یک نوع مرتبط را برگردانند).

با استفاده از این دستور، می‌توانیم کد کلاینت را زیبا نگه داریم و به طور موثر استفاده از الگوی Factory Method را از کاربران آن پنهان کنیم.

یک ساختار switch برای بازگرداندن نوع شکل مناسب استفاده می‌شود و در صورت انتقال یک نوع نامعتبر، null برمی‌گرداند.

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

دو کلاس Rectangle و Triangle هر کدام بصورت جداگانه متد draw را پیاده سازی میکنند.

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

final shape1 = Shape(ShapeType.triangle);
final shape2 = Shape(ShapeType.rectangle);

shape1.draw();
shape2.draw();

براساس قاعده polymorphism در برنامه نویسی شی گرایی برای هر شی متد صحیح draw() فراخوانی میشود.

هم shape1 و هم shape2 از نوع Shape هستند، اما در واقع یکی مثلث و دیگری مستطیل است. می‌توان یک List<Shape> ایجاد کرد که حاوی ترکیبی از انواع شکل باشد.

استفاده از متد Factory در فلاتر

یکی از کاربردهای واضح الگوی روش Factory در یک برنامه فلاتر، تولید عناصر رابط کاربری با استایل بومی برای پلتفرم‌های مختلفه.

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

abstract class PlatformButton {
  factory PlatformButton(TargetPlatform platform) {
    switch (platform) {
      case TargetPlatform.android: return AndroidButton();
      case TargetPlatform.iOS: return IosButton();
      default: return null;
    }
  }

  Widget build({
    @required BuildContext context,
    @required Widget child,
    @required VoidCallback onPressed
  });
}

برای دسترسی به ویجت‌های ElevatedButton با طراحی متریال و ویجت‌های CupertinoButton با استایل iOS، ابتدا کتابخانه‌های مناسب فلاتر ایمپورت می‌کنیم.

درست همانند مثال اشکال از بخش قبلی، یه کلاس انتزاعی برای قرار دادن متد کارخانه ایجاد می‌کنیم.

constructor کارخانه‌ای PlatformButton، بر اساس مقدار پارامتر platform، یه نمونه از کلاسی که PlatformButton رو پیاده‌سازی می‌کنه، برمی‌گردونه.

TargetPlatform یک enum هست که توسط فریم‌ورک Flutter ارائه شده و برای هر کدوم از پلتفرم‌های پشتیبانی‌شده فلاتر مقادیری داره.

نشت حافظه در فلاتر

بخش switch اینجا وظیفه داره که یه نمونه دکمه مطابق با ترجیح کاربر برگردونه. توجه داشته باشین که AndroidButton و IosButton هنوز ایجاد نشدن، اما نگران نباشین، به زودی می‌رسیم بهشون.

PlatformButton همچنین یه اعلان پیاده‌سازی‌ نشده برای متد build() داره تا انتظار ایجاد کنه که پیاده‌کننده‌ها اون رو با یه signature سازگار override می‌کنند.

متد build() پارامترهایی رو می‌گیره که توسط ویجت‌های دکمه خاص پلتفرم مورد نیاز هستند.

class AndroidButton implements PlatformButton {
  @override
  Widget build({
    @required BuildContext context,
    @required Widget child,
    @required VoidCallback onPressed
  }) {
    return ElevatedButton(
      child: child,
      onPressed: onPressed,
    );
  }
}

class IosButton implements PlatformButton {
  @override
  Widget build({
    @required BuildContext context,
    @required Widget child,
    @required VoidCallback onPressed
  }) {
    return CupertinoButton(
      child: child,
      onPressed: onPressed,
    );
  }
}

AndroidButton و IosButton اینترفیسی که توسط PlatformButton ایجاد شده پیاده‌سازی می‌کنن و متد build() هر کلاس، یه ویجت دکمه با استایل متناسب با اون پلتفرم‌ها رو برمی‌گردونه.

آرگومان‌های child و onPressed به اون ویجت‌ها منتقل می‌شن. یه جایی توی برنامه، یه کارخانه PlatformButton می‌تونه اینجوری ایجاد بشه:

PlatformButton(TargetPlatform.android)

و یا به این شکل:

PlatformButton(Theme.of(context).platform)

با این روش، همه دکمه‌های یک برنامه می‌تونن به‌طور خودکار با استایل پلتفرم میزبان رندر بشن و متد‌های build برنامه هم با کدهای اضافی تشخیص پلتفرم شلوغ نمیشوند.

برای ساختن یه دکمه، فقط کافیه متد build() رو صدا بزنید:

PlatformButton(Theme.of(context).platform).build(
  context: context,
  child: Text('My Button'),
  onPressed: () => print('Button pressed!'),
)

با کدی که فقط کمی بیشتر از ایجاد یک دکمه معمولی هست، می‌تونین یه دکمه داشته باشین که از استایل متریال دیزاین اندروید استفاده می‌کنه یا دکمه‌ای که ظاهر و حس iOS رو داره، و این کاملاً بستگی به پلتفرمی داره که برنامه فلاتر شما روی اون اجرا می‌شه.

تا اینجا یاد گرفتیم که چطور از الگوی طراحی Factory برای کمک به برنامه‌هامون در ایجاد یکپارچه دکمه‌هایی با استایل متناسب با پلتفرم استفاده کنیم.

Hesam

Recent Posts

گیتهاب اکشن چیست؟ آموزش استفاده از گیتهاب اکشن در برنامه نویسی فلاتر

گیتهاب اکشن GitHub Actions یکی از ابزارهای گیتهاب است که به شما کمک می‌کنه تا…

6 روز ago

آموزش افزایش سرعت اجرای وب اپلیکیشن های فلاتر

اگر یک برنامه نویس فلاتر هستید و با از نسخه وب اپلیکیشن پروژتون استفاده میکنید…

1 ماه ago

آموزش جامع انتشار اپلیکیشن اندروید و فلاتر در فروشگاه گوگل پلی Google play

به عنوان یک برنامه نویس فلاتر یا اندروید بعد از اتمام پروسه طراحی اپلیکیشن نیاز…

2 ماه ago

دانلود سورس کد رابط کاربری اپلیکیشن فلاتر پروژه پادکست

طراحی رابط کاربری اپلیکیشن پادکست خود را با استفاده از این کیت توسعه UI/UX فلاتر…

2 ماه ago

فایربیس چیست؟ معرفی سرویس ابری Firebase و کاربردهای آن

فایربیس، پلتفرمی قدرتمند از شرکت گوگل برای توسعه و مدیریت برنامه‌های موبایل و وب است.…

3 ماه ago

آموزش پیاده سازی Method Channel در فلاتر + فیلم

فلاتر یک فریم ورک برنامه نویسی چندسکویی است که این امکان را برای برنامه نویس…

3 ماه ago