객체지향 프로그래밍 - 관계의 의미, composite

 오늘은 객체지향 프로그래밍에서 클래스 간의 관계에 대해서 이야기해보겠습니다. 객체지향은 뭘까요? 객체를 생각하며 프로그래밍하는 방법이라고 이해합시다. 보다 철학적이고 복잡한 설명은 접어둡니다. 여기서는 객체라는 아이디어를 중심으로 자유롭게 창작하는 방법을 이야기합니다.

객체(object)는 어떤 설계도에서 탄생한 구체적인 소스코드입니다. 요즘 인기있는 파이썬으로 설명을 하려고 합니다. 여러분에게 주사위를 생각하라는 과제가 주어졌다고 합시다. 그럼 주사위는 어떻게 설계할까를 생각합니다. 먼저 주사위는 ‘주사위’라는 구체적인 사물입니다.

class 주사위:
  pass

주사위에 아무것도 적지 않아서 일단 pass라고 했습니다. 주사위는 여섯 개의 면이 있고 각각의 면에 숫자가 적혀져 있는 사물입니다. 이렇게 주사위의 존재 실체를 생각합니다. 즉, 주사위가 탄생하면 여섯 개의 면도 같이 태어납니다. 이 면들을 다음과 같이 했다고 합시다.

class 측면:
  def __init__(self,number):
    self.number=number

가령 1부터 6까지 쓰여진 측면 6개는 다음과 같이 태어납니다.

측면1=측면(1)
측면2=측면(2)
측면3=측면(3)
측면4=측면(4)
측면5=측면(5)
측면6=측면(6)

이제 주사위와 측면의 관계를 말하고자 합니다. 자, 여러분 위에 쓰인 코드를 잘 보면 이상하지 않습니까? 주사위의 측면은 어떻게 존재할까요? 주사위가 없이 측면1=측면(1)과 같이 존재할 수 있을까요? 주사위의 측면은 주사위의 본질적인 것이므로 주사위와 주사위의 측면의 존재적 차이가 없다고 말할 수 있습니다. 즉, 숫자 6개가 없는 주사위는 주사위가 아니고 주사위에 붙어 있지 않은 측면은 상상하기 어렵습니다. 이처럼 존재적으로 강한 의존서을 가진다는 말은 주사위가 태어날 때 측면 6개도 함께 태어나고 주사위가 사라질 때 이들의 의미도 사라진다는 뜻입니다. 따라서 class 주사위는,

class 주사위:
  def __init__(self):
    self.측면1=측면(1)
    self.측면2=측면(2)
    self.측면3=측면(3)
    self.측면4=측면(4)
    self.측면5=측면(5)
    self.측면6=측면(6)

과 같아야 합니다. 이제 주사위와 측면은 강한 존재적 관계에 놓여 있습니다. 즉, 주사위의 측면에 접근하려면 반드시 주사위를 통해야 합니다. 측면에 call이라고 요청하면 자기의 번호를 알려주는 기능을 설계합시다.

class 측면:
  def __init__(self,number):
    self.number=number
  def call(self):
    return self.number

주사위를 통해 측면.call()에 접근하기 때문에 call을 위한 매쏘드를 class 주사위 안에 만들어야 합니다. 주사위와 측면의 존재적 운명은 같기 때문에 측면에 요구하는 어떤 기능은 곧 주사위에도 내포됩니다. 이 둘은 서로 분리될 수 없으니까 당연합니다.

 class 주사위:
  def __init__(self):
    self.측면1=측면(1)
    self.측면2=측면(2)
    self.측면3=측면(3)
    self.측면4=측면(4)
    self.측면5=측면(5)
    self.측면6=측면(6)
  def call_facet(self,which_one):
    if which_one==1:
      return self.측면1.call()
    elif which_one==2:
      return self.측면2.call()
    elif which_one==3:
      return self.측면3.call()
    elif which_one==4:
      return self.측면4.call()
    elif.which_one==5:
      return self.측면5.call()
    elif.which_one==6:
      return self.측면6.call()
    else:
      return -1

