Skip to content

Latest commit

 

History

History
342 lines (179 loc) · 19.2 KB

File metadata and controls

342 lines (179 loc) · 19.2 KB

2. 객체라는 관점에서 생각하는 방법

객체지향의 설계의 기본단위는 클래스다.

객체지향 설계의 최종 결과는 강력하고 기능적인 객체 모델, 즉 완전한 시스템이다.

인생에는 정답이 없다.

일반적으로 동일한 문제에 대한 해결 방법이 여러 가지다.

따라서 객체지향 솔루션을 설계하려고 할 때 처음부터 완벽하게 설계하려고 에쓸 필요는 없다.

언제나 항상 개선할 부분이 있을 것이다.

우리가 해야하는 일은 브레인스토밍을 통한 생각하는 과정이 다른 방향까지 다른 방향으로까지 나아가게 하는 것이다.

생각

최근에는 객체지향을 사용하여 설계할 때 정답이 없다는 것을 많이 느끼고 있다.  

많은 좋은 개발 서적에서 시작하는 말은 정답은 없다라는 말로 시작하는 부분이 인상적이다.

아직 개발의 역사가 100년정도 밖에 되지 않아서 일까.. 발전이 너무 빨라서일까 프로그래밍은 수학적인 능력만을 요구하는게 아닌 다양한 능력이 요구되는 `기예`에 가깝다.

사실 객체지향뿐만 아니라 설계과정에서는 특정 프로그래밍언어를 고려하지 마라.

업무에 있어서 첫 번째 순서는 문제를 식별하고 해결하는 것이다.

문제를 분석하다 보면 필요한 기술이 보일 것이고 해당 기수에 맞는 솔루션을 고려해야 한다.

앞서 다룬 구조적 프로그래밍과 객체지향적 프로그래밍 모두 공존한다..

실제로 작성중인 객체지향 코드만 봐도 구조적 구문을 사용중이다..

하지만 다른 예로 객체지향 설계로 전환하기 위해선 다른 종류의 투자가 필요하다.

객체지향 언어로 옮겨 탈 생각이라면 무엇보다 먼저 객체지향의 개념을 익히고 객체지향 방식으로 생각하는 과정을 익히는 일에 투자해야 한다.

이렇게 시작하지 않는다면 프로젝트가 객체지향적이지 못하거나 완전히 객체지향 감각 상실적인 망작이 될 것이다.

객체지향 사고 방식을 이해하기 위해 세 가지 중요한 사항을 다룬다.

  1. 인터페이스와 구현부의 차이점을 아는 것
  2. 더 추상적으로 생각하기
  3. 사용자에게 가능한 한 인터페이스를 적게 제공하기

2.1. 인터페이스와 구현부의 차이점을 아는 것

객체지향 설계를 구축하는 비결 중 하나는 인터페이스(interface)와 구현부(implementation)의 차이점을 이해하는 것이다.

클래스를 설계할 때 사용자가 알아야 할 사항, 그리고 아마도 더 중요한 사용자가 몰라야 할 사항을 잘 구분해 둬야 한다는 점이다.

캡슐화 시에 고유한 데이터를 은닉하는 메커니즘이란 필수적이지 않은 데이터를 사용자로부터 숨기는 수단이다.

자동차를 기준으로 인터페이스는 운전대, 가속 페달, 브레이크같은 부품에 포함되어 있다.

기본적으로 우리가 보지 못하는 구현부는 일반 운전자들에게 거의 관심이 없다.

그러나 모든 운전자는 일반적인 인터페이스인 운전대를 사용하는 방법을 알고 있다.

자동차에 표준 운전대를 설치함으로써 제조업체는 표적 시장을 이루고 있는 고객들이 시스템을 사용할 수 있다고 확신할 수 있다.

그러나 제조업체가 운전대 대신에 조이스틱을 설치하기로 결정한 경우에 대부분의 운전자에게는 장애물이 될 뿐만 아니라 자동차도 팔리지 않게 된다.

반면, 성능과 감성이 변하지 않는 이상 평균적인 운전자는 제조업체가 자동차의 엔진을 바꿨는지 여부를 알지 못한다.

