این راهنما قراره بهت نشون بده که چطور با استفاده از الگوی طراحی Factory Method میتونی یک سیستم کنترل سنسور خیلی مرتب و انعطافپذیر بسازی. علاوه بر این، آخرش یک سری تمرین هم برات گذاشتم که بتونی مهارتهات رو تو این زمینه تقویت کنی.
فرض کن داری یک سیستم نهفته طراحی میکنی که باید چندین نوع سنسور مختلف رو کنترل کنه. مثلاً سنسورهای دما، فشار و رطوبت که هر کدومشون از یک پروتکل ارتباطی مختلف (مثل I2C، SPI و UART) استفاده میکنن. هدفت اینه که این سنسورها رو مدیریت کنی و در عین حال بتونی سنسورهای جدید رو به راحتی اضافه کنی.
اول بیا ببینیم اگه بخوای این مسئله رو بدون Factory Method حل کنی، چه مشکلاتی پیش میاد.
فرض کن باید برای هر نوع سنسور، یک سری توابع جداگانه برای مقداردهی اولیه (init) و خواندن داده (read_data) بنویسی. کدش ممکنه این شکلی باشه:
// sensor_manager.c
#include <stdio.h>
// توابع برای سنسور I2C
void init_I2CSensor(void) {
// کدهای مقداردهی اولیه برای I2C
}
int read_I2CSensor(void) {
// کد برای خواندن داده از سنسور I2C
return 0;
}
// توابع برای سنسور SPI
void init_SPISensor(void) {
// کدهای مقداردهی اولیه برای SPI
}
int read_SPISensor(void) {
// کد برای خواندن داده از سنسور SPI
return 0;
}
// توابع برای سنسور UART
void init_UARTSensor(void) {
// کدهای مقداردهی اولیه برای UART
}
int read_UARTSensor(void) {
// کد برای خواندن داده از سنسور UART
return 0;
}برای استفاده از این سنسورها، باید نوع سنسور و توابع مربوط بهش رو تو کد اصلی مشخص کنی.
// main.c
#include "sensor_manager.h"
int main(void) {
// مقداردهی اولیه سنسور I2C
init_I2CSensor();
// خواندن داده از سنسور I2C
int data = read_I2CSensor();
while (1) {
// حلقه اصلی برنامه
}
return 0;
}-
کد تکراری: برای هر نوع سنسور، باید توابع جداگانهای برای مقداردهی اولیه و خواندن داده تعریف کنی. این باعث میشه که کد تکراری زیادی داشته باشی.
-
عدم انعطافپذیری: اگه بخوای نوع سنسور یا پروتکل ارتباطی رو تغییر بدی، باید کدهای زیادی رو تو برنامه اصلی تغییر بدی.
-
افزودن سنسور جدید: اگه بخوای یک سنسور جدید اضافه کنی، باید توابع جدیدی براش تعریف کنی و برنامه اصلی رو هم تغییر بدی. این کار باعث میشه کدت پیچیده و نگهداریش سخت بشه.
حالا بیا ببینیم چطور میتونیم با استفاده از Factory Method این مشکلات رو حل کنیم.
اول یک ساختار پایه برای سنسورها تعریف میکنیم که شامل توابعی مثل init() و read_data() باشه. اینجوری میتونیم با هر نوع سنسوری به یک شکل برخورد کنیم.
// sensor.h
#ifndef SENSOR_H
#define SENSOR_H
typedef struct {
void (*init)(void);
int (*read_data)(void);
} Sensor;
Sensor* createI2CSensor(void);
Sensor* createSPISensor(void);
Sensor* createUARTSensor(void);
#endifحالا برای هر نوع سنسور، یک تابع Factory تعریف میکنیم که یک شیء Sensor بسازه و توابع مربوط به اون سنسور رو بهش اختصاص بده.
// sensor.c
#include "sensor.h"
#include <stdlib.h>
void I2C_init(void) {
// کدهای مقداردهی اولیه برای I2C
}
int I2C_read_data(void) {
// کد برای خواندن داده از سنسور I2C
return 0;
}
Sensor* createI2CSensor(void) {
Sensor* sensor = (Sensor*)malloc(sizeof(Sensor));
sensor->init = I2C_init;
sensor->read_data = I2C_read_data;
return sensor;
}
void SPI_init(void) {
// کدهای مقداردهی اولیه برای SPI
}
int SPI_read_data(void) {
// کد برای خواندن داده از سنسور SPI
return 0;
}
Sensor* createSPISensor(void) {
Sensor* sensor = (Sensor*)malloc(sizeof(Sensor));
sensor->init = SPI_init;
sensor->read_data = SPI_read_data;
return sensor;
}
void UART_init(void) {
// کدهای مقداردهی اولیه برای UART
}
int UART_read_data(void) {
// کد برای خواندن داده از سنسور UART
return 0;
}
Sensor* createUARTSensor(void) {
Sensor* sensor = (Sensor*)malloc(sizeof(Sensor));
sensor->init = UART_init;
sensor->read_data = UART_read_data;
return sensor;
}حالا میتونیم بدون تغییر تو کد اصلی، سنسورهای مختلف رو به سادگی مدیریت کنیم.
// main.c
#include "sensor.h"
int main(void) {
Sensor* mySensor;
// انتخاب سنسور بر اساس نیاز
mySensor = createI2CSensor(); // یا createSPISensor() یا createUARTSensor()
// مقداردهی اولیه سنسور
mySensor->init();
// خواندن داده از سنسور
int data = mySensor->read_data();
// پاکسازی حافظه
free(mySensor);
while (1) {
// حلقه اصلی برنامه
}
return 0;
}-
انعطافپذیری بالا: اضافه کردن سنسورهای جدید یا تغییر پروتکل ارتباطی بدون نیاز به تغییر تو کد اصلی انجام میشه.
-
کد مرتبتر و نگهداری راحتتر: همه سنسورها از یک ساختار پایه استفاده میکنن که باعث میشه کد تمیزتر و نگهداریش راحتتر باشه.
-
قابلیت گسترش: به راحتی میتونی سنسورهای جدیدی اضافه کنی و فقط باید یک تابع Factory جدید براش تعریف کنی.
| شماره | سوال | بارم |
|---|---|---|
| 1 | یک سنسور جدید به اسم createUARTSensor اضافه کن که از پروتکل UART استفاده میکنه. کدهای مربوط به مقداردهی اولیه و خواندن داده از سنسور رو بنویس. |
2 |
| 2 | برنامه اصلی رو طوری تغییر بده که بتونه به صورت پویا سنسور مورد نیاز رو بر اساس ورودی کاربر (مثلاً I2C یا SPI) انتخاب کنه. | 3 |
| 3 | یک سنسور جدید به اسم createAnalogSensor درست کن که از ورودی آنالوگ استفاده میکنه. |
4 |
| 4 | کد رو طوری بهینه کن که بتونه به راحتی تعداد زیادی سنسور از انواع مختلف رو مدیریت کنه. برای هر سنسور یک نمونه از کلاس پایه ایجاد کن و بهش اضافه کن. | 5 |