객체지향프로그래밍(OOP, Object-Oriented Programming)은 컴퓨터 프로그래밍의 패러다임 중 하나로, 기존의 컴퓨터 프로그램을 명령어의 목록으로 보는 시각에서 벗어나 여러개의 독립된 단위인 객체의 모임으로 파악하고자 하는 것이다.
데이터를 추상화시켜 상태와 행위를 가진 객체를 만들고, 객체들 간의 유기적인 상호작용을 통해 로직을 구성하는 프로그램 방법이다.
Java, C++, Python, C#, Kotlin 등이 대표적인 객체지향 프로그래밍 언어이다.
객체지향프로그래밍에서 객체(Object)란 상태(속성)와 행동(메서드)을 가지는 독립적인 객체를 의미한다.
객체는 Class라는 틀을 기반으로 생성된다.
// 클래스 정의 (설계도)
class Car {
String color; // 속성(상태)
void drive() { //행동(메서드)
System.out.println(color + " 차가 달립니다.");
}
}
public class Main {
public static void main(String[] args) {
// 객체 생성 (실체화)
Car myCar = new Car(); // 객체 생성
myCar.color = "빨간색"; // 속성 값 설정
myCar.drive(); // "빨간색 차가 달립니다." 출력
}
}
이전 패러다임인 절차지향(Procedural) 프로그래밍의 한계로 인해 등장하게 되었다.
데이터를 중심으로 순차적으로 실행하는 구조로 함수를 사용하여 코드의 재사용성을 높이려 하였으나, 프로그램이 커질수록 코드의 복잡도가 증가하여 유지보수가 어려워졌다.
데이터와 함수가 따로 존재하여 긴밀하게 연결되지 못했으며, 하나의 데이터구조가 변경되면 관련된 모든 함수가 수정되어야 했다.
여러기능을 구현하다보면 비슷한 코드를 반복작성해야 했고, 규모가 커질수록 유지보수가 어려워졌다.
새로운기능을 추가하면 기존 코드들이 수정되어야 했고, 변경이 발생했을때 기존 코드에 많은 영향을 주어 확장성에 문제가 생겼다.
절차지향 프로그래밍언어로는 C언어가 대표적이다.
객체지향 프로그래밍의 가장 큰 4가지 특징은 다음과 같다.
객체 내부의 데이터를 외부에서 직접 접근하지 못하도록 보호하는 것이다.
private, protected 등의 접근 제한자를 사용해 데이터를 은닉하고, getter, setter 등의 메서드를 통해 접근 할 수 있게 한다.
객체 내부에서 관련된 데이터와 메서드를 함께 묶어서 사용하기 때문에 응집도가 증가한다.
외부에서 객체 내부 구현을 알 필요 없이 제공된 메서드(인터페이스)만 사용하면 되기 때문에 결합도가 감소한다.
public: 모든 접근을 허용한다
protected: 같은 패키지에 있는 객체와 상속관계인 객체들을 허용한다
default: 같은 패키지 내의 객체들만 허용한다
private: 현제 객체 내에서만 허용한다
기존 클래스(부모 클래스)의 속성과 메서드를 새로운 클래스(자식 클래스)가 물려받는 개념이다.
코드 재사용성이 증가하고, 유지보수가 쉬워진다.
공통기능을 부모 클래스에 모아두고, 각 클래스가 자신의 역할에 집중하도록 설계 할 수 있어 응집도가 증가한다.
부모 클래스가 변경되면 자식 클래스도 영향을 받기 때문에 잘못된 상속 구조는 결합도를 높힐 수 있다.
같은 메서드 호출이지만, 객체에 땨라 다르게 동작하는 개념이다.
메서드 오버라이딩과 메서드 오버로딩이 대표적인 예시이다.
객체별로 적절한 기능을 할당하여 역할을 분리 할 수 있어 응집도가 증가하며, 유지보수시 클래스간 영향을 최소화 할 수 있어 결합도가 감소한다.
오버로딩은 객체의 메서드명은 같지만 매개변수의 타입이나 개수가 다른경우이다.
오버라이딩은 부모 클래스의 상속받은 메서드를 자식 클래스에서 재정의 하는 것을 말한다. 메서드의 이름, 매게변수, 리턴값이 모두 같아야 한다.
객체의 필수적인 부분만 노출하고, 불필요한 세부 사항은 숨기는 개념이다.
공통의 개념을 추상화로 구현해두고, 각각의 객체는 이를 상속받아 각자의 기능을 구현한다.
주로 추상클래스 (Absctraction Class)나 인터페이스(Interface)를 통해 구현한다.
공통적인 기능을 추상화하여 하위 클래스에서 일관적인 동작을 구현할 수 있어 응집도가 증가한다.
추상 클래스를 통해 구현과 상호작용을 분리 할 수 있어 결합도가 감소한다.
상속과 다형성을 활용하여 기존 코드를 재사용 할 수 있다.
중복코드가 감소하며, 유지보수시 변경사항을 최소화 할 수 있다.
캡슐화와 추상화를 통해 모듈별 수정이 용이하여 한 부분이 변경되도, 다른 부분에 영향을 덜 주게되어 버그 발생확률이 줄어든다.
복잡한 시스템을 작은 객체 단위로 관리 할 수 있다.
인터페이스와 다형성으로 인해 새로운 기능을 쉽게 추가 할 수 있어, 기존코드 수정없이 새로운 클래스를 추가 할 수 있다.
실재의 개념을 코드로 표현하기 쉬워 유지보수시 이해하기 쉽고 논리적인 코드 구조를 갖는다.
객체 단위로 분리되어 가독성이 좋아진다.
코드의 유연성을 높이지만, 절차지향프로그래밍 보다 속도가느릴 수 있다.
객체를 생성하고 메서드를 호출하는 과정에서 메모리 사용량이 증가한다.
불필요한 객체 생성을 최소화하고, 메소드 호출을 줄이는 것이 좋다.
초보자가 개념을 익히는데 시간이 걸리고, 구조가 복잡해질수록 객체지향프로그래밍의 장점보다 단점이 커질 수 있다.
절차지향방식에 비해서 코드가 복잡하고 길어질 수 있다.
단순한 기능의 경우 다른 방식이 적절 할 수 있다.
잘못된 상속구조를 설계하면 유지보수가 더 어려워지며, 결합도가 높아지면 변경할 때 여러 곳을 수정해야 한다.