엔진또한 해당 인터페이스 소속이기 때문에 다른 엔진과 교체될 수 있다.

엔진은 구현의 일부이며, 운전대는 인터페이스의 일부이다.

구현부를 변경해도 운전자에게 영향을 미치지 않아야 하지만, 인터페이스는 변경될 수 있다.

운전자는 비슷한 방식으로 작동하더라도 핸들에 대한 미적 변화를 알아차릴 수 있을 것이다.

인터페이스와 구현부라는 두 부분만으로 클래스를 설계해야 클래스가 제대로 합성이 된다.

2.1.1. 인터페이스

최종 사용자에게 제공되는 서비스들에 맞춰 인터페이스가 합성된다.

최종 서비스에게 필요한 서비스, 바로 그것만 제공하는 게 최선이다.

물론, 사용자에게 필요한 서비스라는 게 의견에 따라 달라질 수 있다.

한 방에 열명이 있게 한 다음에 그들이 각자가 독립적인 설계를 하도록 요청하면 완전히 다른 열 가지 설계를 받을 수 있는데, 이럴지라도 아무런 문제는 없다.

그러나 일반적으로 클래스에 대한 인터페이스에는 사용자가 알아야 할 내용만 포함헤야 한다.

사용자는 토스터가 인터페이스(이 경우에는 전기 콘센트)에 연결되는 방법과 토스터자체가 작동하는 방법만 알아야 한다.

사용자 식별: 클래스를 설계할 때 가장 중요한 고려 사항은 클래스의 시청자, 즉 클래스를 사용하는 사람을 식별하는 것이다.

2.1.2. 구현부

구현부의 상세 내용(세부사항)은 사용자에게 드러나지 않는다.

구현부에 관한 한 가지 목표를 명심해야 한다.

구현부를 변경할지라도 사용자는 자신의 코드를 변경하지 않아도 되게 해야 한다.

다소 혼란스러워 보일 수 있지만, 이것이야말로 설계 시의 핵심 목표이다.

좋은 인터페이스: 인터페이스가 올바르게 설계된 경우 구현부가 변경되어도 사용자 코드를 변경하지 않아도 된다.

핸드폰의 전화기능을 예로 들어보자.

핸드폰 마다 전화를 거는 인터페이스는 간단하다.

공급자가 소프트웨어를 업데이터 하더라도 전화를 거는 방법은 바뀌지 않는다.

구현부를 어떤 식을 변경했든지 간에 상관없이 인터페이스가 동일하게 유지된다.

  • 기본적으로 고객은 사용하던 인터페이스가 익숙하기 때문에 변경을 싫어한다.

토스트기의 예제로 돌아가서 토스트의 전기 콘센트를 사용하여 전류를 공급받으면 문제없이 작동한다.

토스트키는 해당 인터페이스를 준수하여 제작되었기 때문에 문제가 없다.

마찬가지로 전류를 공급해주는 발전소또한 전류 공급 인터페이스가 존재한다.

해당 인터페이스를 준수하여 전류를 공급 해야 하는데 만약 1번 공장에선 교류를 2번은 직류를 생산한다면 문제가 발생한다.

따라서 사용자든 구현부든 모두 인터페이스를 준수해야 한다는 것이다.

2.1.3. 인터페이스/구현부 예제

간단한 예제로 DatabaseReader클래스를 만들어 본다.

앞서 이야기 했지만 설계를 할 때는 사용자에 맞게 설계를 진행해야 한다.

요구조건을 보고 그에 맞는 설계를 가져간다.

인터페이스는 프로그래머가 사용할 API인 셈이다.

각 요구 사항에 맞는 기능을 담당할 메서드가 필요하다.

여기서 몇 가지 생각해야 하는 부분이 있다.

  1. 이 클래스를 효과적으로 사용하기 위해서 프로그래머가 구현 세부사항을 알아야 하는가?
  2. 데이터베이스의 내부에 있는 코드가 데이터베이스를 어떻게 여는지를 알아야 하는가?
  3. 특정 레코드에 물리적으로 어떻게 위치하는지를 내부 데이터베이스 코드가 판별하는 방법을 알아야 하는가?
  4. 더 많은 레코드가 남아 있는지 여부를 내부 데이터베이스가 코드가 판별하는 방법을 알아야 하는가?

