2017년 1월 14일 토요일

JAVA와 객체지향 프로그래밍에 대한 오해 그리고 BDD

시작하면서


객체지향 프로그래밍(Object Oriented Programming)에 대한 설명은 많으나, 근거에 대한 설명이 부족해서 신뢰하기가 어려웠고, 그래서 이번에 누가 어떤 의도로 만들었는지 알아보고 JAVA로 OOP 할때 어떻게 진행하는 게 좋을지에 대해 정리해 보았다.

내용

"객체지향프로그래밍"

어떤 단어들이 떠오르는가?

은닉? 캡슐화? 다형성?
애자일 개발방법 5대 원칙인 SOLID(SRP, OCP, LRP, ISP, DIP)?

아쉽지만 원래의 의도대로 OOP를 했을때 이것들은 결과적으로 나오는 것들이지 목표가 되는것들은 아니다.

앨런케이(Alan Kay)에 의하면, 객체지향 프로그래밍이란,
"하나의 시스템을 서로 상호작용하는 객체로 이루어져 있다고 보는 것이며, 객체들끼리는 메시지로 서로 협력한다."

이 이상도 이하도 아니다.

우선 객체지향 프로그래밍(Object Oriented Programming)이란 말을 처음 사용했던 앨런케이(Alan Kay)의 말을 들어보자.
마침, 스테판람이라는 분이 나와 같은 의문을 가졌는지, 앨런케이와 메일을 주고 받았고, 온라인에 공개해두셨다. 아래는 그 일부분이며, OOP를 처음 언급했을때 아래와 같은것을 고려하고 있었다고 한다.

"I thought of objects being like biological cells and/or individual computers on a network, only able to communicate with messages."
  - 저는 오직 메시지로만 커뮤니케이션 할 수 있는 생물학적인 세포같은 무언가 또는 단일 네트웤상의 각각의 컴퓨터들에 대해 생각하고 있었습니다.

실제로 생물학에서 각 세포를 "개체"라는 표현을 사용하고, 이 개체는 군집한 전체 집합에 속하는 하나를 이야기 한다. 영어로 표현하면 "Object"다.
출처 : https://thumb9.shutterstock.com/display_pic_with_logo/848740/185408816/stock-vector-hormones-receptors-and-target-cells-each-type-of-hormone-is-designed-only-certain-cells-these-185408816.jpg


그래서 OOP 를 키워드로 요약하자면, "객체"와 "메시지"이다.

시스템이 커지면서 이 "객체"들의 수가 너무 많아졌고, 이를 분류할 필요성이 대두된다. 분류를 영어로 표현하자면 Classification이며, 자바에서는 이에 더해 코드의 재사용성을 높이기 위한용도로 Class를 사용한다.

여기서 클래스와 객체, 인스턴스에 대해 설명할수 있게된다.
"공통적인 특성을 가진 객체들이 존재한다고 할때, 해당 특성을 클래스로 정의할수 있으며, 이 클래스에 속하는 객체들은 이 클래스의 인스턴스이다."

예를 들면, 자동차라는 클래스가 있다고 해보자.
클래스를 정의하자면, 바퀴가 네개있고, 유리창문을 가지며, 연료를 저장해서 이를 이용해 앞으로 전진한다. 이 클래스에 속하는 객체는 택시, 버스, 트럭등이 있고, 이것들은 자동차의 인스턴스이다.

위에서 객체들끼리는 메시지를 주고 받으며 협력한다고 했으며, 세포 이미지를 다시한번 보자
왼쪽 세포가 호르몬을 분비하고 의도된 대상 세포가 이에 대한 수용체를 가지고 있어서 이 호르몬에 맞춰 대응을 하게된다.

OOP에서도 비슷한 맥락으로 객체(보통은 Client라고 이야기되는)가 메시지를 대상 객체에게 전송되며, 이 메시지의 내용은 특정한 행위(Behavior)를 요구하는 것이다. 이 행위를 어떻게 할지 방법(method) 대해서 Client는 전혀 알지 못한다. 이 행동의 결과로 다시 메시지를 받을수도 있고 아닐수도 있다.  여기서 OOP의 "은닉성"이라는 개념을 이해할수 있다. Client는 Target에게 메시지를 보낼수만 있을뿐 Target이 어떤행동을 할지는 알수 없다.

Objective-C나 Swift라는 언어에서는 극단적으로 null일수도 있는 객체에도 메시지를 보낼수 있으며, 이때 null인 객체는 응답할 의무가 없기때문에 아무런 에러없이 코드가 진행될수도 있다.

