برنامه نویسی

آموزش SOLID Principles برای دوستداران پایتون

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

ما با یک طراحی پایه در پایتون شروع خواهیم کرد که این اصول را زیر پا می گذارد و آن را گام به گام بهبود خواهیم داد.

بیا شروع کنیم!


این آموزش به صورت ویدئویی در اینجا موجود است.


SOLID مخفف:

  • مسئولیت واحد
  • باز برای گسترش و بسته برای تغییر
  • تعویض لیسکوف
  • جداسازی رابط
  • وارونگی وابستگی

در زیر یک کد ساده داریم که chatGPT API را فراخوانی می کند تا یک پیام تولد برای شخصی ایجاد کند و آن پیام را برای آن شخص در شماره تلفن واتس اپ خود ارسال کند.

class AIBirthdayMessageSender:
    """Handle the feature of sending a birthday message to someone."""

    def __init__(self):
        self.message = ''

    def generate_message(self, receiver_name: str):
        print("Authenticate to ChatGPT API and request for birthday message")
        self.message = f"Happy birthday {receiver_name} from ChatGPT"

    def send(self, phone: str):
        print(f"Authenticate to Whatsapp API and send message to {phone}")
        print(f"message sent: ~~~{self.message}~~~")

if __name__ == "__main__":
    birthday_msg_sender = AIBirthdayMessageSender()
    birthday_msg_sender.generate_message("Johnathan Smith")
    birthday_msg_sender.send(phone="022990149224")
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برای سادگی، ما به جای کد واقعی، آنچه را که قرار است کد انجام دهد، چاپ کرده ایم.


1. مسئولیت واحد

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

Authenticate to ChatGPT API and request for birthday message
Authenticate to Whatsapp API and send message to 022990149224
message sent: ~~~Happy Birthday Johnathan Smith from ChatGPT~~~
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بنابراین خوب به نظر می رسد.

اما، یک مسئله وجود دارد. کلاس AIBirthdayMessageSender، همانطور که از نام آن پیداست فقط باید مسئول باشد
برای ارسال پیام اما همانطور که می بینیم، تولید پیام از chatGPT را نیز مدیریت می کند. بنابراین دو کار نامرتبط را انجام می دهد که باعث شکستن آن می شود مسئولیت واحد اصل

برای رفع این مشکل می‌توانیم عملکرد تولید پیام هوش مصنوعی را به کلاس دیگری منتقل کنیم:

class ChatGptBirthdayMsgGenerator:
    """Generate a birthday message from chat GPT."""

    def __init__(self, receiver_name: str):
        self.receiver_name = receiver_name

    def generate(self):
        print("Authenticate to ChatGPT API and request for birthday message")
        return f"Happy Birthday {self.receiver_name} from ChatGPT"


class AIBirthdayMessageSender:
    """Handle the feature of sending a birthday message to someone."""

    def __init__(self, message: str):
        self.message = message

    def send(self, phone: str):
        print(f"Authenticate to Whatsapp API and send message to {phone}")
        print(f"message sent: ~~~{self.message}~~~")


if __name__ == "__main__":
    msg_generator = ChatGptBirthdayMsgGenerator(receiver_name="Johnathan Smith")
    message = msg_generator.generate()
    birthday_msg_sender = AIBirthdayMessageSender(message)
    birthday_msg_sender.send(phone="022990149224")
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

و اگر بدوید، باز هم باید کار کند.


2. باز برای گسترش و بسته برای تغییر

این اصل بیان می کند که قبل از اینکه بتوانیم یک رفتار را تکرار کنیم، نیازی به تغییر کد موجود نداریم.

فرض کنید در آینده می خواهیم بتوانیم پیام را از طریق ایمیل یا به یک حساب فیس بوک به جای شماره واتس اپ ارسال کنیم.
ما موظف به اصلاح خواهیم بود AIBirthdayMessageSender اینجوری احتمالا:

class AIBirthdayMessageSender:

    def send_phone(self, phone: str):
        pass

    def send_email(self, email: str):
        pass

    def send_facebook(self, username: str):
        pass
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اما با این کار ما آن را می شکنیم باز/بستن اصل چون ما باید کلاس خود را در راه می نوشتیم
اگر بخواهیم ویژگی جدیدی اضافه کنیم، نیازی به تغییر کلاس موجود نداریم، بلکه فقط باید آنها را گسترش دهیم.

