SOLID(객체 지향 설계) 원칙

임동혁 Ldhbenecia·2023년 4월 18일
0

Design Pattern

목록 보기
2/6
post-thumbnail

디자인 패턴은 클래스 라이브러리가 아니다.

  • 디자인 패턴의 목표는 프로그램을 재활용 하는 것이다.
    • 즉 어떻게 '부품'으로써 재이용할 수 있는가를 생각해야 한다.
    • 기능을 확장, 변경할 수 있어야 한다.


이러한 23개의 패턴이 디자인 패턴의 거의 전부이다.

디자인 패턴에는 SOLID라는 5개의 객체 지향 설계 원칙이 존재한다.
현재 디자인 패턴을 공부하면서 알게된 내용을 정리하려한다.

SOLID Principles

Single Responsibility(SRP) 단일 책임 원칙

  • 모든 클래스는 하나의 책임만 가져야 한다.
  • 클래스를 구현할 때 한 가지 기능에만 중점을 둬야 한다.
  • 두 가지 이상은 클래스를 나누어야 한다.
  • 한 개의 클래스에 여러 가지 기능이 있다면 관련된 모든 클래스를 수정해야하는 상황이 발생할 수 있다.
  • Print 마저도 나누어야 한다.
def add(num1, num2):
	return num1 + num2
   
def numPrint(num):
	print(num)

이 코드의 경우 SRP 원칙을 잘 따른 코드이다.

def addPrint(num1, num2):
	num = num1 + num2
	print(num)
    return num

이 코드는 SRP 원칙을 따르지 않은 코드이다.

  • Add 함수는 덧셈을 하는 것에만 책임을 다해야 한다.
  • numPrint 함수는 숫자를 출력하는 것에만 책임을 다해야 한다.
  • addPrint 함수는 SRP 원칙을 따르지 않았다.

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

  • 소프트웨어 개체(클래스, 모듈, 함수 등)는 확장에 대해 열려있어야 하고 수정에 대해서는 닫혀있어야 한다.
  • 확장에 대해서 개방
  • 수정에 대해서 폐쇄
class Animal:
	def __init__(self, a_type):
    	self.a_type = a_type

def hey(animal:Animal):
	if animal.a_type == "Cat":
    	print("meow")
    elif animal.a_type == "Dog":
    	print("bark")
    else:
    	raise Error("Wrong a type")
   
# Cow, Sheep

kitty = Animal("Cat")
bingo = Animal("Dog")
cow = Animal("Cow")
sheep = Animal("Sheep")

hey(cow)
hey(sheep)

기능이 추가될 때마다 수정한 코드

class Animal:
	def speak(self):
    	pass

class Cat(Animal):
	def speak(self):
    	print("meow")

class Dog(Animal):
	def speak(self):
    	print("bark")

class Sheep(Animal):
	def speak(self):
    	print("meh")

class Cow(Animal):
	def speak(self):
    	print("moo")
   
def hey(animal:Animal):
	animal.speak()

cow = Cow()
sheep = Sheep()

hey(cow)
hey(sheep)

기능이 추가되어도 수정이 필요하지 않은 코드

Dependency Inversion Principle - 의존관계 역전 원칙

  • 소프트웨어 모듈들을 분리하는 특정 형식을 지칭
  • 상위 계층이 하위 계층에 의존하는 전통적인 의존관계를 반전(역전)시킴으로써 상위 계층이 하위 계층의 구현으로부터 독립되게 할 수 있다.
class Cat(Animal):
	def speak(self):
    	print("meow")

class Dog(Animal):
	def speak(self):
    	print("bark")
        
class Zoo:
	def __init__(self):
    	self.cat = Cat()
        self.dog = Dog()

이 코드에서 동물을 추가할 시

class Cat:
	def speak(self):
    	print("meow")

class Dog:
	def speak(self):
    	print("bark")

class Sheep:
	def speak(self):
    	pass

class Cow:
	def speak(self):
    	pass
        
class Zoo:
	def __init__(self):
    	self.cat = Cat()
        self.dog = Dog()
        self.sheep = Sheep()
        slef.cow = Cow()
  • Zoo 클래스는 Cat, Dog 클래스들에 의존관계가 있다.
    동물들이 늘어나면 Zoo 클래스를 계속 수정해야 한다.
  • Zoo가 상위, Cat, Dog 등등이 하위임에도 불구하고 의존성이 있다.
    이럴 경우 확장성을 고려하면 불편해질 수 있다.
class Animal:
	def speak(self):
		pass

class Cat(Animal):
	def speak(self):
    	print("meow")

class Dog(Animal):
	def speak(self):
    	print("bark")
        
class Zoo:
	def __init__(self):
    	self.animals = []
        
    def addAnimal(self, animal:Animal):
    	self.animals.append(animal)
        
    def speakAll(self):
    	for animal in self.animals:
        	animal.speak()
  • 의존관계가 Animal 추상클래스에 의해 역전되었고, Zoo 클래스는 각 Cat, Dog 등의 클래스로부터 독립되었다.
  • 각 동물들에 의한 추상클래스 Animal을 만든드ㅏ.
  • Zoo라는 동물원이 이번엔 Animal을 통해 처리할 수 있게 되었다.
  • Zoo가 Animal에 의존성이 걸려있다. Zoo -> Animal
  • 각각의 동물들 Cat, Dog도 Animal에 의존성이 걸려있다.
    -> 사자, 호랑이 등 아무리 추가해도 이 Animal에만 연관되고 Zoo라는 연관성이 없어진다.
    -> Zoo는 독립적으로 되었다.

    상위 -> 하위에서 의존성을 가지고 있던 것을
    하위 -> 상위로 의존성을 역전시킴(의존성을 떨어뜨림)

Liskov Substitution Principle - 리스코프 치환 법칙

  • 하위 객체는 상위 객체에서 가능한 행위를 수행할 수 있어야 한다.
    → 즉, 상위 타입 객체를 하위 타입 객체로 치환해도 정상적으로 동작해야 함
class Cat: # Cat 클래스(상위클래스)
	def speak(self):
    	print("meow")

class BlackCat(Cat): # BlackCat 클래스(Cat의 하위 클래스)
	def speak(self):
    	print("black meow")
        
def speak(cat:Cat): # Cat 타입에 맞는 함수가 있음
	cat.speak()
    
cat = Cat()
speak(cat) # meow

cat = BlackCat()
speak(cat) # black meow

class Fish(Cat): # Fish 클래스 (Cat의 하위 클래스)
	def speak(self):
    	raise Exception("Fish cannot speak")
        
Cat = Fish() # Error(상위 클래스의 타입으로 대체 x)
speak(cat)

Interface Segregation - 인터페이스 분리 원칙

  • 클라이언트가 자신이 이용하지 않는 메서드에 의존하지 않아야 한다는 원칙
    • 큰 덩어리의 인터페이스들을 구체적이고 작은 단위들로 분리시킴으로써 클라이언트들이 꼭 필요한 메서드들만 이용할 수 있도록 해야 함
profile
지극히 평범한 공대생

0개의 댓글