컴퓨터 프로그래밍 패러다임 중 하나로, 기존 프로그램을 명령어들의 모임으로 봤던 시각에서 벗어나, 여러개의 독립된 단위인 객체들의 모임으로 바라보고자 하는 것이다.
💡 객체란?
프로그램에서 사용되는 데이터 또는 식별자에 의해 참조되는 저장공간을 의미하며, 값을 저장할 변수와 작업을 수행할 메서드를 연관된 것끼리 묶어놓은 것을 말한다.
객체 지향 프로그래밍에서의 추상화는 객체의 공통적인 속성과 특성을 모아서 정의하는 것을 의미한다. 예를 들어 아이폰과 갤럭시는 모두 전화, 메시지, 인터넷이라는 공통 기능이 있다. 이러한 공통점을 이용해 우리는 전화와 메시지, 인터넷이 가능한 객체를 스마트폰
이라고 추상화 할 수 있다.
부모 클래스의 속성과 기능들을 하위 클래스에서도 사용할 수 있도록 연장하는 것을 말한다. 예를 들어 전화 기능을 가진 Phone
라는 클래스가 있다고 할 때, 이를 상속 받은 SmartPhone
클래스는 부모 클래스가 가진 전화 기능을 그대로 사용할 수 있으며 하위 클래스의 고유 기능인 인터넷 기능도 추가할 수 있다.
class Phone {
String model;
void call(){
System.out.println("전화를 겁니다");
}
}
class SmartPhone extends Phone {
boolean isConnectedToInternet;
void internet(){
System.out.println("인터넷 통신을 합니다");
}
}
어떠한 객체의 속성이나 기능이 맥락에 따라 다른 역할을 수행할 수 있는 것이다. 다형성을 가장 잘 보여주는 것이 오버로딩
과 오버라이딩
이다.
다형성은 객체들 간 관계를 유연하게 만들어주며 확장성 있는 설계가 가능하도록 해준다.
클래스 안에 서로 연관되어 있는 속성과 기능들을 하나의 캡슐로 만들어서 데이터를 외부로부터 보호하는 것을 의미한다.
캡슐화의 목적은 코드를 수정없이 재활용하는 것과 데이터 보호 및 정보 은닉에 있다. 클래스
라는 캡슐에 기능들을 담는 것 자체로 1차적으로 캡슐화를 구현할 수 있으며 접근제어자
를 이용하여 외부에 노출하지 않아야 할 정보 및 기능의 접근 권한을 제어함으로써 정보를 보호할 수 있다. 또한 접근 제어자를 이용하면 불필요한 수정이 일어나는 것을 막고, 수정이 필요한 경우 책임이 있는 객체만 수정이 일어나도록 할 수 있다.
캡슐화를 실현하는 또 다른 방법은 바로 getter/setter
를 이용하는 것이다. 외부에서 멤버 변수에 접근하지 못하게 private
으로 변수를 선언하고 해당 변수에 접근하기 위해서는 getter, setter 메서드를 통해서만 접근을 허용해주는 것이다.
어찌보면 public
으로 선언했을 때와 똑같이 외부에서 접근 가능한 것처럼 보이겠지만 getter/setter
를 이용하면 유효성 검사 또는 예외 처리와 같은 로직을 추가하여 아무 값이나 들어오는 것을 방지할 수 있다.
SOLID 원칙이란 좋은 객체 지향 설계를 위한 5가지 원칙이다.
하나의 클래스는 하나의 책임만 가져야한다는 원칙이다. 하나의 책임이라는 것은 하나의 기능만을 담당해야한다는 의미가 아니라 모듈이 변경되는 이유가 한가지여야 한다는 것을 의미한다.
예를 들어 사칙연산 함수를 가진 클래스가 있다고 할 때, 이 클래스가 수정되는 일은 사칙연산 함수와 관련된 문제때문이여야 하지, 다른 이유여서는 안된다.
확장에 열려있고 수정에는 닫혀있어야한다는 원칙이다. 이는 기존 코드는 변경하지 않으면서 새로운 기능을 추가할 수 있어야한다는 의미이다.
예를 들어 데이터베이스 스펙이 정확히 정해지지 않아 현재는 RDBMS를 사용하다가 추후에 NoSQL로 변경될 가능성이 있다고 해보자. 이때 DB 접근부를 클래스로 바로 구현하기보다 인터페이스와 그것에 대한 구현체를 따로 생성하면 추후에 스펙이 변경되었을 때 변경 스펙에 맞춰 새로운 구현체를 추가해주면 된다.
OCP원칙은 역할과 구현의 명확한 분리와 다형성을 활용함으로써 실현할 수 있다.
프로그램의 객체는 프로그램의 정확도를 깨뜨리지 않으면서 하위 타입의 인스턴스로 변경될 수 있어야한다는 원칙이다. 즉, 부모 클래스의 인스턴스가 들어갈 자리에 자식 클래스의 인스턴스가 들어가도 문제없이 작동해야한다.
자식 클래스는 부모 클래스의 책임을 무시하거나 재정의하지 않고 확장만 수행하도록 해야 이 원칙을 준수할 수 있다.
이 원칙은 하나의 범용 인터페이스보다 특정 클라이언트를 위한 여러개의 인터페이스가 더 낫다는 것이다. 이 원칙을 준수하면 인터페이스가 명확해지고 대체 가능성이 높아진다.
의존 관계를 맺을 때, 변화하기 쉬운 것 보다는 변화하기 어려운것 또는 변화가 없는 것에 의존하라는 원칙이다. 구체적인 클래스보다 인터페이스 또는 추상 클래스와 의존 관계를 맺으라는 것이다.
참고
객체 지향 프로그래밍이 뭔가요? (꼬리에 꼬리를 무는 질문 1순위, 그놈의 OOP)
객체 지향 프로그래밍의 4가지 특징ㅣ추상화, 상속, 다형성, 캡슐화
[OOP] 객체지향 프로그래밍의 5가지 설계 원칙, 실무 코드로 살펴보는 SOLID