می توانیم با استفاده از وراثت آن را برطرف کنیم:

from abc import ABC, abstractmethod

class ChatGptBirthdayMsgGenerator:
    """Generate a birthday message from chat GPT."""

    def __init__(self, receiver_name: str):
        self.receiver_name = receiver_name

    def generate(self):
        print("Authenticate to ChatGPT API and request for birthday message")
        return f"Happy Birthday {self.receiver_name} from ChatGPT"

class AIBirthdayMessageSender(ABC):

    def __init__(self, message: str):
        self.message = message

    @abstractmethod
    def send(self, phone: str):
        pass

class WhatsAppBirthdayMessageSender(AIBirthdayMessageSender):
    """Handle the feature of sending a birthday message to someone."""

    def send(self, phone: str):
        print(f"Authenticate to Whatsapp API and send message to {phone}")
        print(f"message sent: ~~~{self.message}~~~")

class EmailBirthdayMessageSender(AIBirthdayMessageSender):
    """Handle the feature of sending a birthday message to someone."""

    def send(self, email: str):
        print(f"Authenticate to Email API and send mail to {email}")
        print(f"message sent: ~~~{self.message}~~~")

if __name__ == "__main__":
    msg_generator = ChatGptBirthdayMsgGenerator(receiver_name="Johnathan Smith")
    message = msg_generator.generate()

    whatsapp_sender = WhatsAppBirthdayMessageSender(message)
    whatsapp_sender.send(phone="022990149224")

    email_sender = EmailBirthdayMessageSender(message)
    email_sender.send(email="[email protected]")
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اگر دوباره کد را اجرا کنید، باید ببینید:

Authenticate to ChatGPT API and request for birthday message
Authenticate to Whatsapp API and send message to 022990149224
message sent: ~~~Happy Birthday Johnathan Smith from ChatGPT~~~
Authenticate to Email API and send mail to [email protected]
message sent: ~~~Happy Birthday Johnathan Smith from ChatGPT~~~
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

هنوز هم کار می کند.


3. تعویض لیسکوف

این اصل بیان می‌کند که ما باید بتوانیم یک شی از یک کلاس معین را با هر شیء از زیر کلاس‌های آن بدون شکستن یا تغییر درستی برنامه جایگزین کنیم.

در کد ما داریم WhatsAppBirthdayMessageSender و EmailBirthdayMessageSender که زیر کلاس های AIBirthdayMessageSender.
ولی AIBirthdayMessageSender.send() متد یک پارامتر را تعریف می کند phone که نمی تواند برای همه زیر کلاس ها اعمال شود، به خصوص برای EmailBirthdayMessageSender که ندارد phone ولی email بجای. این می شکند Liskov substitution اصل و برای رفع آن، فقط می توانیم استدلال را حذف کنیم phone از طرف والد، زیرا این یک استدلال کلی نیست AIBirthdayMessageSender، پسندیدن message در سازنده، اما مختص به EmailBirthdayMessageSender.

کد جدیدش اینه:

class AIBirthdayMessageSender(ABC):

    def __init__(self, message: str):
        self.message = message

    @abstractmethod
    def send(self):
        pass
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

به این ترتیب دیگر اجازه تماس نخواهیم داشت AIBirthdayMessageSender.send() با phone همانطور که به آن تعلق دارد EmailBirthdayMessageSender
زیر کلاس


4. تفکیک رابط

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

راه دیگری که می توانستیم کد خود را برای احترام به آن تغییر دهیم تعویض لیسکوف اصل این است که این کار را انجام دهید:

class AIBirthdayMessageSender(ABC):
    ...
    @abstractmethod
    def send(self, phone=None, email=None, facebook_username=None,):
        pass
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ما یک رابط سراسری واحد در کلاس والد داریم، با تمام آرگومان های ممکن برای the send روش تمام زیر کلاس های آن، و ما آنها را اختیاری می کنیم.
بنابراین برای هر زیر کلاس، ما فقط می توانیم آرگومان های مورد نیاز را پاس کنیم و بقیه آرگومان ها خواهند بود None.

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


5. وارونگی وابستگی

این اصل آخر بیان می‌کند که کلاس‌های سطح بالا باید به انتزاعات بستگی داشته باشند نه به کلاس‌های مشخص یا کلاس‌های سطح پایین.