아무리 생각해도 답변은 NO다.

우리는 이 정보를 알 필요도 없고 알아서는 안된다.

그저 적절한 반환값을 얻고 작업이 올바르게 수행된다는 점만 신경쓰면 된다.

최소화한 인터페이스: 극단적인 경우도 있지만 최소화한 인터페이스를 결정하는 한가지 방법은 인터페이스를 제공하지 않는 것이다. 물론 그런 클래스는 쓸모없지만 이후 협상을 통해 인터페이스를 추가하는 것이다.

2.2. 인터페이스 설계 시 추상적으로 생각해 보기

객체지향 프로그래밍의 주요 장점 중 하나는 클래스를 재상용할 수 있다는 점이다.

일반적으로 재사용 가능한 클래스는 구상적이라기보다는 오히려 더 추상적인 인터페이스를 갖는 경향이 있다.

구상 인터페이스(concrete interfaces)는 매우 구체적인 경향이 있지만, 추상 인터페이스(abstract interfaces)는 더 일반적이다.

그러니 단순히 아주 추상적인 인터페이스가 매우 구상적인 인터페이스보다 더 유용하다고 말하는게 아니라(종종 그러기는 함), 항상 그렇지는 않다고 말하는 것이다.

전혀 재사용할 수 없지만, 매우 유용하고 구상적인 클래스를 작성할 수는 있다.

그러나 우리는 현재 설계 작업을 하고 있으며, 객체지향이 우리에게 제공하는 면을 이용하고 싶다.

따라서 우리는 재사용 가능한 추상 클래스를 설계하는 것을 목표로 삼아야 한다.

택시를 예로 들어 우회전, 좌회전, 정지등과 같이 별도의 인터페이스를 가지는 것 보다 공항으로 가주세요 같은 인터페이스를 갖는 것이 휠씬 더 유용하다.

사용자는 도착하기만 하면 그만이기 때문

실제로 호텔에서 나와 택시를 탄다면 택시기사는 "어디로 갈까요?"라고 물을 것이고 우리는 "공항으로 가주세요"라고 답할 것이다.

물론 도시에 공항이 하나만 있다면 위처럼 말하거나 여러개라면 인천공항등의 Type이 붙을 것이다.

따라서 "공항으로 가주세요"같은 추상 인터페이스는 일반적으로 시카고나 뉴욕같은 도시별로 구현방식을 다르게 하여 재사용 가능하기 때문에 더 유용하다.

2.3. 가능한 한 사용자에게 인터페이스 적게 제공하기

클래스를 설계할 때는 사용자에게 클래스의 내부 작업에 대해서 될 수 있으면 언제든지 최소한의 내용만 알려주는게 일반적인 규칙이다.

  • 사용자에게 꼭 필요한 것만 제공하자.

이것은 클래스에 될수록 적은 인터페이스가 있음을 의미한다. 클래스 설계를 시작할 때 최소한의 인터페이스로 시작하자.

클래스 설계는 반복적이므로 우리는 최소한의 인터페이스 세트로는 충분하지 않을 수 있음을 곧 알게 될 것이다.

  • 사용자가 필요로 하는 것보다 더 많은 인터페이스를 제공하는 것보다 실제로 필요할 때 제공하는 편이 바람직하다.

때때로 사용자가 특정 인터페이스에 접근하는 일이 큰 문제가 된다.

예를 들어, 모든 사용자에게 급여 정보를 제공하는 인터페이스는 필요하지 않다. 알아야 하는 사용자만 알면 된다.

  • 우선, 하드웨어 예제를 사용해 소프트웨어 예제를 이해해 보자.

모니터와 키보드 없이 사용자에게 컴퓨터 상자를 전달하는 일을 상상해보자

이런 경우라면 컴퓨터는 사용되지 않을 것이다.

PC에 대한 최소한의 인터페이스 세트를 사용자에게 제공해야 한다.

