티스토리 뷰

CS/Java

자바의 OOP

기억용블로그 2022. 10. 25. 21:09
728x90

자바에서 OOP라는 개념은 어떤 것일까?

OOP는 여러 개발방법론 중 하나인 객체 지향 프로그래밍을 의미하며 여러 개발 패러다임 중 하나에 속한다.

 

패러다임이라는 것은 "우리 이건 앞으로 이렇게 생각하자."를 누군가가 주창하면 그것은 하나의 패러다임이 되며 해당 패러다임이 여러 사람에 의해 충분히 입증되고 과거에 잔재했던 여러 문제를 해결하게 되면 과거의 패러다임은 폐기, 축소되고 새로운 패러다임으로 사고하게 되는 "패러다임 시프트"가 일어나게 된다.

 

즉, OOP라는 패러다임을 이해하고자 하면 OOP 이전에 존재했던 패러다임을 알아보고 과거의 패러다임이 어떤 문제를 가졌고 OOP가 그 문제를 어떻게 해결했는지를 알아야 한다.

 

절차적 프로그래밍

기존에 절차적 프로그래밍(Procedural Programming, PP)이 존재했다.

PP는 procedure(==function) call을 통해서 task를 처리하는 패러다임이다. 여기서 procedural은 절차를 의미하는 것이 아닌 프로시저, 즉 함수를 의미하는 것이다.

 

PP는 그 당시에 존재하던 문제, 즉 스케일이 크지 않고 복잡하지 않은 명령어를 처리하는 방식의,들을 해결하는데에 적합했고 훌륭하게 동작했다. 

다만 PP의 문제는 code만 모듈러가 되어 재활용되고 code가 다루는 data는 같은 바운더리라고 생각하지 않았다. 함수들의 연속적인 호출로 논리적인 문제를 해결하는 것이 주된 관점이었지 함수가 다루는 데이터는 주된 관심사가 아니었다.

 

PP 패러다임에선 data가 관심사의 밖이었다는 데에서 조금씩 불협화음이 발생하기 시작했고, 프로그램의 스케일이 커지고 기술이 발전하고 수요가 증가하면서 개발자들은 code와 data를 함께 다루는 어떤 새로운 개념의 필요성을 느끼기 시작했다.

 

객체 지향 프로그래밍

OOP는 이런 code와 data를 함께 다루고자 하는 개발자의 필요성에 의해 탄생하게 되었다. OOP의 가장 핵심적인 개념 중 하나인 캡슐화(encapsulation)가 여기서 등장한다.

 

캡슐화란 data(변수)와 code(함수)를 하나로 묶는 것을 의미한다. Java에서는 캡슐화를 class를 통해 구현하였고 OOP의 발흥과 함께 한 언어답게 Java에서는 어떤 캡슐화되지 않은 변수와 함수는 존재조차 할 수 없다. 모든 Java 개발자는 숨쉬듯 하고 있는 것이 캡슐화라 할 수 있다.

 

캡슐화된 code와 data는 그 자체로 하나의 어떤 존재로서 존재할 수 있게 되었고 사람들은 이 존재를 객체(Object)라 부르기 시작한다.

이제 프로그래밍의 패러다임은 함수 중심의 task 처리에서 데이터와 함수를 가지는 존재들 간의 상호작용이라는 개념으로 생각되게 되었다.

 

OOP의 개념과 원칙

프로그래밍 패러다임은 데이터와 함수에서 객체를 다루는 개념으로 넘어가게 되었고 이제부터 객체를 다룰때 생기는 비효율성과 문제점을 해결하기 위해 여러 가지 개념과 원칙이 생기게 된다.

 

정보 은닉

현대의 OOP에선 대부분의 경우 캡슐화가 정보 은닉과 동일한 의미로 사용되지만 좀 더 엄밀한 설명을 위해 캡슐화와 분리하여 설명한다.

 

변수와 함수를 캡슐화를 통해 모듈화해두었지만 이를 외부에서 어디서든 쉽게 접근할 수 있다면 모듈화의 의미가 퇴색되고 객체간의 결합도가 높아지고 응집도는 낮아지는 문제가 발생하게 된다.

