객체지향 프로그래밍 OOP (Object Oriented Programing)
객체지향 프로그래밍은 컴퓨터 프로그램을 명령어의 목록으로 보는 시각에서 벗어나 여러 개의 객체들이 서로 협력하는 형태의 구조를 지향한다
객체는 각자 맡은 책임이 있고, 서로 메시지를 주고받으면서 협력한다.
객체지향의 핵심
역할과 책임 협력은 객체지향의 핵심이다.
객체들이 애플리케이션의 기능을 구현하기 위해 수행하는 상호작용을 협력이라고 한다.
그리고 객체가 협력에 참여하기 위해 수행하는 로직을 책임이라고 부른다.
객체들이 협력 안에서 수행하는 책임들이 모여 객체가 수행하는 역할을 구성한다.
협력
두 객체의 협력에 사용되는 유일한 수단은 메시지 전송이다.
다른 객체에게 도움을 요청하기 위해 메시지를 전송하고, 메시지를 수신한 객체는 메시지를 처리하는 데 적합한 메서드를 자율적으로 선택한다.
책임
협력에 참여하기 위해 객체가 수행하는 행동을 책임이라고 한다.
객체가 책임을 수행하는 방법을 메서드라고 부른다.
책임 주도 설계(Responsibility-Driven Design)란 책임을 수행할 적절한 객체를 찾아 책임을 할당하는 방식으로 설계하는 방법이다.
역할
객체가 어떤 특정한 협력 안에서 수행하는 책임의 집합을 역할이라고 한다.
역할을 통해서 유연하고 재사용 가능한 협력을 얻을 수 있다.
역할은 다른 것으로 교체할 수 있는 책임의 집합이다.
때문에 특정 역할을 수행할 수 있는 객체라면 어떠한 객체로 바꿔 끼워도 상관 없다.
객체지향 특징
추상화
추상화란 객체들의 공통된 특징을 파악하여 하나의 개념으로 다루는 것이다.
객체지향 프로그래밍에서는 추상화를 통해 클래스를 정의하고 객체를 생성한다.
상속
상속은 새로운 클래스가 기존의 클래스의 자료와 연산을 이용할 수 있게 하는 기능이다.
상속을 통해 새로운 클래스를 작성할 때 기존 클래스의 자료와 연산을 수정하지 않고도 확장할 수 있다.
기존의 클래스를 상위 클래스, 부모 클래스라 부르며, 상속을 받는 클래스를 하위 클래스, 자식 클래스, 파생 클래스라 부른다.
상속을 통해 기존의 클래스를 상속받은 하위 클래스를 이용해 프로그램의 요구에 맞추어 클래스를 수정할 수 있고, 클래스 간의 종속 관계를 형성함으로써 객체를 조직화할 수 있다.
다형성
다형성은 하나의 변수명, 함수명 등이 상황에 따라 다른 의미로 해석될 수 있는 것을 의미한다.
일반적으로 오버라이딩과 오버로딩을 말한다.
오버로딩(Overloading)은 같은 이름의 메서드가 매개변수의 타입과 개수를 다르게 하여 정의하는 것을 의미한다.
오버라이딩(Overriding)은 상위 클래스가 가지고 있는 메서드를 하위 클래스가 재정의하여 사용하는 것을 의미한다.
@Override
어노테이션은 컴파일러에게 해당 메서드가 오버라이딩된 메서드임을 알려준다. 만약 해당 메서드가 오버라이딩된 메서드가 아니라면 컴파일 에러를 발생시킨다. 따라서@Override
어노테이션을 사용하면 코드의 가독성을 높이고, 버그를 방지할 수 있다.
캡슐화
캡슐화는 객체의 상태와 행동을 객체 안에 모으고, 내부 구현을 외부로부터 감추는 것을 말한다.
다시말해 외부에서 알 필요가 없는 부분을 감춤으로써 대상을 단순화하는 추상화의 한 종류다.
접근 제어자를
private
으로 설정하여 캡슐화를 구현할 수 있다.
응집도
응집도는 모듈에 포함된 내부 요소들이 연관돼 있는 정도를 나타낸다.
모듈 내의 요소들이 하나의 목적을 위해 긴밀하게 협력한다면 그 모듈은 높은 응집도를 가진다.
모듈 내의 요소들이 서로 다른 목적을 추구한다면 그 모듈은 낮은 응집도를 가진다.
하나의 변경을 수용하기 위해 다수의 모듈이 함께 변경돼야 한다면 응집도가 낮은 것이고, 하나의 모듈만 변경된다면 응집도가 높은 것이다.
결합도
결합도는 의존성의 정도를 나타내며 다른 모듈에 대해 얼마나 많은 지식을 갖고 있는지를 나타내는 척도이다.
어떤 모듈이 다른 모듈에 대해 너무 자세한 부분까지 알고 있다면 두 모듈은 높은 결합도를 가진다.
어떤 모듈이 다른 모듈에 대해 꼭 필요한 지식만 알고 있다면 두 모듈은 낮은 결합도를 가진다.
결합도가 높으면 함께 변경해야 하는 모듈의 수가 늘어나기에 변경이 어려워진다.
OOP에서 코드의 설계, 유지보수성, 확장성, 이해 용이성을 높이기 위한 5가지 설계 원칙
(S) 단일 책임 원칙 - Single Responsibility Principal
하나의 클래스는 하나의 책임(핵심기능)만을 가져야 한다.
SRP을 준수할 경우 영향 범위가 축소 되고 변경으로 인한 오류 가능성이 감소한다.
(O) 개방 폐쇄 원칙 - Open-Closed Principal
새로운 기능에는 열려 있어야 하지만, 기존 기능의 변경에는 닫혀 있어야 한다.
기존 코드를 변경하여 새로운 기능을 추가할 경우 코드의 안정성이 떨어지므로 기존 코드를 변경하지 않고 새로운 코드를 작성해야 한다.
(L) 리스코프 치환 원칙 - Liskov Substitution Principal
부모 클래스의 인스턴스가 자식 클래스의 인스턴스로 대체될 수 있어야 한다.
상속 관계에서 부모 클래스와 자식 클래스의 기능 호환성을 의미한다.
(I) 인터페이스 분리 원칙 - Interface Segregation Principal ###
클라이언트는 자신이 필요한 최소한의 인터페이스만 사용해야 한다.
하나에 인터페이스에 많은 기능을 포함할 경우 코드의 결합도가 높아지므로 기능 별로 인터페이스를 정의하고, 필요한 기능만 인터페이스로 제공한다.
(D) 의존 역전 원칙 - Dependency Inversion Principal
객체는 구체적인 객체가 아닌 추상화에 의존해야 한다.
예시 - Dependency Injection - Constructor에 Interface를 Parameter로 받는 경우
객체지향의 장단점
장점
변경에 유연하게 대응할 수 있는 구조를 갖게 되어 유지보수하기 쉬워진다
코드의 재사용성을 높일 수 있다.
객체 단위로 코드를 작성하기 때문에 디버깅이 쉽다.
단점
프로그램이 커질 경우 객체간의 관계를 파악하기 어렵다.
규모가 작은 프로젝트에서 객체지향을 적용한다면 오히려 가독성을 저하시킬 수 있다.
추상화와 다형성 등의 기능을 제공하기 위해 런타임에서 추가적인 작업을 수행해야 하기에 일부 경우에는 성능 오버헤드가 발생할 수 있다.
ref
Last updated