آموزش 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~~~
همین.
با تشکر برای خواندن.
دریغ نکنید که نسخه ویدیویی این مقاله را اینجا ببینید.