객체 지향 프로그래밍
좀 더 나은 프로그램을 만들기 위한 프로그래밍 패러다임으로 로직을 상태(state)와 행위(behave)로 이루어진 객체로 만드는 것이다.
객체지향 프로그래밍을 학습하는데 장애 중의 하나는 번역이다. Object를 번역한 객체는 현실에서는 거의 쓰지 않는 말이고, 뭐랄까 철학적인 느낌을 자아낸다. 그래서 객체지향 프로그래밍을 처음 접하는 입문자들은 객체지향 프로그래밍을 철학적인 탐구의 대상으로 파악하는 경향을 보이는데, 필자의 생각에 이것은 공부를 어렵게 할 뿐 도움이 되지 않는다. 쉽게 생각하자. 객체는 변수와 메소드를 그룹핑한 것이다.
객체지향 프로그래밍 교육에서는 문법과 설계 크게 두 가지로 구분된다.
하나는 문법, 객체지향을 편하게 할 수 있도록 언어가 제공하는 기능을 익히는 것 이다. 이러한 기능들은 if,for문처럼 문법적인 구성을 가지고 있다. 이 문법을 이해하고, 숙지해야 객체를 만들 수 있다.
두번째는 좋은 객체를 만드는 법이다. 다른말로는 설계를 잘하는 법이라 할 수 있다. 복잡함 속에서 필요한 관점만을 추출하는 행위를 추상화라고 한다. 프로그램을 만든다는 것은 소프트웨어의 추상화라고 할 수 있다.
객체 지향 프로그래밍은 좀 더 현실을 잘 반영하기 위한 노력의 산물이다. 이것은 단순히 객체 지향의 문법을 이용해서 객체를 만든다고 달성되는 것이 아니다. 고도의 추상화 능력이 필요하다. 좋은 설계는 문법을 배우는 것보다 훨씬 어려운 일이다. 심지어 이것은 지식을 넘어서 지혜의 영역이다. 좋은 설계를 위한 조언들은 많지만 이러한 조언들은 조언자의 입을 떠나는 순간 생명력을 잃어버린다. 지식은 전수되지만 지혜는 전수되지 않기 때문이다. 스스로 경험하고 깨우쳐서 자기화시켜야 한다.
객체 지향은 부품화의 정점이라고 할 수 있다. 자바의 메소드는 부품화의 예라고 할 수 있다. 메소드를 사용하는 기본 취지는 연관되어 있는 로직들을 결합해서 메소드라는 완제품을 만드는 것이다. 그리고 이 메소드들을 부품으로 해서 하나의 완제품인 독립된 프로그램을 만드는 것이다. 메소드를 사용하면 코드의 양을 극적으로 줄일 수 있고, 메소드 별로 기능이 분류되어 있기 때문에 필요한 코드를 찾기도 쉽고 문제의 진단도 빨라진다.
그런데 프로그램이 커지면 엄청나게 많은 메소드들이 생겨나게 된다. 메소드와 변수를 관리하는 것은 점점 어려운 일이 되기 시작한다. 급기야는 메소드가 없을 때와 같은 상황에 봉착하게 된다. 메소드는 프로그래밍의 역사에서 중요한 도약이었지만, 이 도약이 성숙하면서 새로운 도약지점이 보이기 시작한 것이다.
그 도약 중의 하나가 객체 지향 프로그래밍이다. 이것의 핵심은 연관된 메소드와 그 메소드가 사용하는 변수들을 분류하고 그룹핑하는 것이다. 바로 그렇게 그룹핑 한 대상이 객체(Object)다. 비유하자면 파일과 디렉토리가 있을 때 메소드나 변수가 파일이라면 이 파일을 그룹핑하는 디렉토리가 객체라고 할 수 있다.
그런데 부품화라고 하는 목표는 단순히 동일한 기능을 하는 메소드와 변수를 그룹핑한다고 달성되는 것은 아니다. 제대로된 부품이라면 그것이 어떻게 만들어졌는지 모르는 사람도 그 부품을 사용하는 방법만 알면 쓸 수 있어야 한다. 이를테면 모니터가 어떻게 동작하는지 몰라도 컴퓨터와 모니터를 연결하는 방법만 알면 화면을 표시 할 수 있는 것과 같은 이치다. 즉 내부의 동작 방법을 단단한 케이스 안으로 숨기고 사용자에게는 그 부품의 사용방법만을 노출하고 있는 것이다. 이러한 컨셉을 정보의 은닉화(Information Hiding), 또는 캡슐화(Encapsulation)라고 부른다. 자연스럽게 사용자에게는 그 부품을 사용하는 방법이 중요한 것이 된다.
각각의 부품은 미리 정해진 약속에 따라서 신호를 입, 출력하고, 연결점의 모양을 표준에 맞게 만들면 된다. 이러한 연결점을 인터페이스(interface) 라고 한다. 인터페이스란 이질적인 것들이 결합하는 것을 막아주는 역할도 하는 것이다. 즉 인터페이스는 부품들 간의 약속이다.
refactoring(리팩토링) - 기존의 있던 코드와 동일하게 동작하지만 코드의 내용을 개선해서 효율적으로 만든 행위 리팩토리를 거듭하면 거듭할수록 코드의 짜임새나 질은 높아지고 동시에 문제점들을 개선하기 쉽고 버그가 줄어든다. 유지보수가 용이하고 코드의 가독성이 향상된다.
Tip 효율적인 코드 작성법 : 중복을 최대한 제거한다 -> 같은 로직처리가 있다면 최대한 변수를 활용하여 가변적으로 변환해준다.
프로그래머라면 항상 가지고 있어야 할 마인드이다. 마음속 깊이 다시 생각해보자.
클래스와 인스턴스 그리고 객체
클래스 - 일종의 설계도로, 연관되어 있는 변수와 메소드의 집합이다
인스턴스 - 클래스(설계도)의 구체적인 제품을 만드는 것으로 생각하면 편하다. 그 때 사용하는 키워드는 'new' 이다
Calculator 라는 클래스가 존재한다고 가정할 때
위의 코드는 new를 이용해서 만든 인스턴스를 변수 c1에 담고 있다. 변수 c1에 인스턴스를 담은 이유는 c1을 통해서 인스턴스를 제어해야 하기 때문이다. 그런데 c1 앞에 Caculator가 위치하고 있다. 지금까지 우리는 여러가지 변수를 선언해왔다. 예를들어서 변수 a는 변수가 담을 수 있는 데이터 타입에 따라서 형태로 선언된다.
여기서 변수명 c1의 데이터 타입은 Calculator라는 의미다. 즉 클래스를 만든다는 것은 사용자 정의 데이터 타입을 만드는 것과 같은 의미다. 지금 기억할 것은 클래스를 인스턴스화 할 때는 변수에 담아야 한다는 것과 이 때 사용하는 변수의 데이터 타입은 그 클래스가 된다는 점이다.
클래스와 인스턴스는 설계도와 제품이라고 설명했다. 그럼 객체는 클래스일까 인스턴스일까? 다소 논란이 있지만 일반적으로 설계도인 클래스가 구체적인 실체인 인스턴스가 되었을 때 객체라고 부른다. 보통은 구체적인 코드 상에서 나타는 객체를 인스턴스라고 부르고, 로직을 설계 할 때 나타나는 인스턴스를 객체라고 부른다. 또는 클래스, 인스턴스의 구분없이 포괄적으로 객체라는 말을 쓰기도 한다.
한 예제로 메소드 setOprands 내에 this.이라는 것이 있다. this는 클래스를 통해서 만들어진 인스턴스 자신을 가리킨다. 위의 코드에서 left는 매개변수이고 이 변수는 setOprands 밖에서는 접근 할 수 없다. 반면에 this.left는 4행에서 선언한 변수에 값을 설정하는 것이고 메소드 밖에서 선언한 변수는 인스턴스 내의 모든 메소드에서 접근이 가능하다.
상태가 다른 객체를 대상으로 메소스를 실행시키면 다른 결과를 나타내게 된다. 변수는 다른말로 상태 (state), 메소드를 다른 말로는 행동(behave)이라고도 표현한다.
즉, 하나의 클래스를 바탕으로 서로 다른 상태를 가진 인스턴스를 만들면 서로 다른 행동을 하게 된다는 것을 알 수 있다. 하나의 클래스가 여러개의 인스턴스가 될 수 있다는 점이 객체 지향이 제공하는 가장 기본적인 재활용성이라고 할 수 있다.
객체 지향의 객체를 하나의 작은 프로그램처럼 느껴보자. 프로그램 안에 객체라는 작은 프로그램을 만드는 것이다. 이것들을 레고 블럭처럼 쌓아서 웅장한 프로그램을 만드는 것이 객체 지향이 추구하는 바라고 할 수 있다.
'Java' 카테고리의 다른 글
Java 기초 입문 6일차 (Interface, 다형성, Exception, multi catch) (0) | 2022.11.23 |
---|---|
Java 기초 입문 5일차 (classpath, API, 접근제어자, abstract) (0) | 2022.11.22 |
Java 기초 입문 4일차 (class와 instance의 관계, 지역변수와 전역변수, 상속, 생성자, 오버로딩과 오버라이딩) (0) | 2022.11.21 |
Java 기초 입문 2일차 (method, input output, GUI) (0) | 2022.11.18 |
Java 기초 입문 1일차 (조건문, 반복문, 배열) (0) | 2022.11.16 |