ابتدا بیایید سعی کنیم یک کلاس سطح بالا اضافه کنیم. این فقط کلاس اصلی است که کلاس های دیگر را برای مدیریت منطق تجاری فرا می خواند.

a اضافه کرده ایم User کلاس و یک کلاس منطق تجاری به نام AIBirthdayManager که از کلاس های سطح پایین مانند ChatGptBirthdayMsgGenerator
یا WhatsAppBirthdayMessageSender برای رسیدگی به برخی از ویژگی ها در اطراف تولد یک داده شده است User:

اصلاح کرده ایم ChatGptBirthdayMsgGenerator برای به ارث بردن آن از یک انتزاع:

class User:
    def __init__(self, name, email, phone):
        self.name = name
        self.email = email
        self.phone = phone

class AIBirthdayManager:

    def __init__(self, user: User):
        self.user = user

    def order_a_gift(self):
        pass

    def send_happy_birthday_message(self):
        msg_generator = ChatGptBirthdayMsgGenerator(self.user.name)
        message = msg_generator.generate()
        whatsapp_sender = WhatsAppBirthdayMessageSender(message)
        whatsapp_sender.send(phone="022990149224")

if __name__ == "__main__":
    user = User("Johnathan Smith", "[email protected]", "022990149224")
    birthday_manager = AIBirthdayManager(user=user)
    birthday_manager.send_happy_birthday_message()
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

مسئله این است که در آینده اگر بخواهیم ارائه دهنده هوش مصنوعی را به یک ارائه دهنده دیگر مانند BardAI تغییر دهیم، موظف به تغییر خواهیم بود. AIBirthdayManager کلاسی که می شکند باز بسته اصل
از همین رو AIBirthdayManager نباید مستقیماً به ChatGptBirthdayMsgGenerator، اما در یک انتزاع از آن مانند این است:

class AIBirthdayMsgGenerator(ABC):
    def __init__(self, receiver_name: str):
        self.receiver_name = receiver_name

    @abstractmethod
    def generate(self):
        pass

class ChatGptBirthdayMsgGenerator(AIBirthdayMsgGenerator):
    """Generate a birthday message from chat GPT."""
    def generate(self):
        print("Authenticate to ChatGPT API and request for birthday message")
        return f"Happy Birthday {self.receiver_name} from ChatGPT"

...

class AIBirthdayManager:
    def __init__(self, user, ai_class: AIBirthdayMsgGenerator):
        self.user = user
        self.ai_class = ai_class

    def send_happy_birthday_message(self):
        msg_generator = self.ai_class(self.user.name)
        ...

if __name__ == "__main__":
    user = User("Johnathan Smith", "[email protected]", "022990149224")
    birthday_manager = AIBirthdayManager(
        user=user,
        ai_class=ChatGptBirthdayMsgGenerator
    )
    birthday_manager.send_happy_birthday_message()
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اکنون ماژول سطح بالای ما AIBirthdayManager بستگی به انتزاع دارد AIBirthdayMsgGenerator و برای تغییر ارائه‌دهنده هوش مصنوعی، فقط باید زیر کلاس دیگری از آن ایجاد کنیم AIBirthdayMsgGenerator و آن را به ai_class آرگومان در سازنده
بیایید BardAI را پیاده سازی کنیم:

class BardAIBirthdayMsgGenerator(AIBirthdayMsgGenerator):
    """Generate a birthday message from BardAI."""
    def generate(self):
        print("Authenticate to BardAI API and request for birthday message")
        return f"Happy Birthday {self.receiver_name} from BardAI"

if __name__ == "__main__":
    user = User("Johnathan Smith", "[email protected]", "022990149224")
    birthday_manager = AIBirthdayManager(
        user=user,
        ai_class=BardAIBirthdayMsgGenerator
    )
    birthday_manager.send_happy_birthday_message()
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

خروجی جدید باید:

Authenticate to BardAI API and request for birthday message
Authenticate to Whatsapp API and send message to 022990149224
message sent: ~~~Happy Birthday Johnathan Smith from BardAI~~~
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

همین.
با تشکر برای خواندن.
دریغ نکنید که نسخه ویدیویی این مقاله را اینجا ببینید.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا