SOLID ์›์น™๐Ÿ˜ฏ

๊ธฐ๋ฆฐ์ดยท2021๋…„ 3์›” 5์ผ
0

ํˆฌ๋น…์Šค(Tobigs)๐Ÿง 

๋ชฉ๋ก ๋ณด๊ธฐ
1/9
post-thumbnail

๊ฐ์ฒด์ง€ํ–ฅ์–ธ์–ด๊ฐ€ ๊ฐ€์ ธ์•ผํ•˜๋Š” ์กฐ๊ฑด์ธ ์†”๋ฆฌ๋“œ ์›์น™์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ฒ ๋“œ์•„,,

  • ์šฉ์–ด ์ •๋ฆฌ
    ์ถ”์ƒ ํด๋ž˜์Šค:
from abc import *
 
class ์ถ”์ƒํด๋ž˜์Šค์ด๋ฆ„(metaclass=ABCMeta):
    @abstractmethod
    def ๋ฉ”์„œ๋“œ์ด๋ฆ„(self):
        ์ฝ”๋“œ

์œ„์™€ ๊ฐ™์€ ๊ตฌ์„ฑ์œผ๋กœ ์‚ฌ์šฉ
์•„๋ž˜๋Š” ์˜ˆ์‹œ

from abc import *
 
class StudentBase(metaclass=ABCMeta):
    @abstractmethod
    def study(self):
        pass
 
    @abstractmethod
    def go_to_school(self):
        pass
 
class Student(StudentBase):
    def study(self):
        print('๊ณต๋ถ€ํ•˜๊ธฐ')
 
    def go_to_school(self):
        print('ํ•™๊ต๊ฐ€๊ธฐ')
 
james = Student()
james.study()
james.go_to_school()

์ถœ์ฒ˜
์ถ”์ƒํด๋ž˜์Šค๋Š” ์‹ค์ œ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋Š” ํด๋ž˜์Šค๊ฐ€ ์•„๋‹ˆ๋ผ ์ƒ์†์„ ์œ„ํ•œ ํด๋ž˜์Šค์ด๋‹ค. ์ถ”์ƒ ํด๋ž˜์Šค์—์„œ ํ•ด๋‹น ํด๋ž˜์Šค๊ฐ€ ๊ผญ ๊ฐ€์ ธ์•ผํ•  ๊ธฐ๋Šฅ์„ ๋ช…์‹œํ•ด๋†“์œผ๋ฉด ์ด๋ฅผ ์ƒ์†๋ฐ›๋Š” ํด๋ž˜์Šค๋Š” ๊ผญ ์ƒ์œ„ํด๋ž˜์Šค์˜ ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด์•ผํ•œ๋‹ค.

์ธํ„ฐํŽ˜์ด์Šค:

์ถœ์ฒ˜

Single Responsiblity Principle (๋‹จ์ผ ์ฑ…์ž„ ์›์น™)

  • ์†Œํ”„ํŠธ์›จ์–ด์˜ ์„ค๊ณ„ ๋ถ€ํ’ˆ(ํด๋ž˜์Šค, ํ•จ์ˆ˜ ๋“ฑ)์€ ๋‹จ ํ•˜๋‚˜์˜ ์ฑ…์ž„๋งŒ์„ ๊ฐ€์ ธ์•ผ ํ•œ๋‹ค.
    ํ•˜๋‚˜์˜ ๋ฉ”์˜๋“œ๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์—ญํ• ์„ ํ•˜๋ฉด ์•ˆ๋œ๋‹ค๋Š” ๋œป์ด๋ผ๊ณ  ํŒŒ์•…๋œ๋‹ค.

์˜ˆ์‹œ

class Human:
    """์ถ”์ƒํ™” ๊ณผ์ •์ด ํ•„์š”ํ•œ Human ํด๋ž˜์Šค"""
    def __init__(self, name, sex):
        self.name = name
        self.sex = sex
        
    def go_restroom(self):
        """ํ™”์žฅ์‹ค ๊ฐ€๋Š” ํ•จ์ˆ˜"""
        if(self.sex == '๋‚จ์ž'):
            print("๋‚จ์žํ™”์žฅ์‹ค๋กœ ๊ฐ„๋‹ค.")
        elif(self.sex == '์—ฌ์ž'):
            print("์—ฌ์žํ™”์žฅ์‹ค๋กœ ๊ฐ„๋‹ค.")
        else:
            print("์„ฑ๋ณ„์„ ์ง€์ •ํ•ด์ฃผ์„ธ์š”.")
 


