데이터와 기능을 모두 포함한, 독립적으로 실행할 수 있는 객체가 있고,
각 객체들의 상호작용을 통하여 낮은 관계의 의존성과 높은 기능의 집중도를 지향하는 프로그래밍 설계 방법이다.
소프트웨어가 커지면서 함수와 데이터가 관리하기 힘들 정도로 늘어나고 문제 발생 시 해당 문제를 추적 하는데 어려움이 생김.
어려움이란?
데이터가 많아 지면서 해당 변수 이름을 선언하는 데 어려움이 생김
코드가 길어져 원하는 기능을 찾기 힘들다.
이러한 단점을 개선하기 위해 기능 단위로 객체를 나누어
데이터 관리 주체가 데이터를 소유한 객체로 지정되어 객체 간에 데이터를 의존하는 경우를 낮추는 객체 지향 언어가 탄생하였다.
SRP: 단일 책임 원칙
Single Responsibility Principle(SRP)
객체는 오직 하나의 책임을 가져야 한다. (객체는 오직 하나의 변경의 이유만을 가져야 한다.)
사칙연산 함수를 가지고 있는 계산 클래스가 있다고 치자. 이 상태의 계산 클래스는 오직 사칙연산 기능만을 책임진다. 만일 프로그램이 대대적으로 공사를 들어가게 되더라도 계산 클래스가 수정될만한 사유는 누가 봐도 사칙연산 함수와 관련된 문제뿐이다. 이처럼 단일 책임 원칙은 클래스의 목적을 명확히 함으로써 구조가 난잡해지거나 수정 사항이 불필요하게 넓게 퍼지는 것을 예방하고 기능을 명확히 분리할 수 있게 한다.
OCP: 개방-폐쇄 원칙
Open-Closed Principle(OCP)
객체는 확장에 대해서는 개방적이고 수정에 대해서는 폐쇄적이어야 한다는 원칙이다. 즉, 객체 기능의 확장을 허용하고 스스로의 변경은 피해야 한다.
LSP: 리스코프 치환 원칙
Liskov Substitution Principle(LSP)
자식 클래스는 언제나 자신의 부모 클래스를 대체할 수 있다는 원칙이다. 즉 부모 클래스가 들어갈 자리에 자식 클래스를 넣어도 계획대로 잘 작동해야 한다는 것. 상속의 본질인데, 이를 지키지 않으면 부모 클래스 본래의 의미가 변해서 is-a 관계가 망가져 다형성을 지킬 수 없게 된다.
ISP: 인터페이스 분리 원칙
Interface Segregation Principle(ISP)
클라이언트에서 사용하지 않는 메서드는 사용해선 안 된다. 그러므로 인터페이스를 다시 작게 나누어 만든다. OCP와 비슷한 느낌도 들지만 엄연히 다른 원칙이다. 하지만 ISP를 잘 지키면 OCP도 잘 지키게 될 확률이 비약적으로 증가한다. 정확히 말하자면 인터페이스의 SRP라고 할 수 있다.
DIP: 의존성 역전 원칙
Dependency Inversion Principle(DIP)
추상성이 높고 안정적인 고수준의 클래스는 구체적이고 불안정한 저수준의 클래스에 의존해서는 안 된다는 원칙으로서, 일반적으로 객체지향의 인터페이스를 통해서 이 원칙을 준수할 수 있게 된다. (상대적으로 고수준인) 클라이언트는 저수준의 클래스에서 추상화한 인터페이스만을 바라보기 때문에, 이 인터페이스를 구현한 클래스는 클라이언트에 어떤 변경도 없이 얼마든지 나중에 교체될 수 있다. (디자인 패턴 중 전략 패턴을 떠올리면 된다)
제어의 역전을 의미하는 Inversion of control(IoC)과 용어가 비슷하기에 혼동하기 쉬우나, 혼동해서는 안 된다. IoC는 제어의 흐름에 대한 개념이지만 DIP는 클래스 사이의 의존성에 대한 개념이다.
객체
객체는 사람이 인식할 수 있는 최소한의 의미( 어떤 의미를 전달 할수 있는 )를 가진 사물이다.
클래스
객체를 만들어 내기 위한 틀, 형식
속성
객체가 가지고 있는 고유 값 ( 데이터 )
속성은 외부로부터의 접근이 차단 되어야 한다 ( 캡슐화 )
메소드
클래스 안에서 사용되는 함수
객체 지향에서 행동은 메소드로 표현
객체가 자신 또는 다른 객체에게 제공하는 기능 ( 상호작용 )
추상화
관찰 대상(객체) 의 의미 있는 특징( 속성 )를 뽑아내는 작업
의미 있는 데이터들을 모아 더 큰 단위의 의미있는 정보로 만드는 작업
캡슐화
정보은닉 + 데이터랑 기능을 묶는것.
변수와 함수를 하나의 단위로 묶어 분리하여 틀 안에 숨기고 보호한다는 의미이다.
외부에서 속성을 건드리지 못하도록 속성을 숨기고 보호하는 기능을 한다.
( 게터 , 세터 )
getter , setter 사용 이유?
객체 안의 데이터를 다른 객체가 잘못 조작하는 것을 막기 위해 getter 메소드를 만들어 속성을 가져 올 수 만 있게 하고, 외부로 부터 속성값의 조작을 막거나 setter 메소드를 통해 제한적으로 조작하게 허용한다
보통 캡슐화와 정보 은닉을 묶어서 생각하는 경우가 많은데, 정보 은닉은 캡슐화로부터 파생된 보조 개념이지 '캡슐화 = 정보 은닉'은 아니다. Python은 정보 은닉을 지원하지 않지만 클래스를 통한 캡슐화를 지원하기 때문에 객체지향 언어로 분류된다.
정보 은닉
프로그램의 세부 구현을 외부로 드러나지 않도록 특정 모듈 내부로 감추는 것이다. 내부의 구현은 감추고 모듈 내에서의 응집도를 높이며, 외부로의 노출을 최소화하여 모듈 간의 결합도를 떨어뜨려 유연함과 유지보수성을 높이는 개념은 거의 모든 현대 프로그래밍 언어에 녹아 있다. 많은 객체 지향 언어에서 사용되는 클래스를 기준으로 보면, 클래스 외부에서는 바깥으로 노출된 특정 메소드에만 접근이 가능하며 클래스 내부에서 어떤 식으로 처리가 이루어지는지는 알지 못하도록 설계된다.
일반적으로 세 종류의 접근 제한이 사용된다.
public: 클래스의 외부에서 접근 가능
protected: 다른 클래스에게는 노출되지 않지만, 상속받은 자식 클래스는 접근 가능
private: 클래스의 내부에서만 사용되며 외부로 노출되지 않는다.
상속
상속은 자식 클래스가 부모 클래스의 특성과 기능을 그대로 물려받는 것을 말한다.
상속을 통해 부모의 속성과 기능을 중복 없이 재 사용 할 수 있다.
기능의 일부분을 변경해야 할 경우 자식 클래스에서 상속받은 그 기능만을 수정해서 다시 정의하게 되는데, 이러한 작업을 '오버라이딩'이라고 한다. 상속은 캡슐화를 유지하면서도 클래스의 재사용이 용이하도록 해 준다.
다형성
하나의 변수, 또는 함수가 상황에 따라 다른 의미로 해석될 수 있는 것을 말한다.
오버로딩 : 같은 이름의 함수가 받는 파라미터의 데이터 타입으로 다른 로직을 실행.
오버라이딩 :
같은 클래스의 동일한 메소드를 호출할 때 자식 클래스들이 저마다 다른 로직을 수행하고 리턴하는 것
부모에게 상속받은 함수를 다른 로직으로 확장 및 재정의