객체 지향 연습하기 2

타키탸키·2021년 3월 23일
0

저번 시간에는 간단한 객체 지향 프로그래밍을 연습했습니다.

이번 시간에는 난이도를 좀 더 높여 완성도 있는 프로그램 하나를 작성해보고자 합니다.

⬜ What time is it now?

이번에 만들어볼 프로그램은 시간을 알려주는 프로그램입니다. 객체 지향 프로그래밍을 시작하려면 객체의 속성과 행동부터 정의해야 하는데요. 시계 프로그램에 필요한 속성행동은 어떤 것들이 있을까요?

  1. 현재 시간 설정하기
  2. 현재 시간 변경하기
  3. 현재 시간에 1초씩 더하기

등을 생각해볼 수 있습니다.

우리는 아래 코드처럼 Clock 클래스를 사용하기 위해 위 세 가지 기능을 Clock 클래스 안에 정의하려 합니다.

# 2시 40분 58초인 시계 인스턴스 생성
clock = Clock(2, 40, 58)

# 15초 늘리기
for i in range(15):
    clock.tick()
    
# 현재 시간 출력
print(clock)

# 1시 2분 57초로 시계 세팅
clock.set(1, 2, 57)

# 10초 늘리기
for i in range(10):
    clock.tick()
    
# 시계의 현재 시간 출력
print(clock)
02:41:13
01:03:07

⬜ 시간 나누기

❗ 개요

시계의 핵심적인 기능은 역시 시간을 나타내는 것이겠죠? 시간은 시, 분, 초로 구성되어 있습니다. 이 세 가지는 하나의 클래스로 표현이 가능한데요. 이 하나의 클래스는 다음과 같은 속성과 행동을 가져야 합니다.

  • 속성
    • 시, 분, 초는 각각 자기의 값을 속성으로 가짐. 예를 들면, 3시 53분 2초에서는 3, 53, 2가 각각 시, 분, 초의 값임.
    • 시, 분, 초 모두 최댓값을 지님. 분과 초는 59, 시는 23.
  • 행동
    • 값 1 증가시키기:
      -- 시간이 흐르는 동작(1초, 1분, 1시가 증가)
      -- 시, 분, 초는 각자의 최댓값에 도달할 경우 그 값을 0으로 바꾸고 윗 단위를 1 증가시킴. 예를 들어 59초에서 60초가 되면 0초와 1분이 됨.
    • 값 설정하기:
      -- 시계가 고장났을 경우
      -- 해외에 여행갔을 경우
      -- 위 두 경우에는 시, 분, 초를 다시 세팅해야 함

이제 위 속성과 행동을 바탕으로 시, 분, 초를 하나의 클래스로 만들어보겠습니다. 시, 분, 초의 주된 동작'0 또는 시작값'에서 '최댓값'까지 수를 증가시키는 것이므로 클래스 이름을 Counter로 작성하겠습니다.

❗ 구현

클래스를 선언합니다.

class Counter:

이 클래스는 Clock 클래스의 시, 분, 초를 각각 나타내는데 사용됩니다.

이제 인스턴스 변수를 설정할 차례입니다. 최댓값을 나타내는 limit현재까지 카운트한 값 value를 선언합니다. 인스턴스를 생성할 때, 인스턴스 변수 limit만 파라미터로 받고 value는 초깃값 0으로 설정합니다.

def __init__(self, limit):
    self.limit = limit
    self.value = 0

시간을 설정할 수 있는 set 메소드를 선언합니다. 파라미터가 0 이상이고 최댓값 미만이면 value에 설정합니다. 반대의 경우에는 value에 0을 설정합니다.

def set(self, new_value):
    if 0 <= self.value < self.limit:
        self.value = new_value
    else:
        self.value = 0    

다음으로 시간을 증가시키는 tick 메소드를 선언합니다. 이 메소드는 카운터의 값 value가 limit에 도달하면 value를 0으로 바꾼뒤 True를 리턴합니다. 반대의 경우에는 False를 리턴합니다.

이때, 불린값을 리턴하는 이유는 어떤 상태를 True와 False로 구분하여 리턴하면서 조건문 등에 활용하기 위함입니다.

def tick(self):
    self.value += 1
    
    if self.value == self.limit:
        self.value = 0
        return True
    return False

value를 문자열로 반환해주는 던더 str 메소드를 선언합니다.

def __str__(self):
    return str(self.value).zfill(2)

value를 최소 두 자릿수 이상으로 나타내기 위해 zfill 메소드를 사용합니다. self.value의 값이 정수이기 때문에 str을 통해 문자열로 변환합니다.

❗ 테스트 코드

최대 10까지 셀 수 있는 카운터 인스턴스를 생성합니다.

counter = Counter(10)

1부터 3까지 셉니다.

print("1부터 3까지 카운트하기")
for i in range(3):
    counter.tick()
    print(counter)

타이머 값을 0으로 바꿉니다.

print("카운터 값 0으로 설정하기")
counter.set(0)
print(counter)

카운터 값을 8로 설정합니다.

print("카운터 값 8으로 설정하기")
counter.set(8)
print(counter)

이제 카운터 값이 10이 되면 0으로 바뀌는지 확인합니다.

print("카운터 값이 10이 되면 0으로 바뀝니다")
for i in range(3):
    counter.tick()
    print(counter)

❗ 출력 결과

1부터 3까지 카운트하기
01
02
03
카운터 값 0으로 설정하기
00
카운터 값 8로 설정하기
08
카운터 값이 10이 되면 0으로 바뀝니다
09
00
01

⬜ 시계 프로그램

❗ 개요