์ถœ์ฒ˜: https://doorbw.tistory.com/236 [Tigercow.Door]

์œ„๋Š” ๋‹จ์ผ ์ฑ…์ž„์›์น™์ด ์•ˆ์ง€์ผœ์ง„ ์ž˜๋ชป๋œ ์˜ˆ์‹œ

from abc import *
 
class HumanBase(metaclass=ABCMeta):
    def __init__(self, name):
        self.name = name
    
    @abstractmethod
    def go_restroom(self):
        pass
    
class Male(HumanBase):
    def __init__(self, name):
        super().__init__(name)
        self.sex = "๋‚จ์ž"
    
    def go_restroom(self):
        print("๋‚จ์žํ™”์žฅ์‹ค๋กœ ๊ฐ„๋‹ค.")
        
class Female(HumanBase):
    def __init__(self, name):
        super().__init__(name)
        self.sex = "์—ฌ์ž"
        
    def go_restroom(self):
        print("์—ฌ์žํ™”์žฅ์‹ค๋กœ ๊ฐ„๋‹ค.")


์ถœ์ฒ˜: https://doorbw.tistory.com/236 [Tigercow.Door]

์œ„๋Š” ๋‹จ์ผ์ฑ…์ž„์›์น™์ด ์ง€์ผœ์ง„ ์˜ˆ์‹œ

Open-Closed Principle (๊ฐœ๋ฐฉ-ํŒจ์‡„ ์›์น™)

  • ๊ธฐ์กด์˜ ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ (Closed) ๊ธฐ๋Šฅ์„ ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋„๋ก(Open) ์„ค๊ณ„ํ•ด์•ผ ํ•œ๋‹ค.

  • ํ™•์žฅ์—๋Š” ์—ด๋ ค์žˆ๊ณ , ๋ณ€๊ฒฝ์—๋Š” ๋‹ซํ˜€์žˆ์–ด์•ผ ํ•œ๋‹ค.

    ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•จ์— ์žˆ์–ด์„œ๋Š” ์‹ ๊ทœ ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ์†Œ์Šค ์ถ”๊ฐ€๋งŒ ์ง„ํ–‰ํ•ด์•ผ ํ•˜๊ณ  ๊ธฐ์กด์˜ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•ด์•ผ ํ•˜๋Š” ์ผ์€ ์—†์–ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

class Message:
    """Message ์ถ”์ƒ ํด๋ž˜์Šค"""
    def __init__(self, data):
        self.data = data
 
class FirstGradeMessage(Message):
    """FirstGrade์— ๋Œ€ํ•œ ๋ฉ”์„ธ์ง€ ์ฒ˜๋ฆฌ ํด๋ž˜์Šค"""
    
class SecondGradeMessage(Message):
    """SecondGrade์— ๋Œ€ํ•œ ๋ฉ”์„ธ์ง€ ์ฒ˜๋ฆฌ ํด๋ž˜์Šค"""
    
class ThirdGradeMessage(Message):
    """ThirdGrade์— ๋Œ€ํ•œ ๋ฉ”์„ธ์ง€ ์ฒ˜๋ฆฌ ํด๋ž˜์Šค"""
    
class DefaultGradeMessage(Message):
    """DefaultGrade์— ๋Œ€ํ•œ ๋ฉ”์„ธ์ง€ ์ฒ˜๋ฆฌ ํด๋ž˜์Šค"""
    
class GradeMessageClassification():
    """Grade์— ๋”ฐ๋ฅธ ๋ฉ”์„ธ์ง€ ๋ถ„๋ฅ˜ ํด๋ž˜์Šค"""
    def __init__(self, data):
        self.data = data
        
    def classification(self):
        if(self.data['grade'] == 1):
            return FirstGrade(self.data)
        elif(self.data['grade'] == 2):
            return SecondGrade(self.data)
        elif(self.data['grade'] == 3):
            return ThirdGrade(self.data)
        else:
            return DefaultGrade(self.data)


์ถœ์ฒ˜: https://doorbw.tistory.com/237 [Tigercow.Door]

Message๋ผ๋Š” ์ถ”์ƒํด๋ž˜์Šค๋ฅผ ๊ฐ ํ•™๋…„ ํด๋ž˜์Šค๊ฐ€ ์ƒ์†๋ฐ›๋Š” ํ˜•์‹

GradeclassificationMessage์—์„œ data๋ฅผ ๋ฐ›์•„์„œ ๊ฐ ํ•™๋…„์„ ๊ตฌ๋ถ„ํ•˜๊ณ  ํ•ด๋‹น ํ•™๋…„์˜ ํด๋ž˜์Šค๋ฅผ ์ด์šฉํ•ด์„œ ๋ฐ์ดํ„ฐ ๋ฐ˜ํ™˜