실행을 해봅시다.

dice=주사위()
dice.call_facet(2)
dice.call_facet(3)

이와 같은 클래스의 관계를 복합 관계(composite relationship)이라고 말합니다. 복합관계는 본질적으로 클래스의 존재적 정의와 연관이 있습니다. 따라서 경계가 애매할 경우가 많습니다. 사실 위의 예제를 측면이라는 클래스가 정의되지 않아도 되는 형태로 변형할 수 있습니다.

class Dice:
  def __init__(self):
    facets=[]
    facets.append(1) #1번
    facets.append(2) #2번
    facets.append(3)
    facets+=4 #간단하게
    facets+=5
    facets+=6
    self.facets=facets
  def call_facet(self,which_one):
    return_value=-1
    try:
      return_value=self.facets[which_one-1]
    except:
      pass
    return return_value

이 경우가 더 간단하게 보입니까? 코드가 간단하다면 상관이 없지만 요구 조건이 추가가 된다면 여러분이 해야 할 일은 더욱 더 어려워집니다. 예를 들어 주사위를 굴릴 때마다 나타나는 숫자에 따라 다른 색을 부여하는, 가령 1은 red, 2는 green, 3은 blue, 4는 yellow, 5는 pink, 6은 black이라고 하는 작업을 추가한다고 생각합니다. 처음 코딩을 할 땐 없던 요구입니다. 단지 class Dice로 할 때는 이제 이 조건을 반영하기 위해 코드 전체를 손봐야 합니다. 그러나 주사위와 측면을 복합 관계로 분리해두면 간단하게 대응할 수 있습니다. 먼저 측면을 생각하고 다음에 측면과 주사위가 어떻게 기능을 나눠가질 것인지 고민하면 됩니다.

class 측면:
  color_space=[‘red’,’green’,’blue’,’yellow’,’pink’,’black’]
  def __init__(self,number):
    assert number >= 1 and number <= 6, “숫자는 1에서 6까지입니다.”
    assert isinstance(number, int), “숫자는 정수여야 합니다.”
    self.number=number
    self.color=측면.color_space[self.number-1]
  def call(self):
    return self.number
  def has_color(self):
    return self.color

측면의 숫자와 색깔에 관련된 assert를 추가하고 측면의 기본 색상 팔렛트를 클래스 멤버로 정했습니다. 다음으로 측면이 생성될 때 숫자에 맞춰서 색을 부여했습니다. 이제 주사위를 살펴봐야겠습니다. 추가해야할 매쏘드만 따로 쓰겠습니다.

class 주사위:
  def call_color(self,which_one):
    if which_one==1:
      return self.측면1.has_color()
    elif which_one==2:
      return self.측면2.has_color()
    elif which_one==3:
      return self.측면3.has_color()
    elif which_one==4:
      return self.측면4.has_color()
    elif.which_one==5:
      return self.측면5.has_color()
    elif.which_one==6:
      return self.측면6.has_color()
    else:
      return ‘’

코드는 아주 단순하게 작성했습니다. 보다 효율적이고 안전한 코딩 방법이 있겠지만 초심자가 이해하기 좋게 만들었습니다. 객체지향 프로그래밍을 할 때 관계의 종속성을 살펴보는 이유는 결국 설계에 높은 유연성을 부여함으로써 코드 업데이트를 쉽게 하고 확장성을 키우기 위해서입니다. 처음부터 완벽하게 설계할 수 있다면 사실 객체지향적 방법을 100% 따르지 않더라도 문제는 없습니다. 이 경우라면 틀렸다가 아니라 다르다가 적용될 수 있을런지...


댓글

이 블로그의 인기 게시물

R에서 csv 파일 읽는 법

xlwings tutorial - 데이터 계산하여 붙여 넣기