-
Python: 견고한 객체지향 프로그래밍 SOLID 설계원칙 공부하기!Python 2020. 8. 16. 21:26
SOLID 원칙이란?
Solid 원칙이란 로버트 마틴이 개발한 객체 지향 프로그래밍 및 설계의 다섯가지 원칙을 말합니다.
프로그래머가 시간이 지나도 유지보수와 확장이 쉬운 시스템을 만들고자할 때 이 원칙들을
함께 적용할 수 있습니다.
Solid 원칙은 각 원칙들의 첫 글자를 따서 만들어졌는데
다음과 같이 나뉩니다.
Single responsibility principle 단일 책임 원칙 Open/closed principle 개방-폐쇄 원칙 Liskov substitution principle 리스코프 치환 원칙 Interface segregation principle 인터페이스 분리 원칙 Dependency inversion principle 의존관계 역전 원칙 순서대로 알아봅시다.
1. 단일 책임 원칙(Single responsibility principle)
모든 클래스는 단 한 가지의 책임만을 갖고, 클래스 안에 정의되어 있는 모든 기능은
이 하나의 책임을 수행하는데 집중되어 있어야 합니다.
쉽게 말하면 "하나의 클래스로 한가지 일만 하자!" 입니다.
예를 들어 Ship이라는 배 클래스가 하나 있다고 생각해 봅시다.
배를 구성하는 것들은 아주 많습니다. 배의 엔진, 배에 필요한 물자, 인력, 손님등
엄청나게 많습니다. 이렇게 수많은 요소들을 Ship이라는 클래스안에 모두 넣을 수는 있습니다.
하지만 어떤 문제가 발생해 클래스 전체를 뜯어 유지 보수하기 보다
Ship 이라는 클래스안에 배의 엔진을 담당하는 Engine 클래스, 물자를 담당하는 Material
손님을 담당하는 Guest등을 만들어 각각의 클래스 책임을 부여하는 방식으로 만든다면
변경사항에 대해서만 코드를 수정하게 되는 좋은 구조로 만들 수 있을 것입니다.
단일 책임 원칙을 사용하기 위해서는 어떠한 책임을 기준으로 나누어야 할지 생각해야 합니다.
책임은 수정되어야 할 이유를 기준으로 범위를 정하면 단일 책임 원칙을 잘 지킬 수 있습니다.
2. 개방 폐쇄 원칙(Open/closed principle)
무엇이 개방, 무엇이 폐쇄일까요?
"클래스는 확장에 열려 있어야 하며, 수정에는 닫혀 있어야 한다" 입니다.
더 쉽게 말하면 코드를 수정하지 않아도 기존의 기능을 확장할 수 있어야 합니다.
다형성과도 연관이 되어 있는데요.
다형성을 가진 객체 지향 프로그래밍을 하기 위해서 추상 클래스를 이용했었습니다.
모듈들은 고정된 추상화에 의존하기 때문에 수정에 대해서 닫혀 있을 뿐만 아니라
추상 클래스의 새 파생 클래스를 만드는 것을 통해서도 확장이 가능합니다.
이렇게 추상화는 개방 폐쇄 원칙의 핵심 요소라고 할 수 있습니다.
3. 리스코프 치환 원칙(Liskov substitution principle)
"프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다." 입니다.
쉽게 말하면 부모 클래스의 인스턴스를 사용하는 위치에 자식 클래스의 인스턴스를
대신 사용했을때 코드가 원래 의도대로 동작하면 됩니다.
애초에 자식 클래스의 인스턴스는 부모 클래스의 인스턴스이기 때문에
변수나 메소드를 그냥 물려 받으면 문제가 없지만
만약 변수나 메소드를 잘못 오버라이딩한다면 문제가 생길 수 있습니다.
예를 들어, 자식 클래스가 부모 클래스 메소드의 파라미터 갯수, 리턴 타입
또는 부모 클래스의 변수 타입을 바꾸면 이러한 문제가 발생하겠죠?
또한 자식 클래스가 부모 클래스의 의도와는 다르게 메소드를 오버라이딩하는
경우도 생기는데 이러한 부분들은 협업에서 아주 중요하게 여겨야할 부분입니다.
4. 인터페이스 분리원칙(Interface segregation principle)
“특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다."입니다.
인터페이스란 추상 메소드로만 이루어진 추상 클래스를 의미합니다.
그렇다면 여러개의 인터페이스를 상속 받으면 다중상속인데 전에 다중상속은 권하지 않는다고
제가 포스팅했었는데 왜 갑자기 말이 바뀌냐?
좀 더 자세히 말씀드리면 인터페이스 다중 상속의 경우, 반드시 필요한 추상 메소드들만
오버라이딩하여 compact한 클래스를 만들 수 있기 때문입니다.
인터페이스를 분리하지 않고 사용한다면 불필요한 메소드를 포함한 모듈이 만들어질 수도 있으며
이렇게 너무 많은 메소드를 포함한 인터페이스를 뚱뚱한 인터페이스가 됩니다.
그래서 인터페이스를 기능과 역할을 기준으로 나누고 필요한 인터페이스만 상속 받아 사용하는 것이
바람직한 프로그래밍 원칙입니다.
5. 의존 관계 역전 원칙(Dependency inversion principle)
“추상화에 의존해야지, 구체화에 의존하면 안된다.”입니다. 말이 너무 어려운 것 같습니다.
"상위 모듈은 하위 모듈의 구현 내용에 의존하면 안 된다"라고 생각합시다
여기서 상위 모듈은 다른 클래스를 사용하는 주된 클래스,
하위 모듈은 사용되는 클래스라고 생각하시면 됩니다.
게임을 예로 들겠습니다. 게임 캐릭터는 총이라는 무기를 사용합니다.
여기서 총은 사용되는 인스턴스이기 때문에 하위 모듈 인스턴스,
캐릭터는 총이라는 인스턴스를 사용하는 상위 모듈 인스턴스라고 할 수 있습니다.
총 = 하위모듈 인스턴스
캐릭터 = 상위모듈 인스턴스
그렇다면 이제는 이렇게 생각할 수 있을겁니다.
"캐릭터는 총의 구현내용에 의존하면 안된다." 라고요
그리고 총에 공격하는 메소드인 shot()이 있다고 생각해봅시다 .
그리고 캐릭터 클래스안에서 총을 파라미터로 받을것을 예상하고
캐릭터안 클래스안에 미리 총의 shot이라는 메소드를 사용한 경우를 "캐릭터는 총의 구현내용에 의존한다."
라고 할 수 있겠네요. 이제 이렇게 되면 문제가 생깁니다.
캐릭터가 총만 계속 사용한다면 문제는 없겠지만 만약 검이나 몽둥이 같이 다른
무기를 사용한다면 캐릭터 클래스 안에 미리 지정해 놓은 총의 shot 메소드는 의미가
없어질 뿐더러 공격을 할 수도 없네요.
이에 대한 해결책은 처음부터 Weapon이라는 무기 추상 클래스를 만들어
처음부터 사용할 메소드이름을 고정하고 상위 모듈과 하위 모듈 사이에
추상화 레이어를 만드는 것입니다. Weapon 추상 클래스에 처음부터
use_on이라는 공격 메소드를 만들어 놓은 후, 검이든 총이든 Weapon을
상속받아 use_on이라는 메소드를 사용한다면 캐릭터 클래스에 use_on 메소드를
사용해도 문제가 없겠네요.
정리하면 추상클래스로 상위 모듈과 하위 모듈사이에 추상화
레이어를 만드는것이라 할 수 있겠네요.※ 코드잇과 위키피디아를 참고했습니다. ^^
반응형'Python' 카테고리의 다른 글
Python: 파일 입출력 (읽기, 쓰기, 복사, 암호화 및 복호화) 참고 (0) 2021.03.19 Python: 리스트, 문자열, 제어문, 튜플, 딕셔너리 참고 (0) 2021.03.12 Python: 객체 지향 프로그래밍을 위한 4가지 기본개념 (상속, 추상화, 캡슐화, 다형성)2 (0) 2020.08.16 Python: 객체 지향 프로그래밍을 위한 4가지 기본개념 (상속, 추상화, 캡슐화, 다형성)1 (0) 2020.08.16 Python: 파이썬 데코레이터(Decorator) 공부하기! (0) 2020.08.15