OOP란?
OOP는 Object Oriented Programming의 줄임말로, 많은 객체(Objcet)들이 모여서 상호 협력하면서 데이터를 처리하는 방식의 프로그래밍 설계 방법을 일컫는다.
즉, 현실 세계를 프로그래밍으로 옮겨와 프로그래밍하는 것을 말한다. 현실 세계의 사물들을 객체라고 보고 그 객체로부터 개발하고자 하는 애플리케이션에 필요한 특징들을 뽑아와 프로그래밍하는 것이다.
이처럼 객체 지향 프로그래밍은 객체들을 레고 블럭 조립하듯 유연하고 변경이 용이하다. 또한 각각의 객체들이 독립적인 역할을 가지기 때문에 코드의 변경을 최소화하고 유지보수를 하는 데 유리하다. 뿐만 아니라 코드의 재사용을 통해 반복적인 코드를 최소화하고, 코드를 최대한 간결하게 표현할 수 있다. 이러한 장점으로 인해 대규모 소프트웨어 개발에 많이 사용된다.
💡 절차적 프로그래밍(Procedure Programming)
객체 지향 프로그래밍의 반대 개념으로는 절차적 프로그래밍이라는 것이 있다. C 언어가 대표적이다.
소프트웨어 발전 속도가 빨라지면서 자연스레 코드도 복잡해지기 시작했다. 그러다보니 복잡한 알고리즘을 짤 때 절차적 프로그래밍을 하면 순서도가 꼬이게 되면서 가독성 뿐만 아니라 코드의 유연성과 변경 용이성이 떨어진다.
이러한 문제를 극복하기 위해 객체지향 프로그래밍이 등장하게 되었다.
OOP의 특징
1. 추상화
객체의 공통적인 속성과 기능을 추출하여 정의하는것을 의미한다.
객체 지향 프로그래밍에서 추상화는 크게 제어 추상화, 데이터 추상화 두 분류로 나눌 수 있다.
- 제어 추상화 - 어떤 클래스의 메소드를 사용하는 사용자에게 해당 메소드의 작동방식과 같은 내부 로직을 숨기는 것을 말한다.
- 데이터 추상화 - 대상을 간단한 개념으로 일반화하는 과정을 말한다.
2. 상속
기존의 클래스를 재활용하여 새로운 클래스를 작성하는 자바의 문법 요소를 의미한다.
추상화의 연장선에서 상속은 클래스 간 공유될 수 있는 속성과 기능들을 상위 클래스로 추상화 시켜 해당 상위 클래스로부터 확장된 여러 개의 하위 클래스들이 모두 상위 클래스의 속성과 기능들을 간편하게 사용할 수 있도록 한다.
상속 기능을 이용하여 중복 속성 제거, 코드 재사용 증대 효과를 누릴 수 있다.
3. 캡슐화
클래스 안에 서로 연관있는 속성과 기능들을 하나의 캡슐(capsule)로 만들어 데이터를 외부로부터 보호하는 것을 말한다.
서로 관련 있는 데이터와 이를 처리할 수 있는 기능들을 한곳에 모아 관리하는 것이다.
자바 객체 지향 프로그래밍에서 이렇게 캡슐화를 하는 이유는 크게 두 가지이다.
- 데이터 보호(data protection) – 외부로부터 클래스에 정의된 속성과 기능들을 보호
- 데이터 은닉(data hiding) – 내부의 동작을 감추고 외부에는 필요한 부분만 노출
즉, 외부로부터 클래스에 정의된 속성과 기능들을 보호하고, 필요한 부분만 외부로 노출될 수 있도록 하여 각 객체 고유의 독립성과 책임 영역을 안전하게 지키고자 하는 목적이 있다.
자바에서 이러한 목적을 달성하기 위한 방법은 다음 두 가지이다.
- 접근 제어자(access modifiers) - 클래스 또는 클래스의 내부의 멤버들에 사용되어 해당 클래스나 멤버들을 외부에서 접근하지 못하도록 접근을 제한하는 역할
- getter/setter 메서드 - 선택적으로 외부에 접근을 허용할 속성과 그렇지 않을 속성을 getter/setter 메서드를 통해 설정
4. 다형성
다형성이란 하나 이상의 형태을 갖는 것을 말한다. 이를 객체지향에서는 이름이 같은 메서드가 사용하는 객체에 따라 다르게 동작하는 것을 의미하며, 서로 다른 구현(코드)을 제공한다.
자바에선 대표적으로 오버로딩, 오버라이딩, 업캐스팅, 다운캐스팅, 인터페이스, 추상메소드, 추상클래스 방법이 모두 다형성에 속한다.
객체 지향 설계의 5가지 원칙(SOLID)
1. SRP (Single Responsibility Principle) : 단일 책임 원칙
한 클래스는 하나의 책임만 가져야 한다는 원칙이다.
하나의 책임이라는 것은 클 수도 있고 작을 수도 있으며, 문맥과 상황에 따라 다르기 때문에 모호하다. 이때 중요한 기준은 바로 변경이다. 즉, 어떤 클래스를 변경할 때 파급 효과가 적으면 단일 책임의 원칙을 잘 따른 것이라고 볼 수 있다.
2. OCP (Open Closed Principle) : 개방 폐쇄 원칙
소프트웨어의 요소는 확장에는 열려있으나 변경에는 닫혀있어야 한다는 원칙이다.
이는 다형성을 통해 가능해지는데, 인터페이스와 구현 클래스를 분리하여 개발한다면, 구현 클래스의 내부 구조가 변경되거나 구현 클래스 자체가 변경되는 경우에도 소스 코드 변경을 하지 않을 수 있다. 즉 인터페이스 확장에는 열려있지만, 코드 변경에는 닫혀있어야 한다.
3. LSP(Liskov Substitution Principle) : 리스코프 치환 원칙
프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다는 원칙이다.
예를 들어 자동차라는 인터페이스가 있고 그 안에 axel()이라는 메서드가 있다. 보통 이 메서드는 자동차가 앞으로 가도록 구현되어야 한다. 만약 새로운 구현 클래스의 axel() 메서드가 자동차가 뒤로 가도록 구현할 수도 있다.
이 경우 컴파일 에러는 발생하지 않겠지만, 기존 클래스에서 새로운 구현 클래스로 치환 시 LSP 원칙을 위반하게 된다.
정리하자면, LSP 원칙은 부모 클래스 혹은 인터페이스가 가진 기능적 규약을 지킨 채 하위 클래스 혹은 구현체를 구현해야 한다는 원칙이다. 즉, 다형성에서 하위 클래스는 인터페이스의 규약을 모두 지켜야 하며, 이는 다형성을 지원하기 위한 원칙이고 인터페이스를 구현한 구현체를 믿고 사용하기 위해 필요하다.
4. ISP(Interface Segregation Principle) : 인터페이스 분리 원칙
특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다는 원칙이다.
예를 들면 자동차라는 하나의 인터페이스보단, 이 인터페이스를 운전과 정비라는 2개의 인터페이스로 나누는 것이 더 낫다는 것이다. 그렇게되면 향후 정비 인터페이스가 변경된다고 하더라도 운전자 인터페이스에는 영향을 주지 않게되고, 각 인터페이스의 역할이 좀 더 명확해지며 대체 가능성이 높아진다.
5. DIP(Dependency Inversion Principle) : 의존 역전 원칙
프로그래머는 추상화에 의존해야지, 구체화에 의존하면 안된다는 원칙이다.
즉, 구현 클래스에 의존하지 말고 인터페이스에 의존하라는 의미이다. 객체 세상에서 인터페이스에 의존해야 유연하게 구현체를 변경할 수 있으며, 만약 구현체에 의존할 경우 변경이 아주 어려워진다.
의존성 주입은 이 원칙을 따르는 방법 중 하나이다.
🔗 Reference
'CS' 카테고리의 다른 글
[DB] NoSQL (1) | 2024.04.06 |
---|---|
[Network] 프록시(Proxy) (0) | 2024.04.06 |
[자료구조] 힙(Heap) (0) | 2024.03.07 |
[DB] Replication이란? (1) | 2023.11.15 |