이제 Counter 클래스로 시계의 시, 분, 초를 나타내려 합니다. 이때, 시계라는 객체도 하나의 클래스로 정의할 수 있는데요. 앞서, 시계의 세 가지 조건Counter 클래스를 조합하면 시계의 기능을 완성할 수 있습니다. 이 클래스를 Clock이라고 하겠습니다.

Clock 클래스가 지닌 속성행동을 봅시다.

  • 속성(현재 시간)
    • 초: 1부터 59까지 세는 Counter 클래스의 인스턴스
    • 분: 1부터 59까지 세는 Counter 클래스의 인스턴스
    • 시: 1부터 23까지 세는 Counter 클래스의 인스턴스
  • 행동
    • 1초 증가시키기
      -- 시간을 1초씩 증가시킴
      -- 주의할 점: 시간을 증가시킬 때 최댓값에 도달하면 0으로 설정하고 윗 단위를 1 증가시킴. tick 메소드의 리턴값을 활용하여 구현할 것
    • 값 변경하기
      -- 시, 분, 초를 각각 따로 설정하지 말고 한번에 설정하는 메소드 구현하기

❗ 구현

클래스를 선언합니다.

class Clock:
    HOURS = 24
    MINUTES = 60
    SECONDS = 60

def __init__(self, hour, minute, second):
    self.hour = Counter(Clock.HOURS)
    self.minute = Counter(Clock.MINUTE)
    self.second = Counter(Clock.SECOND)
    
    self.hour.set(hour)
    self.minute.set(minute)
    self.second.set(second)

시, 분, 초를 나타내는 인스턴스 변수 3개가 필요합니다. hour, minute, second라는 Counter 클래스인스턴스 3개를 정의합니다.

이때, hour의 최댓값은 24, minute와 second의 최댓값은 60입니다. 이들은 각각 클래스 변수 HOURS, MINUTES, SECONDS에 넣어주겠습니다.

Counter 클래스의 set 메소드를 활용하여 현재의 시, 분, 초를 설정합니다.

이제 Clock 클래스의 set 메소드를 작성할 겁니다. 이미 이닛 메소드에서 각 Counter 인스턴스를 초기화했기 때문에 여기서는 Counter 인스턴스의 값만 지정하면 됩니다. 이때, Counter 클래스의 set 메소드를 사용합니다.

def set(self, hour, minute, second): 
    self.hour.set(hour)
    self.minute.set(minute)
    self.second.set(second)

위 코드는 이닛 메소드의 뒷 부분과 동일합니다. 그럼 위 이닛 메소드의 뒷 부분의 코드를 방금 완성한 set 메소드를 사용해서 더 간결하게 나타내겠습니다.

self.set(hour, minute, second)

시간은 1초씩 증가하므로 초를 나타내는 second의 tick 메소드를 호출해야 합니다. 이렇게 1초씩 증가시키다가 second의 value가 limit 이상이 될 경우 True를 리턴합니다. True의 의미는 초의 값을 0으로 초기화하고 더 윗 단위인 minute를 1만큼 증가시키라는 뜻입니다.

def tick(self):

  if self.second.tick():
      if self.minute.tick():
          self.hour.tick():

Clock 클래스의 tick 메소드에서 시, 분, 초를 나타내는 Counter 인스턴스 세 가지의 tick 메소드를 활용하고 있습니다. 이렇게 계단식으로 표현하면 우리가 원하는대로 시계를 동작시킬 수 있습니다.

이제 현재 시간을 '시:분:초' 형식으로 리턴하기 위해 던더 str 메소드를 선언합니다.

def __str__(self):
    return "{self.hour}:{self.minute}:{self.second}"

❗ 테스트 코드

초가 60을 넘을 때, 분이 1 늘어나는지 확인해 보겠습니다.

print("시간을 3시 40분 58초로 설정합니다")
clock = Clock(3, 40, 58)
print(clock)

15초를 늘립니다.

print("15초가 흘렀습니다")
for i in range(15):
    clock.tick()
print(clock)

분이 60을 넘을 때, 시간이 1 늘어나는지 확인해 보겠습니다.

print("시간을 4시 59분 57초로 설정합니다")
clock = Clock(4, 59, 57)
print(clock)

10초를 늘립니다.

print("10초가 흘렀습니다")
for i in range(10):
    clock.tick()
print(clock)

시간이 24를 넘을 때, 00:00:00으로 넘어가는지 확인해 보겠습니다.

print("시간을 23시 59분 59초로 설정합니다")
clock = Clock(4, 59, 57)
print(clock)

3초를 늘립니다.

print("3초가 흘렀습니다")
for i in range(3):
    clock.tick()
print(clock)

❗ 결과

시간을 3시 40분 58초로 설정합니다
03:40:58
15초가 흘렀습니다
03:41:13
시간을 4시 59분 57초로 설정합니다
04:59:57
10초가 흘렀습니다
05:00:07
시간을 23시 59분 59초로 설정합니다
23:59:59
3초가 흘렀습니다
00:00:02

객체 지향 연습 두 번째 시간 어떠셨나요? 시계라는 하나의 객체를 완성하기 위해 기능을 나누어 두 클래스를 작성했습니다. 일단 하나의 기능을 수행하는 클래스를 완성하면 다른 클래스에서 활용이 가능합니다.

다음 시간에는 객체 지향의 네 가지 핵심 기둥에 대해 함께 알아보겠습니다.

* 이 자료는 CODEIT의 '객체 지향 프로그래밍' 강의를 기반으로 작성되었습니다.
profile
There's Only One Thing To Do: Learn All We Can

0개의 댓글