์—ฌ๊ธฐ์„œ ๋ฌธ์ œ์ ์€ grade๊ฐ€ ๋Š˜์–ด๋‚  ๊ฒฝ์šฐ GradeclassificationMessage์˜ ํ•จ์ˆ˜๋ฅผ ๋ฐ”๊ฟ”์•ผ ํ•œ๋‹ค.

OCP๊ฐ€ ์ง€์ผœ์ง€์ง€ ์•Š์•˜๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

class Message:
   """Message ์ถ”์ƒ ํด๋ž˜์Šค"""
   def __init__(self, data):
       self.data = data
       
   @staticmethod
   def is_collect_grade_message(data: dict):
       return False

class FirstGradeMessage(Message):
   """FirstGrade์— ๋Œ€ํ•œ ๋ฉ”์„ธ์ง€ ์ฒ˜๋ฆฌ ํด๋ž˜์Šค"""
   @staticmethod
   def is_collect_grade_message(data: dict):
       return data['grade'] == 1
   
class SecondGradeMessage(Message):
   """SecondGrade์— ๋Œ€ํ•œ ๋ฉ”์„ธ์ง€ ์ฒ˜๋ฆฌ ํด๋ž˜์Šค"""
   @staticmethod
   def is_collect_grade_message(data: dict):
       return data['grade'] == 2
   
class ThirdGradeMessage(Message):
   """ThirdGrade์— ๋Œ€ํ•œ ๋ฉ”์„ธ์ง€ ์ฒ˜๋ฆฌ ํด๋ž˜์Šค"""
   @staticmethod
   def is_collect_grade_message(data: dict):
       return data['grade'] == 3
   
class DefaultGradeMessage(Message):
   """DefaultGrade์— ๋Œ€ํ•œ ๋ฉ”์„ธ์ง€ ์ฒ˜๋ฆฌ ํด๋ž˜์Šค"""
   
class GradeMessageClassification():
   """Grade์— ๋”ฐ๋ฅธ ๋ฉ”์„ธ์ง€ ๋ถ„๋ฅ˜ ํด๋ž˜์Šค"""
   def __init__(self, data):
       self.data = data
       
   def classification(self):
       for grade_message_cls in Message.__subclasses__():
           try:
               if grade_message_cls.is_collect_grade_message(self.data):
                   return grade_message_cls(self.data)
           except KeyError:
               continue
               
           return DefaultGradeMessage(self.data)


์ถœ์ฒ˜: https://doorbw.tistory.com/237 [Tigercow.Door]
  • ์šฐ์„  ์ถ”์ƒํด๋ž˜์Šค์ธ Message๊ฐ€ ์žˆ๊ณ , ๊ฐ ํ•™๋…„ ํด๋ž˜์Šค๋Š” Message๋ฅผ ์ƒ์†๋ฐ›๋Š”๋‹ค.

  • ๊ฐ ํ•™๋…„ ํด๋ž˜์Šค๋Š” ๋ฐ›์€ ๋ฐ์ดํ„ฐ๊ฐ€ ํ•ด๋‹น ํ•™๋…„ ๊ฒƒ์ธ์ง€ True/False ๋กœ ์•Œ๋ ค์ฃผ๋Š” is_collect_grade_message ๋ฉ”์˜๋“œ๊ฐ€ ์žˆ๋‹ค.

  • Message์˜ ๋ชจ๋“  ํ•˜์œ„ํด๋ž˜์Šค์˜ is_collect_grade_message๋ฅผ ์‹คํ–‰ํ•ด์„œ True์ธ ๋ฉ”์˜๋“œ์˜ ๋ฐ์ดํ„ฐ ๋ฐ˜ํ™˜!

Liskov Substitution Principle (๋ฆฌ์Šค์ฝ”ํ”„ ์น˜ํ™˜ ์›์น™)

  • ์ž์‹ ํด๋ž˜์Šค๋Š” ๋ถ€๋ชจํด๋ž˜์Šค์—์„œ ๊ฐ€๋Šฅํ•œ ํ–‰์œ„๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.
    ๋ถ€๋ชจ ํด๋ž˜์Šค๊ฐ€ ๊ฐ€์ง€๋Š” ์†์„ฑ์€ ์ž์‹ ํด๋ž˜์Šค๋Š” ๋ชจ๋‘ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์•ผ ํ•œ๋‹ค๋Š” ์˜๋ฏธ๋กœ ํŒŒ์•…๋œ๋‹ค.