이를 해결하기 위해 객체의 내부를 외부에서 알지 못 하도록 설계하게 되는데 Java에서는 정보 은닉을 접근 제한자를 통해 구현하였다.

 

  • private : class 내부에서만 사용되며 외부로 노출되지 않음.
  • protected : class와 상속받은 class에만 노출됨.
  • public : 모든 다른 class에게 노출됨.

여기서 getter와 setter 등의 개념 또한 변수의 은닉성(data abstraction)을 위해 도입된다.

 

추상화

추상화와 정보 은닉은 내부 구현을 감추어 응집도를 높이고 결합도를 낮춘다는점에서는 같지만 정보 은닉의 목적은 잘못된 조작이 되지 않도록 보호하는, 외부로부터의 보호 역할이라고 한다면

추상화는 외부와의 원활한 상호작용을 위해 내부 구현을 감추는 용도로 사용된다는 점에서 다르다고 할 수 있다. 추상화는 어떤 model이 가져야 하는 specification의 개념으로 이해할 수 있다.

 

추상화는 OOP만의 개념이 아닐뿐더러 IT의 모든 layer마다 추상화를 통해 상호작용하기에 좀 더 큰, 광의의 개념이라고 생각할 수 있다.

또한 많은 경우에 추상화와 캡슐화는 비슷하게 이해되므로 OOP의 핵심 개념에서 빠지는 경우가 많다.

 

상속

객체의 공통 부분을 추출해서 부모 클래스로 만들거나 기존 클래스를 확장하여 새로운 클래스를 만들어서 사용이 가능하는 등, 기존 객체의 행동을 답습하고 그것을 확장하거나 행위를 변경하는 등의 객체 구조를 위한 용도로 사용이 가능한 상속이 도입되었다.

Java에서는 상속을 extends로 구현하였다.

 

다만 상속이 객체간의 hierarchy를 구성하는데에 상속이 매우 유용하고 강력하게 사용되었지만 이상적인 사용방법 이외의 어떤 상황에서도 매우 강력하게 사용된다는 점때문에 과거에 많은 부작용을 낳고 문제를 발생시키게 되었다.

 

과거의 개발자들은 상속이라는 망치를 들고 상속 할 수 있어 보이는 못이란 못을 전부 찾아다녔고 처음에는 아주 손쉽게 상속을 이용해서 문제를 해결할 수 있(는 것처럼 보였)었다. 다만 문제는 상속은 부모와 자식 클래스 간에 컴파일 시점에 관계가 결정되고 이는 두 클래스 간에 높은 결합도를 가지게 만들었고 높아진 결합도는 코드의 확장성과 유연성을 낮추게 되어 OOP 패러다임이 가질 수 있는 장점을 많이 퇴색시키게 되는 결과를 낳게 되었다.

 

또한 상속은 is a 관계가 확실한 경우에만 사용되어 객체간의 hierarchy를 구성하는데에 사용되는 것이 바람직하나 모든 곳에서 상속을 사용하다 보면 잘못된 is a 관계를 가지는 경우에도 상속이 되어버릴 수 있는 문제가 발생한다. is a 관계가 확실한 경우에도 확장한 자식 클래스가 정확히 부모 클래스와 같은 행동을 할 것이라는 보장을 할 수 없다.

 

이런 상속이 가지는 단점으로 인해 리스코프 치환 원칙, 접근 제한자, composition over inheritance(상속보다는 합성) 등의 개념이 도입되었다.

 

다형성

외부로는 하나의 인터페이스(Java의 interface X)를 두고 내부적으로 여러 방향으로 다르게 해석되고 사용될 수 있는 개념으로 OOP에 도입되었다.

Java에서는 다형성을 subtyping(interface, inheritance), parameter(generic type), ad hoc(overloading) 등을 통해 구현하였다.

 

  • subtyping : interface와 inheritance로 생성된 상위 클래스를 외부에서 인터페이스로 참조하도록 하고 implements, extends하는 하위 클래스에서 구현하여 서브타입이 구현된다.
  • parameter : generic type으로 파라미터를 선언( <? extends T>, <? super T>와 같이 )하여 파라미터 다형성을 구현한다.
  • ad hoc : 동일한 시그니처를 갖는 메서드의 파라미터의 개수나 타입을 다르게 하여 오버로딩 다형성을 구현한다. 

 

SOLID