그러나 이 최소 세트로는 충분하지 않으며, 곧 인터페이스를 추가해야 할 것이다.

  • 공개 인터페이스를 지정하여 사용자가 바로 접근할 수 있는 대상을 정의한다.

우리가 처음에는 인터페이스를 비공개로 지정해 클래스 전체를 사용자로부터 은닉했다고 할지라도, 프로그래머가 이 클래스를 사용하기 시작하면서 일부 메서드를 공개로 지정해야겠다는 압박을 받게 되면 그런 메서드를이 공개 인터페이스가 되는 것이다.

  • 정보 시스템의 관점에서 클래스를 설계하기보다는 사용자의 관점에서 클래스를 설계해야 한다.

클래스 설계자가 특정 기술 모델에 맞도록 클래스를 설계하는 경우가 너무 흔하다.

설계자가 사용자의 관점에서 보려고 애를 쓰더라도 여전히 기술자의 관점에서 벗어나기는 힘들며, 이러한 상황에서는 클래스가 기술적인 관점에서 작동하도록 설계되므로 사용자는 클래스를 사용하기 쉽지 않게 된다.

  • 요구사항과 설계를 다루는 클래스를 설계할 때 개발자뿐만 아니라 실제로 사용하는 사람들과 함께 설계해야 한다.

시스템 프로토 타입을 만들 때 이러한 클래스를 개선하고 보강할 여지가 크다.

생각

대략적으로 알고 있던 내용을 전체적으로 살펴주니 이해가 잘된다.

그 중에서 경험적인 부분이 있어서 적어본다.

1. 사용자에게 필요 이상의 API를 노출하지 말아야 한다는 부분

초보 개발자들이 많이 하는 실수 중 하나가 클래스를 설계하고 이후 다른 사용자가 해당 클래스의 인터페이스를 사용할 때 필요 이상의 API를 오픈하는 문제이다.

왜 그런것인지 내가 왜 그랬는지 생각을 해보니 가장 많이 접하는 API가 바로 라이브러리 메서드들이다.

해당 클래스들은 필요 이상의 예상된 기능들을 많이 넣어두는데 이걸 보고 클래스를 설계할 때도 좀 더 편하게 쓰라고 필요 이상의 인터페이스를 오픈하는 것 같다.

우리가 만드는 클래스와 해당 라이브러리의 성격은 전혀 다르며 자동차를 제작중인 경우에 사용되는 부품정도를 라이브러리라고 볼 수 있을 것 같다..

2. 클래스 설계자가 특정 기술에 맞춰 클래스를 설계하는 경우이다.

앞에서 두번 정도 말했지만 사용자에게 맞춰서 맞는 방법으로 제작해야 하지만 공부나 체험의 명목으로 설계가 되게 되면 원하는 결과물이 나오기 쉽지 않다.  

과거 프로그래밍 패턴을 공부하던 중 해당 패턴을 실제 프로젝트에 적용하고 싶어서 억지로 같은 틀을 끼워넣은 저이 있다.  

지금은 좋은 반면교사이지만, 그 때는 그게 정답인줄 알았다.  

위에서 말하는 정답은 없다와 100명이 설계하면 전부 다른 결과물이 나오는 것 처럼 그 때 설계하고 이후에 필요하다면 기술을 도입하는 방식이 적절하다.

2.3.1. 누가 사용자인지 결정하기

앞서 다룬 택시 예제에서는 사용자를 실제로 시스템을 사용하는 사용자라고 결정했다.

조금 더 생각해본다면 "사용자는 누구인가?"라는 점을 집중하게 된다.

첫 번째는 아마 고객일 것이다.

확실히 고객도 사용자이긴 하지만 택시기사도 마찬가지로 고객에게 서비스를 성공적으로 제공할 수 있어야 한다.

다시 말해서 공짜로 공항까지 태워주세요라고 말하는 고객에게 인터페이스를 제공할 일은 의심할 여지없이 택시 기사와 어울리지 않을 것이다.

따라서 현실에 맞게 사용해 볼 만한 인터페이스를 구축하려면 고객과 택시기사 모두 사용자이다.

요컨대, 택시 객체에 메세지를 보내는 모든 객체는 사용자로 간주된다.