Interface Segregation Principle (์ธํ„ฐํŽ˜์ด์Šค ๋ถ„๋ฆฌ ์›์น™)

  • ํ•œ ํด๋ž˜์Šค๋Š” ์ž์‹ ์ด ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋Š” ๊ตฌํ˜„ํ•˜์ง€ ๋ง์•„์•ผ ํ•œ๋‹ค. ํ•˜๋‚˜์˜ ์ผ๋ฐ˜์ ์ธ ์ธํ„ฐํŽ˜์ด์Šค๋ณด๋‹ค๋Š”, ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๊ตฌ์ฒด์ ์ธ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ๋‚ซ๋‹ค.

Dependency Inversion Principle (์˜์กด ์—ญ์ „ ์›์น™)

  • ์˜์กด ๊ด€๊ณ„๋ฅผ ๋งบ์„ ๋•Œ, ๋ณ€ํ™”ํ•˜๊ธฐ ์‰ฌ์šด๊ฒƒ ๋ณด๋‹จ ๋ณ€ํ™”ํ•˜๊ธฐ ์–ด๋ ค์šด ๊ฒƒ์— ์˜์กดํ•ด์•ผ ํ•œ๋‹ค๋Š” ์›์น™์ด๋‹ค.
# DIP๋ฅผ ์ง€ํ‚ค์ง€ ๋ชปํ•œ ์˜ˆ์ œ
from abc import *
 
class EventStreamer():
    def __init__(self, parsed_data: str, client: Syslog):
        self.parsed_data = parsed_data
        assert client is Syslog, "Client is not Syslog"
        self.client = client
        
    def stream(self):
        self.client.send(self.parsed_data)    
        
class Syslog():
    def send(data: str):
        print(f"Syslog send: {data}")
        pass
    
class OtherClient():
    def send(data: str):
        print(f"OtherClient send: {data}")
        pass
 
 
streamer1 = EventStreamer("for Syslog data!", Syslog)
streamer1.stream()
streamer2 = EventStreamer("for OtherClient data!", OtherClient)
streamer2.stream()


์ถœ์ฒ˜: https://doorbw.tistory.com/240 [Tigercow.Door]

์ƒ์œ„ ํด๋ž˜์Šค์ธ EventStreamer์—์„œ Syslog๋ฅผ ์ •์˜๋ฅผ ํ•ด๋ฒ„๋ฆฌ๋ฉด ์•ˆ๋œ๋‹ค.

EventStreamer() ๋ผ๋Š” ๊ณ ์ˆ˜์ค€์˜ ๋ชจ๋“ˆ์ด ์ง์ ‘ Syslog๋ฅผ ์ฐธ์กฐํ•จ์œผ๋กœ์จ, Syslog์— ๋Œ€ํ•ด ์˜์กดํ•˜๊ณ  ์žˆ๋‹ค.

# DIP๋ฅผ ์ ์šฉํ•œ ์˜ˆ์ œ
from abc import *
 
class EventStreamer():
    def __init__(self, parsed_data: str, client):
        self.parsed_data = parsed_data
        assert client in DataTargetClient.__subclasses__(), "Client is not DataTargetClient"
        self.client = client
        
    def stream(self):
        self.client.send(self.parsed_data)
 
class DataTargetClient(metaclass=ABCMeta):
    """Interface: DataTargetClient class"""
    @abstractmethod
    def send(self, data: str):
        pass            
        
class Syslog(DataTargetClient):
    def send(data: str):
        print(f"Syslog send: {data}")
        pass
    
class OtherClient(DataTargetClient):
    def send(data: str):
        print(f"OtherClient send: {data}")
        pass
 
 
streamer1 = EventStreamer("for Syslog data!", Syslog)
streamer1.stream()
streamer2 = EventStreamer("for OtherClient data!", OtherClient)
streamer2.stream()


์ถœ์ฒ˜: https://doorbw.tistory.com/240 [Tigercow.Door]
profile
์ค‘์š”ํ•œ ๊ฒƒ์€ ์†๋ ฅ์ด ์•„๋‹ˆ๋ผ ๋ฐฉํ–ฅ์„ฑ, ๊ณต๋ถ€ํ•˜๋ฉฐ ๋ฉ”๋ชจ๋ฅผ ๋‚จ๊ธฐ๋Š” ๊ณต๊ฐ„์ž…๋‹ˆ๋‹ค.

0๊ฐœ์˜ ๋Œ“๊ธ€