위에서 설명한 OOP의 기본 개념처럼 언어 레벨에서 이미 구현되어 있어 반드시 강제되는 정도는 아니지만 훌륭한 OOP 설계를 위해서 따라야 하는 5가지 원칙에 대해 얘기한다.

모든 원칙은 결합도를 낮추고 응집도를 높히는데에 중점적으로 작용하게 되며 이때 인터페이스와 구현체를 분리하는 추상화를 통해 원칙을 지킬 수 있으며 이는 곧 높은 유지보수성과 유연성, 재사용성을 가져오고 결과적으로 이를 지키며 설계할 경우 유지 보수와 확장이 상대적으로 더 편한 시스템을 만들 수 있을 가능성이 높아진다.

 

SOLID 원칙에 대해서는 심도있게 다뤄놓은 자료가 많기 때문에 간략한 설명으로 대체합니다.

https://mangkyu.tistory.com/194

 

단일 책임 원칙 (SRP)

하나의 클래스는 하나의 책임만을 지고 해당 책임만을 충실히 이행해야 함을 의미한다. 책임이라는 단어는 '변경하려는 이유'로 치환이 가능하며 이때 어떤 클래스가 변경되어야 할 때는 하나의 이유로 인해서만 변경되어야 한다고 이야기 할 수 있다.

 

개방 폐쇄 원칙 (OCP)

요구 사항이 변함에 따라 변경될 수 있는 기능을 추가함에 있어 확장에 개방되어 있어야 하며

기능이 추가될때 내부 소스 코드의 변경없이(폐쇄) 이뤄져야 한다는 것을 의마한다.

 

리스코프 치환 원칙 (LSP)

부모 클래스에 자식 클래스가 들어가도 코드는 다른 곳의 변경없이 정확히 같은 행동을 해야 한다는 것을 의미한다. 

상술하였듯이 상속이 가지는 문제를 해결하기 위해 도입되게 되었다.

 

인터페이스 분리 원칙 (ISP)

클라이언트에서 사용하지 않는 메서드까지 포함하는 커다란 인터페이스보다 관심사에 맞게 분리되어 꼭 필요한 인터페이스들로 구성된 더 작은 인터페이스로 분리해서 사용해야 함을 의미한다.

 

의존관계 역전 원칙 (DIP)

외부에서는 구현체를 바라보는 것이 아니라 인터페이스를 바라보아야 한다는 것을 의미한다.

OCP와 높은 상관 관계를 가지며 추상화를 통해 원칙을 지킬 수 있다.

 

레퍼런스

https://mangkyu.tistory.com/193

 

[Java] 메소드 오버라이딩/ 메소드 오버로딩을 통한 상속 다형성에 대한 이해와 Self 참조

아래의 내용은 오브젝트(Object)를 읽으면서 정리한 내용입니다. Java에 한정되는 이야기이므로 다른 언어에는 적용되지 않을 수 있습니다. 1. 상속을 이용한 다형성과 메소드 오버라이딩/ 메소드

mangkyu.tistory.com

https://mangkyu.tistory.com/199

 

[OOP] 코드의 재사용, 상속(Inheritance)보다 합성(Composition)을 사용해야 하는 이유

객체지향 프로그래밍에서 코드를 재사용하기 위한 방법으로 크게 상속과 합성이 있습니다. 대부분의 경우 상속보다 합성을 이용하는 것이 좋은데, 이번에는 왜 합성을 사용해야 하는지에 대해

mangkyu.tistory.com

https://namu.wiki/w/%EA%B0%9D%EC%B2%B4%20%EC%A7%80%ED%96%A5%20%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D

 

객체 지향 프로그래밍 - 나무위키

프로그램의 세부 구현을 외부로 드러나지 않도록 특정 모듈 내부로 감추는 것이다. 내부의 구현은 감추고 모듈 내에서의 응집도를 높이며, 외부로의 노출을 최소화하여 모듈 간의 결합도를 떨

namu.wiki

https://www.nextree.co.kr/p6960/

 

객체지향 개발 5대 원리: SOLID

현재를 살아가는 우리들은 모두 일정한 원리/원칙 아래에서 생활하고 있습니다. 여기서의 원칙 이라 함은 좁은 의미로는 개개인의 사고방식이나 신념, 가치관 정도가 될 수가 있겠고, 넓게는 한

www.nextree.co.kr

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함