2.3.2. 객체의 행위

사용자를 식별했다면 객체의 행위를 결정해야 한다.

각 객체의 목적과 올바르게 수행해야 할 작업을 모든 사용자의 관점에서 식별하자.

많은 초기 선택지는 최종 공개 인터페이스 선발에서 살아남지 못할 것 이다.

2.3.3. 환경에 따른 제약 조건

길버트, 맥거티의 유명 저서에서 종종 객체가 할 수 있는 작업에 제약을 가한다고 지적한다.

실제로 환경에 따른 제약은 거의 한가지 요인으로 컴퓨터 하드웨어가 소프트웨어의 기능을 제한할 수 있다는 말이다.

택시가 공항으로 가는 길에 도로가 없는 것

2.3.4. 공개 인터페이스 식별

사용자, 객체의 행위, 환경애 대한 모든 정보가 수집되면 각 사용자 객체의 공개 인터페이스를 결정해야 한다.

택시 객체를 어떻게 사용할지 생각해 보자

  • 택시에 탄다.
  • 택시 기사에게 가고 싶은 곳을 말한다.
  • 택시 기사에게 요금을 지불한다.
  • 택시 기사에게 팁을 준다.
  • 택시에서 내린다.

택시 객체를 사용하려면?

  • 갈 곳이 있다.
  • 택시를 부른다.
  • 택시 기사에게 돈을 지불한다.

이 내용을 기반으로 Cabble클래스를 만들어보자.

나열된 인터페이스를 정리하거나 수정하는 일은 반복과정이고 필수적이다.

많은 객체지향 교과서에서 각 인터페이스 모델은 하나의 행위만 수행하라고 권한다.

이것은 우리가 설계를 어떻게 추상화하고 싶은지에 대한 질문으로 돌아가게 된다.

enterTaxi() 인터페이스에 요금을 지불하는 로직이 들어가 있기를 바라지 않을 것이다.

해당 로직이 들어가 있으면 논리적이지 않고 클래스 사용자가 요금을 택시기사에게 지불하기 위해서는 어떤 일이 이뤄져야 하는지를 말할 길이 전혀 없게 된다.

2.4. 구현부 식별

공개 인터페이스를 선택했다면 그다음은 구현부를 식별하는 것이다.

클래스가 설계되고 클래스가 올바르게 다루는 데 필요한 모든 메서드가 준비되었다면 클래스의 작동 방법을 구체적으로 생각해 볼 차례다.

기술적으로 보면, 공개 인터페이스가 아닌 것은 모두 구현부로 간주할 수 있다.

다시 말해서 사용자는 구현부에 속하는 메서드를 전혀 볼 수 없다는 말인데, 이러한 것들로는 메서드의 시그니처도 포함되며, 마찬가지로 사용자는 이러한 메서드 내부의 실제 코드를 볼 수 없다는 말이기도 하다.

클래스에는 클래스 내부에서만 사용하는 비공개 메서드들이 있을 수 있다.

사용자는 비공개 메서드를 볼 수 없고 접근할 수도 없으며 비공개 메서드는 구현부의 일부로 간주된다.

예를 들어, 클래스에 ChangePassword()라는 메서드가 있다고 가정해 보자.

같은 클래스 안에 비밀번호를 암호화하는 비공개 메서드도 있을 수 있다.

이런 경우에 비공개 메서드는 사용자에게 숨겨지고 changePassword() 메서드를 통해서만 호출된다.

구현부는 사용자에게 완벽하게 감춰져 있어야 한다.

공개 메서드 내의 코드 또한 사용자가 볼 수 없기 때문에 구현부에 해당한다.

이는 이론적으로 구현부로 간주되는 모든 것이 사용자 클래스에 인터페이스하는 방식에 영향을 미치지 않으면서 변경될 수 있음을 의미한다.

2.5. 느낀점

객체지향 방식으로 일을 한다는 건 과학이라기보다는 예술에 가깝다.

조금은 객체지향에 대한 강박이 사라진 것 같다..

자꾸 정답을 찾으려고 노력하는 스트레스가 있었는데 해결된 기분,,