Java를 예를들면,
메시지는 메서드명으로 이야기할수 있으며, 해당 메시지를 받을수 있는 객체에게 전송하게 된다.
Client는 내부 코드에서 아래와 같이 작성할수 있다.

Class Client {
....
    public void someMethod() {
    ...

        targetObject.message();
     ...
    }
...
}

뭔가 이상하다. 우리는 보통 targetObject에 아래와 같이 작성한다.
targetObject.doSomething(parameter1, parameter2)

이상할것 없이, 이 코드는 단지 message를 구체적으로 풀어쓴것 뿐이다.
위 코드에서 "메시지"가 무엇인가라고 묻는다면, "doSomething(parameter1, parameter2)"이다.
말로 풀어쓰자면 "parameter1과 parameter2를 가지고 무언가를 해봐"라는 행위(behavior)를 메시지에 담아 보내는것이다. 그 대상은 물론 targetObject이다.

우리가 Class를 선언할때, 두가지 작업을 한다는 것을 알고 있어야 한다.
1. 외부로 받을수 있는 메시지의 종류
2. 메시지로 요청되는 행위(Behavior)에 대한 방법(Method)
이다.

위의 그림에서, 우리는 대상세포가 받을수 있는 호르몬의 종류만 알수 있을뿐, 대상세포가 어떻게 행동할지는 알수 없었다. 우리가 비즈니스를 클래스안에 구현할때도 마찬가지로 대상객체가 받을수 있는 메시지만 정의하고 내부적으로 어떻게 행동할지는 고려하지 않는다.

하지만, 우리가 대상객체를 JAVA Class로 선언하면서 대상의 행동까지 생각하게 되기 때문에 머릿속이 복잡해지고, 작성하던 처음 클래스에 어떤 의도를 담고자 했는지 점점 잊게된다.

그래서 OOP관점에서 Client를 작성할때, targetOjbect를 Interface를 사용해 보낼 메시지만 정의하는것이 자연스럽다. 물론 Class는 Class일 뿐이고 프로그래밍할때 내부는 신경쓰지 않는다고 한다면 상관은 없지만, 의도적으로 대상 객체의 내부 행위를 무시한다는 관점에서는, Interface가 훨씬 자연스럽고, 현재 작성하고 있는 Client의 비즈니스 로직에 집중할수 있게된다.

이보다, 프로그래밍을 시작하기 전, 설계작업이 없이 코드작업을 진행할때 문제가 발생한다. 해결하려는 비즈니스를 소규모 시스템으로 정의할때, 설계시 정의된 도메인 모델들은 시스템을 구성하는 1차적인 객체들로 표현될수 있으며, 실제 고객들과 소통할 때에도 사용되기 때문에, 명확하지 않다면 커뮤니케이션 비용의 증가로 이어진다.

한꺼번에 디테일까지 생각해서 고려할수 있는게 동양인의 스마트한 뇌구조라서 이것이 힘들다는 것은 이해하지만, 실제로 OOP를 할때는 이것이 걸림돌이 될수 있다. 그래서 Interface를 의도적으로 사용하는게 OOP를 하는데 도움이 된다.


정리

객체지향 프로그래밍이란, 시스템을 서로 메시지를 통해 상호작용하는 객체로 구성되어있다고 바라보는 프로그래밍 기법이며, 지금까지 메시지로 행위(Behavior)를 요청하고 이에 대한 방법은 Client가 알지 못한다고 하였다.
즉, 이것은 대상 객체의 자율에 맡긴다는 의미이며, 우리가 정의한 Interface에 맞게 메시지를 처리할수 있다면 구현은 다양할수 있다. 이 구현체를 작성할때 적용할 수 있는 방법이 BDD(Behavior Driven Development) 또는 TDD(Test Driven Development)이다.

비즈니스를 구현하면서, 대상이 되는 객체들에게 요청할 행위(Behavoir)들을 알고 있기때문에, 의도한 바 대로 Mocking을 통해 테스트코드를 작성할수 있다. Client의 테스트코드가 완료 되면, 해당 Interface를 상속받는 대상Class들의 테스트코드들의 PreCondition과 PostCondition을 어느정도 알수 있는데, 이는 해당 행위를 하기 전 전제 조건과 실행후 결과상태들이며 의도에 때라 테스트 내용이 추가될수 있다. BDD의 용어로 표현하자면 PreCondition=전제조건=Given, 행위=When, PostCondition=결과상태=Then 이다.

다음에는 BDD에 대한 조금더 상세한 설명과 함께,
Spock을 이용한 테스트 예제를 알아보도록 하겠다.













댓글 없음:

댓글 쓰기