|
객체지향 프로그래밍(OOP) 1이민규 |
|||||||||||
1. 서론(1) 왜 그렇게 난리 법석들인가?객체지향 프로그래밍을 처음 배우려는 사람들에게 오히려 그 동기를 약화시킬 수 있을지도 모르는 소리를 하려고 한다. 많은 사람들과 여러 문헌에서 마치 객체지향 프로그래밍을 하면 모든 것이 좋을 것이라는 환상과 같은 말을 많이 한다. "객체지향적으로 작성된 프로그램은 언제든지 재사용할 수 있다.", "객체지향적으로 작성된 프로그램은 엄청난 확장성을 가져다 준다." 수 년전 필자도 이와 같은 말에 현혹되어 공부를 시작한 것이다. 물론, 지금이야 객체지향에 관련된 공부를 오랫동안 했던 것에 대해서는 매우 다행스럽게 생각하지만, 공부를 시작하기 전에 정확한 특징을 미리 파악할 수 있었다면 여러 시행착오를 겪지 않아도 되었을 것이다. 필자가 해주고 싶은 한마디는 바로 이것이다. "객체지향은 만병통치약이 아니다." 세상에 만병통치약이 없다는 것을 사람들은 잘 알지만 몇몇 어리석은 사람은 약장수의 농간에 넘어가 비싼 돈을 주고 그 약(?)을 사게 된다. 모든 프로그램을 객체지향적으로 작성하는 것은 결코 바람직한 것이 아니다. 약의 효능을 제대로 알고 적당한 현상에 치료를 하여야 최상의 효과를 거둘 수가 있는 것이다. 그래서 이제 객체지향이라는 약의 효능을 제대로 알기 위해 이 글을 보는 것이 아니겠는가? (2) 프로그래밍 패러다임(Programming Paradigm)패러다임은 문제를
이해하고 지식을 구성하는 모든 이론, 표준 및 기법이다. "패러다임(paradigm)"이란 말의 뜻은 뭘까? 필자의 소견으로는 "사고의 틀"이라 생각한다. 그렇다면 프로그래밍 패러다임이라는 말은 프로그램을 작성할 때 그 프로그래머의 생각이 맴도는 틀일 것이다. 그런데 지금까지 C나 Pascal로 프로그램을 작성하던 사람들이라면 패러다임 전환(paradigm shift)이라는 고통을 감수해야 한다. C와 Pascal 프로그래머는 데이터와 그것을 처리하는 프로시져를 따로 생각하며 또한 전체 프로그램을 기능적 분할(functional decomposition)에 따라 구성하는데 익숙해져 있을 것이다. 하지만 객체지향 프로그래밍을 통하여 작성한 프로그램의 확장성과 재사용성을 향상 시키려면 반드시 이러한 틀에서 빠져나와야 할 것이다. 그렇다면 객체지향 패러다임 이외에 다른 프로그래밍 패러다임에는 뭐가 있느냐는 질문을 할 수 있는 똑똑한 사람들이 있을 것이다. 그런 사람들을 위해서 프로그래밍 패러다임 중 대표적인 것들을 간단히 설명하고자 한다. 명령형(imperative) 패러다임 우리는 지금까지 C, Pascal, Basic과 같은 언어들을 많이 사용해왔다. 이러한 언어들은 모두 명령형 패러다임에 속하는 언어들이다. 명령형 패러다임의 기본 생각은 처리장치(processor)가 기억장소(memory)의 값을 처리하고 또한 값을 변경함으로써 계산을 수행하여 문제를 해결하고자 하는 방법이다. 이러한 명령형 패러다임은 상태(즉, 메모리에 기억된 값들)를 점진적으로 변경하여 원하는 상태에 이를 때까지 처리를 계속하게 된다. 다음의 Pascal 코드를 보자. Pascal은 몰라도 대충 어떻게 돌아가는 지는 이해할 것이라 생각된다. 1부터 n까지의 수를 더하는 프로시져 sum은 상태 0에서부터 반복적으로 그 값을 변경하면서 우리가 원하는 답에 도달하게 된다. 이와 같은 방법들은 전통적으로 우리가 C나 Pascal로 프로그래밍하는 방식이다. function sum(n: Integer): Integer; 함수형(functional) 패러다임 함수형 프로그래밍은 함수(function)를 값에 적용함으로써 문제를 해결하게 된다. 함수형 패러다임의 특징은 정수, 문자열뿐만 아니라 함수도 값(value)이라는 점이다. 즉, 값은 인자로 넘겨줄 수 있고, 변수에 저장될 수 있으며 함수의 결과도 될 수 있다는 점이다. 그리고 이들은 새롭게 생성될 수는 있으나 그 값이 변경되지는 않는 다는 점이 큰 차이점이 되겠다. 또한 동일한 함수에 동일한 값을 사용하여 호출하면 항상 동일한 결과는 낸다는 특징(referencial transparency)로 대표된다. 명령형에서는 상태를 점진적으로 바꾸게 되면 함수의 호출 결과가 같은 값으로 호출을 한다고 하더라도 항상 값은 값이 나올 수는 없다. 이와 같은 함수형 패러다임의 대표적인 언어로는 Scheme, FP, Hope, SML, Miranda, Haskell 등이 있다. 논리형(logical) 패러다임 논리형 패러다임은 정리의 증명과정(theorem proving)이 계산의 수행에 대응한다. 가장 중요한 특징은 선언적(비절차적)이라는 것인데, 문제를 해결하는 방법(how)을 기술하는 것이 아니라 문제 그 자체(what)만 기술하면 문제가 해결되다는 것이다. 대표적인 언어로들로는 Prolog가 가장 유명하고 CLP, Goedel, Mercury 등이 있다. 지금까지 객체지향 패러다임 외에 대표적인 패러다임에 대해서 간략하게 알아봤는데, 뭐 이것들을 다 이해하자고 설명한 건 아니고, 객체지향 패러다임도 이러한 프로그래밍 패러다임의 하나이고 다른 프로그래밍 패러다임에는 이러한 것들이 있다는 정도만 기억하도록 하자. (3) 객체지향 프로그래밍 언어의 족보이 세상에 객체지향 프로그래밍 언어는 너무도 많다. 우리가 얼핏 들은 것만 해도 C++, Java, Delphi Object Pascal, Python, Smalltalk 등 여러 가지가 있다. 이런 언어들은 도대체 어떤 것에 영향을 받아 만들어지고 또 어떤 언어들에게 영향을 주고 있는지를 대충 알아보면 재미가 있을 것 같지 않은가? 우리가 잘 알고 있는 몇 가지 객체지향 언어에 대해서 그 족보를 한번 살펴보도록 하겠다. Simular ------------------>SmalTalk-------------------->Java 그림 1에서 보듯이 객체지향 언어의 시초는 Simula이다. 1964년도에 Ole-Johan Dahl에 의해 처음으로 만들어진 이후 1967년도에 Simula67로 개선되었다. 그 후 Simula의 아이디어에 감동을 받아 개발된 언어는 Smalltalk이다. Smalltalk는 Simula의 개념을 한 단계 발전시켰고 이 때부터 객체지향의 시대가 오기 시작했다. 그 후에 AT&T Bell Labs에서 연구하던 머리가 좀 벗겨진 Bjarne Stroustrup은 C 사용자들이 객체지향 프로그래밍을 좀 더 쉽게 할 수 있도록 C++ 라는 언어를 개발했다. 이 후 C++는 C 사용자들을 포용하면서 급속도로 확산되어 현재 가장 인기 있는 객체지향 언어들 중 하나가 되었다. C를 확장한 것이 C++라면 Pascal을 확장한 Object Pascal이 있다. 사실 진짜 Object Pascal은 Apple에서 개발되었는데 현재 Delphi라는 개발 도구에서 사용되는 Object Pascal과는 다르다. 그래서 Delphi에서 사용되는 Object Pascal은 Delphi Object Pascal이라 부르기도 한다. 그 후 Sun의 James Gosling이라는 사람에 의해 C++의 복잡함을 줄이고 Smalltalk의 장점을 얻어 Java라는 새로운 객체지향 언어를 만들어냈다. Java 역시 C++와 함께 최고의 인기를 누리고 있는 객체지향 언어이다. 지금까지 별 도움 예기는 안하고 잡담만 늘어 놓은 것 같다. 다음호부터는 본론에 들어가서 객체에 대한 이야기부터 최대한 이해하기 쉽게 설명하도록 노력하겠다. 다음호를 기대해주기 바란다. 2. 객체(Object)(1) 용어의 의미객체지향 프로그래밍이란 말을 한번 자세히 들여다 보자. 영어로는 "object-oriented programming"이라 표기한다. 여기서 "object"를 "객체"로, "oriented"를 "지향"으로 번역해서 "객체지향"으로 사용한다. "object"란 용어를 사전에서 찾아보면 뭐 뜻이 많다. "물건/물체/대상/목적" 등 여러 뜻이 있는데 "물건/물체"라는 의미로 쓰인 것이고 "oriented"란 용어를 찾아보면 "...를 지향하는"이란 의미로 쓰였다. 즉, 물건이나 물체를 지향하면서 프로그래밍을 한다는 의미가 바로 객체지향 프로그래밍의 말뜻이 되는데, 뭐 이런 거는 대부분 다 잘 알고 있으리라 생각한다. 객체는 우리 현실에서 보고 느끼고 생각하는 모든 것을 일컫는 말이다. 우리 주변에서 항상 볼 수 있는 자동차, 오토바이, 책, 노트, 책상, 컴퓨터, 컵, 거울, 볼펜 등 구체적인 형상이 드러나는 것도 물론 객체가 될 수 있으며, 물, 맛, 냄새, 온도, 습도, 압력, 바람 등도 물론 눈으로 볼 수는 없지만 명백히 존재하는 것이므로 이것들도 객체가 될 수 있다. 그렇다면 존재하지 않으면 객체가 아닌가? 절대 그렇지 않다. 상상 속의 동물 용이나 해태, 천사, 조물주, 옥황상제 같은 것들도 객체이다. 뿐만 아니라 구체적인 이름이 붙여져 있지 않은 것도 가능하다. 예를 들면 "유명한 시인", "재미없는 개그맨", "이쁜 여자" 등을 들 수 있겠다. 결국 이렇게 설명하고 보면 우리가 생각할 수 있는 모든 것이 객체가 될 수 있는 것이다. (2) 객체지향 프로그래밍에서의 객체그런데 객체지향 프로그래밍에서는 그런 객체를 컴퓨터라는 제약 내에서 표현해야 하기 때문에 객체는 "상태(state)"와 "행위(behavior)"로 나누어서 생각한다. 즉 모든 객체는 자신의 상태들을 가지고 있으며 또 그것이 행할 수 있는 행위들을 가진다는 것이다. 예를 들어서 자동차라는 객체를 한번 생각해보자. 객체: 자동차 우리가 일반적으로 생각할 수 있는 자동차의 상태와 행위는 위와 같다. 비단 자동차뿐만 아니라 다른 여러 객체들에 대해서도 이런 식으로 분석을 할 수 있을 것이다.그렇다면 왜 궂이 상태와 행위의 관점으로 객체를 바라보아야 하는가? 그 이유는 조금전에도 이야기했지만 컴퓨터라는 상당히 제약적인 곳에서 표현해 내야 하기 때문이다. C나 파스칼 등의 프로그래밍을 직접 해본 사람이라면 느꼈겠지만 프로그램은 데이터(data)와 프로시져(procedure)로 구성된다. 메모리에 데이터가 있고 그 데이터를 이리저리 지지고 볶는 프로시져(procedure)가 있다. 이러한 제약에서 객체를 표현하기에는 상태와 행위로 분석해 내는 것이 편리한데, 상태는 바로 데이터에 해당하고 행위는 바로 프로시져에 해당하기 때문이다. 그렇다고 아무 상태와 아무 행위를 묶는다고 해서 올바른 객체가 되는 것을 아니다. 어떤 이는 다음과 같이 객체를 정의하고 있다. 객체는 관계된 데이터와 프로시져를 묶은 소프트웨어 패키지이다. 이런 방식으로 하나의 객체만 더 생각해보자. 대상은 바로 "사람"이다. 먼저 아래 필자의 분석을 보기 전에 스스로 한번 생각해보기 바란다. 과연 사람은 어떤 상태와 행위를 가지는 지를... 분석한 결과는 어떠한 관점으로 바라보느냐에 따라 천차만별로 달라질 수 있으므로 필자의 것과 다르다고 하여 자학 내지는 자멸하는 사태는 없길 바란다. 오히려 동일한 사람이 이상한 사람이라 하여 위로하기 바란다. 객체: 사람 필자의 경우는 처음 3가지의 행위를 주로 행하는데 독자의 경우는 어떤지 한번 생각해보라. 여자의 경우는 키나 나이 몸무게 등을 직접 드러내는 것을 상당히 꺼리는데 자신의 상태를 직접 드러내는 일은 가히 유쾌한 일을 아닐 것이다. 객체의 경우도 마찬가지이다. 객체는 다른 객체가 요청한 메시지에 응답만 하길 원할 뿐 객체의 상태를 직접 드러내어 다른 객체가 직접 값에 접근할 수 있는 것을 싫어한다. 이는 객체의 독립성과 관련된 것인데 외부 객체가 직접 상태의 값을 바꾸는 등의 일을 행하는 것을 객체의 의존성을 증가시키고 뜻하지 못한 값의 변경으로 인해 객체가 오동할 가능성마저 높아지므로 이런 일은 피해야 한다. 어떤 이의 키를 알고 싶다고 하여 직접 줄자를 가지고 그 사람의 키를 잰다면 상당히 기분 나쁠 것이다. 대신 "키가 얼마나 되십니까?"라는 메시지를 보냄으로써 상대방이 그에 대답할 수 있도록 하는 것이 바람직하다. 필자의 경우 누군가 그런 질문을 한다면 5cm 정도 높여서 말하는데 이는 상대방이 요청한 메시지에 필자가 직접 적절한 반응을 할 수 있기 때문이다. 여기서 간단히 자바로 객체에 대한 예를 보이려고 한다. 그런데 안타깝게도 자바에서는 객체를 바로 표현할 수 있는 방법이 없다. 자바는 클래스 기반(class-based) 언어라 하여 객체를 만들어 내기 위해서는 반드시 클래스라는 것을 통하도록 되어 있다. 대부분의 객체지향 언어가 바로 이 클래스 기반 언어이기는 하지만 객체 기반(object-based) 언어에서는 클래스가 없고 객체를 직접 표현할 수 있는 방법을 제공한다. 클래스에 대한 설명은 차후에 자세히 거론 되겠지만 우선 예제를 이해하기 위해서 간단히 설명하자면 클래스는 객체를 만들어내기 위한 틀이라고 생각하면 쉽다. 붕어빵을 만들기 위해서는 붕어빵을 구워낼 수 있는 틀이 필요하다. 즉, 클래스를 정의하는 것은 객체를 구워내기 위한 틀을 만드는 것이고 그 후에 다시 그 틀에서 객체를 구워내는 일을 해야 한다. 먼저 틀을 정의해보자. class Person { 이전에 예에서 보였던 사람이라는 객체를 만들어내기 위한 틀(클래스)을 만들었다. 클래스를 정의하기 위해서는 위와 같이 "class"라는 키워드 다음에 클래스의 이름을 기술하고 상태와 행위는 "{","}" 사이에 정의하도록 되어 있다. 상태와 행위의 정의는 순서가 따로 없다. 필자의 경우는 상태를 먼저 정의하였다. 여기서 "private"는 외부 객체가 접근할 수 없다는 의미이다. 우리가 흔히 프라이버시(privacy)라는 말을 쓰는데 이 역시 밝힐 수 없다는 의미이다. 그리고 키, 나이, 몸무게의 값은 일단 정수로 정했다. 혹자가 "내 몸무게는 70.5kg인데..."라고 말한다면 따지지 마라고 말하고 싶다. 자바에서는 이러한 상태를 필드(field)라는 말을 대신 사용하기도 하므로 기억해두기 바란다. 행위의 정의는 역시 외부에 드러나도록 하기 위하여 "public" 이라고 지정해 두었다. 이것은 외부 객체가 행위를 요청할 수 있다는 것이다. 어떤 독자는 이렇게 말할 수도 있을 것이다. "내 똥은 내가 싼다(excrete)! 그러므로 excrete는 public이어서는 안된다." 맞는 말이다. 행위라고 해서 모두가 public일 필요는 없다. 내부적으로만 일어나는 행위인 경우에는 public으로 두어서는 안된다. 외부에 드러날 것이지 아니면 내부에서만 접근할 것인지는 객체가 어떤 목적으로 만들어졌는지에 따라 달라질 수 있으므로 신중하게 선택해야 할 것이다. 이렇게 만들어진 틀에서 이제 실제 객체를 만들어낼 수 있다. 틀에서 객체를 구워내는 것은 다음과 같이 "new"라는 연산자를 통해서 이루어진다. Person A; "Person A"라고 한 것은 A는 하나의 Person 이라는 의미이고 "new Person()"은 Person 클래스로부터 하나의 객체를 구워내라는 명령이다. 그리고는 그 객체를 A로 둔 것이다. 이것은 한꺼번에 "Person A = new Person()"이라고 할 수 있다. 이로써 두 개의 사람 객체 A, B가 탄생한 셈이다. 이제 우리는 이 두 객체에 여러 가지 행위를 요청할 수 있다. A는 먹고 B는 싸라는 행위는 요청하려면 다음과 같이 하면 된다. A.eat(); 이처럼 어떤 객체에 행위를 요청하는 것은 객체 이름과 행위 이름 사이에 ".(dot)"넣어서 호출하게 된다. (3) 객체의 저장 방식 클래스를 통해서 객체를 생성하든(클래스 기반 언어인 경우) 아니면 직접적으로 객체를 생성하든(객체 기반 언어인 경우) 상관없이 객체는 메모리 공간을 차지하게 된다. 따라서 객체의 상태와 행위가 어떠한 형태로든 저장이 되어야 하는데, 우리가 가장 쉽게 생각해낼 수 있는 방법은 하나의 테이블(table)을 만들어서 저장하는 것이다. 위에서 예로든 자동차 객체를 저장하는 방식을 살펴보자. 자동차 객체
위와 같이 객체의 상태와 행위를 표와 같이 저장하여 요청되는 상태나 행위를 표를 참조하여 상태의 값이나 행위를 수행하게 된다. 여기서는 상태와 행위를 따로 저장하지 않고 하나의 표에다가 모두 저장하는데 다른 방식에서는 그것들을 나누어서 따로 저장하기고 하고 서로 표를 공유하기도 한다. 그런 방식은 나중에 다시 자세히 설명할 것이므로 우선은 이렇게 이해하고 있도록 하자. 이번 호에서는 객체에 대한 의미와 실제 객체지향 프로그래밍에서는 어떤 의미로 이해하는지 그리고 실제 어떤 컴퓨터에서 표현되는지에 대해서 알아보았다. 다 3. 클래스(Class)필자가 처음에 글을 써 나가는 방식에 대한 언급을 조금 했었다. 그 중 하나가 새로운 개념을 설명하기 전에 그것이 왜 필요하게 되었는지에 대해서 설명하겠다고 했다. 지금 클래스라는 개념을 설명하기 이전에 그것이 왜 필요한지에 대한 설명을 먼저 하도록 하겠다. (1) 왜 클래스가 필요한가?객체지향 소프트웨어라 함은 그 자체가 객체들로 구성되어 있으며 그것들이 서로 관계를 맺고 있고 서로 메시지를 주고 받으면서 작동하는 것이라 하겠다. 이러한 관점에서 보면 결국 "객체"만이 필요할 뿐 다른 어떤 것도 필요치 않다. 사실이다. "객체지향 소프트웨어"에서는 "객체"만이 존재할 뿐이다. 그렇다면 분명 뭔가 불편한 점이 있고 그것을 해결하기 위한 방편으로 클래스라는 것이 생겨났을 것이다. 여기서는 간단한 그래픽 편집기를 만든다는 생각으로 클래스의 필요성에 대해서 설명해가도록 하겠다. 사각형, 원, 점, 선의 간단한 도형들을 크기, 굵기, 위치, 색상 등의 변수를 줘서 어떤 그림을 그릴 수 있도록 하는 에디터를 만든다고 생각해보자. 무슨 마우스 제어니 화면 제어니 등등의 복잡한 다른 요소들을 생각하기 이전에 먼저 그런 도형들이 객체로써 존재해야 할 것이다. 왜냐하면 우리는 객체지향적 접근방법으로 에디터를 만들 것이기 때문이다. 간단하게 원에 대해서 다음과 같이 가짜 코드로서 객체를 정의할 수 있을 것이다. object Circle { 위와 같이 좌표 50, 50에서 반지름이 20인 원에 대한 객체를 정의하였다. 객체의 이름은'Circle'이고 메모리에 할당되어 있으며 각 필드(x, y, radius)에 값도 정해져 있다. 그런데 생각을 해보면 그래픽 편집기를 만드는데 이런 원에 대한 객체가 필요한 것이 아니라 원을 만들어 낼 수 있는 객체가 필요한 것이다. 왜냐하면 그 이유는 똑똑한 독자들은 이미 파악을 했겠지만 그래픽 편집기에서 사용자가 도대체 몇 개의 원을 그릴지 몇 개의 사각형을 그릴지 선을 그릴지 안 그릴지는 모르는 법이다. 물론 그래픽 편집기에서는 정확한 갯수를 할 수 없는 극단적인 경우지만 예를 들어 눈사람을 직접 그리려고 한 경우는 여러 개의 원 객체로 만들 수 있다. 머리(1개), 눈(2개), 몸통(1개), 코(1개) 도합 5개의 서로 크기가 다른 원이 필요하다. 그렇다면 비슷한 원 객체를 5번 정의해야 하는가? 당연히 5번 정의해야 한다. 그런데 이거 너무 불편하다는 생각이 들 것이다. 이처럼 여러 개의 객체 혹은 생성할 객체의 갯수가 명확하지 않을 때 클래스를 사용하면 매우 편리하다. (2) 자바에서의 클래스우리가 길가다가 붕어빵을 사먹을 때 "아줌마 붕어빵 5개 주세요"라고 하면 아줌마는 붕어빵 5개를 직접 만들지 않고 간단하게 빵틀에다가 구워낸다. 또 아줌마 입장에서는 붕어빵을 몇 개 팔지 알 수 없기 때문에 붕어빵틀을 가지고 장사를 나가는 것은 매우 편리하다. 이와 같은 맥락에서 위의 원 객체에 대한 클래스를 자바로 만들어보자. class Circle { 이렇게 정의된 Circle 클래스는 실제 메모리를 할당하고 있는 객체가 아니라 객체를 만들어 낼 수 있는 틀(template)이다. 이제 이 틀에서 빵을 구워내야 하는 것이다. 그럼 "빵을 구워내라"는 연산이 또 필요하게 된다. 여기서 클래스라는 빵틀에서 객체라는 빵을 구워내기 위해 사용하는 특별한 연산자가 바로 "new" 이다. Circle1 = new Circle(50, 50, 20); 위 정의에서 Circle(_x, _y, _r) 이라는 것이 있는데 이것은 흔히 우리가 생성자(constructor)라고 부르는 특별한 함수이다. 우리가 붕어빵을 주문할 때 "아줌마 앙꼬 많이 넣어주세요" 라고 말할 때 이 "앙꼬"의 양은 주문자가 원하는 만큼 넣을 수 있는 것이다. 이처럼 우리가 원을 만들 때에서 원의 위치와 반지름의 크기는 생성하는 사람이 정할 수 있도록 하면 매우 편리할 것이다. 이 같은 역할을 수행하기 위해서 사용하는 것이 생성자이다. 이것은 객체가 메모리에 생성된 이후 가장 처음 호출되는 함수로써 보통 초기값을 주기 위해 사용되는 것이 일반적이다. 자바의 클래스에서는 생성될 객체의 각 필드(field)에 기본 값(default value)을 둘 수 있도록 하고 있다. 예를 들어 원을 생성할 때 기본 좌표는 (100, 100)이고 반지름이 50이 되도록 한다면 다음과 같이 클래스를 정의한다. class Circle { 이제 객체의 메소드에 대한 예를 보여주겠다. 원 객체에 대한 예제에 대한 확장으로 Circle 클래스를 좀 더 확장해보자. 우리가 원 이라는 객체에 대해서 요구할 수 있는 행위는 여러 가지가 있을 수 있겠는데 여기서는 이동하라(move)와 반지름을 변경하라(setRadius)를 보여주도록 하겠다. 이 두 가지를 포함하여 Circle 클래스를 확장하면 다음과 같이 될 것이다. class Circle { 위에서 "move" 메소드는 두 개의 인자(가로 이동 거리 dx, 세로 이동 거리 dy)를 받아 원을 이동하도록 하는 메소드이고, "setRadius"는 새로운 반지름 값을 받아서 반지름을 변경하는 것이다. 클래스 Circle에서 정의된 메소드는 모두 클래스에서 굽혀질 객체의 것이 될 것이기 때문에 객체가 만들어지기 전까지는 이 모든 메소드들은 아무런 의미가 없다. 여기서 잠깐 setRadius 메소드를 살펴보자. 여기서 반지름 값을 radius라는 이름으로 받고 객체 자신의 반지름 속성인 radius를 변경해야 하는데 두 개의 이름이 같아서 구분하기가 명확하지 않다. 여기서 객체 자신의 반지름 속성이라는 것을 나타내기 위해서 this.radius라고 표현하였다. "this"는 객체 자신을 가리키는 것이다. 엄밀하게 따지자면 move 메소드에서도 this.x = this.x + dx; this.y = this.y + dy 라고 해야 되지만 편의상 생략이 가능하도록 하고 있다. (3) 가시성(Visibility)가시성은 얼마나 객체의 내부를 들여다 볼 수 있는지에 관한 것이다. 하나의 객체라도 내부적으로 숨기고 싶은 것이 있고 외부에 드러낼 수 있는 것이 있다고 저번 시간에 예를 들어 설명했었다. 그렇다면 이러한 가시성을 표현하기 위해서는 클래스가 객체를 생성할 틀이 되므로 여기에 표현해 주어야 한다. 자바에서는 다음과 같이 모두 4가지의 가시성의 종류를 제공하고 있다.
우선 'package' 가시성 지정자는 자바에서 제공하는 패키지(package)에 관련된 것이다. 동일한 패키지에 속하는 객체들 사이에서는 접근할 수 있는 것이다. 두 번째 'private' 지정자는 객체 내부에서만 사용할 수 있는 것이다. 즉 객체 외부에서는 접근할 수 없다. 그리고 'protected'의 경우는 나중에 설명될 상속(inheritance)와 관계된 것이나 결국 객체 내부에서만 사용할 수 있다는 것은 다르지 않다. 이는 나중에 상속이 설명될 때 다시 자세히 설명될 것이다. 'public'은 객체 외부에서 접근할 수 있도록 지정하는 것이다. 다음에 예제 코드를 보도록 하자. class Circle { 계속 예를 들던 Circle 클래스에서 x, y, radius 필드는 모두 private로 지정했고 그 외의 move, setRadius는 public으로 선언하였다. 일반적으로 속성(혹은 필드)는 객체 내부에 감추고 행위(혹은 메소드)는 밖으로 드러내지만 항상 그런 것은 아니다. 어떤 언어에서는 무조건 이런 원칙을 따르도록 언어에서 규정하고 있지만 자바에서는 지정자를 사용자가 의도에 따라 둘 수 있도록 하고 있다. 원(Circle) 이라는 객체를 생각해보면 외부 객체가 마음대로 좌표와 반지름을 바꾸도록 허용하는 것은 문제가 있다. 만약 그것을 원한다면 객체에게 요청하는 것이 바람직하고 직접 내부 값을 변경하는 것을 막아야 하므로 위와 같이 선언한 것이다. 만약 원 객체를 실제로 화면 그리는 경우라면 더욱 그 필요성을 느낄 수 있는데 원의 좌표만 바꾼다고 화면의 원도 이동하는 것은 아니다. 즉 move 라면 메소드의 요청에 의해서 처리하는 경우에만 화면의 원도 이동할 수 있도록 처리할 수 있을 것이다. 그리고 모든 메소드를 public으로 처리해서도 안된다. 내부적으로만 사용할 목적으로 만든 메소드라면 private(혹은 protected)로 두어야 할 것이다. (4) 클래스도 객체인가?순수하게 객체지향을 생각한다면 모든 것이 객체이다. 물론 이러한 생각이 항상 옳고 효율적인 것이 아니라는 것은 첫 번째 글에서 피력했다. 이렇게 모든 것을 객체로 생각한다면 클래스도 객체이다. 그렇다면 클래스는 객체를 생성하는 객체로 생각해 볼 수 있다. 물론 그것이 생성하는 객체는 자신과 닮았으며 부차적으로 객체 생성의 요청에 응할 수 있는 메소드까지 포함하게 될 것이다. 필자가 연재하는 다른 글인 '객체 이론'의 글은 다른 여러 학자들의 객체에 대한 수학적 이해를 다룬 연구를 소개하고 있는데 여기서도 그러한 이해를 하고 있다. ('객체이론(3)' 글을 참고하기 바란다) 그러나 대부분의 객체지향 언어에서는 그러한 관점보다는 클래스를 객체와는 다른 특별한 어떤 것으로 간주하고 있다. 물론 자바도 다르지 않다. 이번호에서는 클래스에 관해서 이리 저리 주절거려 봤다. 클래스가 왜 필요한지 그건 어떻게 쓰는지 그리고 클래스를 객체 관점에서 이해할 수 있는지에 대한 내용을 OOP (object-oriented programming) ; 객체지향 프로그래밍 객체지향 프로그래밍(이하 줄여서 'OOP'라 칭함)은 컴퓨터 프로그램의 개발을 완전히 새로운 시각으로 바라다보는 혁명적 개념이라 할 수 있는데, 동작보다는 객체, 논리보다는 자료를 바탕으로 구성된다. 프로그램은 전통적으로 논리적인 수행 즉, 입력을 받아 처리한 다음, 결과를 내는 것이라는 생각이 지배적이었다. 또한 프로그래밍을 한다는 것은 어떻게 자료를 정의할까 보다는 어떻게 논리를 써나가는 것인가로 간주되었다. 그러나 OOP는 프로그램에서 정말 중요한 것이 논리보다는 오히려 다루고자 하는 객체라는 시각에서 접근하고 있다. 객체의 예로는, 사람(이름, 주소 등으로 묘사되는)에서부터 건물까지, 그리고 상품 판매를 위한 매장(특성이 서술되고 다뤄질 수 있는)에서부터 컴퓨터 바탕화면의 아주 작은 요소인 버튼이나 스크롤바 같은 것들까지를 모두 망라한다. OOP에서의 첫 단계는 다루고자 하는 모든 객체와, 그것들이 서로 어떤 연관성이 있는지를 식별하는 - 흔히 데이터 모델링이라고 부르는 - 작업이다. 일단 모든 객체를 식별했으면, 객체 클래스로 일반화하고 (플라톤의 "이상적" 의자 개념이 모든 의자를 대표한다고 생각하는 식으로), 그것이 담고 있는 데이터의 종류와 그것을 다룰 수 있는 모든 논리 순서를 정의한다. 논리 순서는 메쏘드(method)라고 부르며, 클래스의 실제 인스턴스(instance)를 하나의 "객체"라 하거나, 어떤 상황에서는 하나의 "클래스 활성체"라 한다. 객체 또는 활성체는 컴퓨터 내에서 실제로 수행되는 것이다. 메쏘드는 컴퓨터 명령어를 규정하고, 클래스 객체의 특성은 관련 데이터를 규정한다. OOP에 사용된 개념과 규칙은 다음과 같은 중요한 이점을 제공한다.
Smalltalk 는 최초의 객체지향 프로그래밍 언어 중 하나이며, C++와 Java는 최근 가장 인기있는 객체지향 프로그래밍 언어이다. C++의 부분집합이라고 할수 있는 Java는 특히 기업이나 인터넷의 분산 응용프로그램에 사용되도록 설계되었다. |