[책][클린코드] SOLID 원칙

Peter·2021년 10월 5일
0

클린코드

목록 보기
2/4

SOLID

객체지향 설계의 5가지 원칙

  • SRP: 단일 책임 원칙
  • OCP: 개방, 폐쇄 원칙
  • LSP: 리스코프 치환 원칙
  • ISP: 인터페이스 분리 원칙
  • DIP: 의존성 역전 원칙

SRP 단일 책임 원칙(Single Responsibility Principle)

스마트폰은 계산기, 전화기, 인터넷컴퓨터, 메모장의 기능을 가지고 있다.
단일 책임 원칙을 적용하자면 계산기로써 책임, 전화기로써 책임, 인터넷컴퓨터로써 책임, 메모장으로써 책임이 분리된다.

  • 클래스는 하나의 기능만 가여야 하며, 클래스는 변경하려는 단 하나 이유만을 가져야 한다.
  • 하나의 기능만 담당하는 클래스는 변경시 벌어지는 연쇄작용에서 자유로워짐
  • 가독성이 향상되고 유지보수가 용이

OCP 개방-폐쇄 원칙(Open Closed Principle)

웹개발자가 한명 있다. 회사에 마케팅 인력이 부족하다. 웹 개발자를 마케팅 인력으로 사용하기 위해 마케팅 연수를 보내 마케팅 인력으로 변경했다
(OCP 위반)

웹개발자가 한명 있다. 회사에 인공지능 인력이 필요하다. 웹 개발자가 이미 다루고 있는 언어를 고려해 해당 언어로 만들어진 인공지능 툴에 대한 연수를 보내 인공지능 인력으로 변경했다.
(OCP 충족)

  • 원래 담당하기로 했던 기능에서 다른 기능으로 변경을 줄여야 한다.
  • 가지고 있던 기능의 확장은 적극 권장된다
  • 변경사항이 발생할 경우 가능한 원래 있던 요소들의 변경 없이 확장으로 변경사항을 반영한다
  • 객체지향의 추상화와 다형성을 활용한다.

LSP 리스코프 치환 원칙(Liskov Substitution Principle)

식칼을 만드는 장인 부모 밑에서 태어나 가업을 물려받은 자식이 칼을 만들지 못하고 망치를 만들고 있다
(LSP 위반)

식칼을 만드는 장인 부모 밑에서 태어나 가업을 물려받은 자식은 전투용 칼을 만들어 낸다
(LSP충족)

  • 서브 타입은 기반 타입이 약속한 규약(접근제한자, 예외 포함)을 지켜야 한다.
  • 클래스 상속, 인터페이스 상속을 이용해 확장성을 획득한다.
  • 다형성과 확장성을 극대화하기 위해 인터페이스를 사용하는 것이 더 좋다
  • 합성(composition)을 이용할 수도 있다.

ISP 인터페이스 분리 원칙(Interface Segregation Principle)

건설회사가 병원을 건축하는데 본관, 입원병동, 연구병동 세가지 시설을 건축해야 한다. 각 건물에 해당하는 표준 설계도 3개를 모아서 각 건물을 담당하는 담당자에게 모두 줬다.
(ISP 위반)

건설회사가 병원을 건축하는데 본관, 입원병동, 연구병동 세가지 시설을 건축해야 한다. 각 건물에 해당하는 표준 설계도 3개를 맡은 건물에 맞게 건축 담당자에게 한개씩 분배했다.
(ISP 충족)

  • 가능한 최소한으로 인터페이스를 구축
  • 클래스를 이용하는 많은 클라이언트가 클래스의 특정 부분만 이용한다면 한개의 인터페이스를 사용하는 부분만 활용하도록 여러개의 인터페이스로 분류해 클라이언트가 필요해하는 기능만 전달한다.
  • SRP가 클래스의 단일 책임이라면, ISP는 인터페이스의 단일 책임

DIP 의존성 역전 원칙(Dependency Inversion Principle)

이제 개발을 막 시작한 개발자에게 특정 프레임워크에 특정 기능 사용하는 방법을 알려줬다.
(DIP 위반)

이제 개발을 막 시작한 개발자에게 클래스와 함수의 구동 원리, 프레임워크 동작방식을 파악하는 방법을 알려주고 어떤 기능 구현을 담당하게 했다.
이 개발자는 프레임워크 종류와 상관없이 어떤 프레임워크등 같은 기능을 구현해냈다.
(DIP 충족)

  • 하위 모델의 변경이 상위 모듈의 변경을 요구하는 위계관계를 끊는다.
  • 실제 사용관계는 그대로지만, 추상화를 매개로 메세지를 주고 받으면서 관계를 느슨하게 만든다
class User:
	def __init__(self, hp, mp, damage):
    	self.hp = hp
        self.mp = mp
        self.damage = damage
        
    def get_damage(self):
    	return self.damage
    
    def attack(self, enemy):
    	self.hp = self.hp - enemy.get_damage()
        
    def attacked(self, damage):
    	self.hp = self.hp - damage
    
    def get_status(self):
    	return f"hp:{self.hp}, mp:{self.mp}, damage:{self.damage}"
        
 redteam = User(100, 100, 50)
 blueteam = User(100, 100, 30)
 
 readteam.attack(blueteam)
 
 print(bludteam.get_status()
 
 # result: "hp:50, mp:100, damage:30"
  • 확장에 유연하지 않음
class User:
	def __init__(self, hp, mp, damage):
    	self.hp = hp
        self.mp = mp
        self.damage = damage
    
    def attacked(self, damage):
    	self.hp = self.hp - damage
    
    def get_damage(self):
    	return self.damage
    
    def get_status(self):
    	return f"hp:{self.hp}, mp:{self.mp}, damage:{self.damage}"

class Battle:
	def __init__(self, attacker, target)
    	self.attacker = attacker
        self.target = target
        
    def fight(self, attacker, target):
    	damage = attacker.get_damage()
        target.attacked(damage)
        
 redteam = User(100,100,50)
 blueteam = User(100,100,30)
 
 Battle.fight(redteam, blueteam)
 
 print(redteam.get_status(), blueteam.get_status())
 
 # result: "hp:100, mp:100, damage:50", "hp:50, mp:100, damage:30"
  • User 클래스 끼리 말고 내부 설계가 다른 클래스끼리도 Battle클래스를 통해서 기능을 수행할 수 있음
profile
컴퓨터가 좋아

0개의 댓글