[swift]project_계산기

Jeff·2024년 10월 31일
1

이번 주차에는 개인이 계산기(UI없는)를 만드는 과제가 주어졌다.

계산기 만들기

# Lv1 & Lv2

# 가이드라인

1. Lv1

  • 더하기, 빼기, 나누기, 곱하기 연산을 수행할 수 있는 Calculator 클래스를 만들기
  • 생성한 클래스를 이용하여 연산을 진행하고 출력

2. Lv2

  • Lv1에서 만든 Calculator 클래스에 “나머지 연산”이 가능하도록 코드를 추가하고, 연산 진행 후 출력


  • Lv1에서 Lv2는 한가지의 기능(나머지 연산)만 추가되기에 같이 작성해보려 한다.

  • 먼저 각 연산을 수행할 수 있는 class를 만드기 위해 Calculator이라는 class안에 함수들을 선언해 놓았다. 이 함수들은 값을 받아 연산 후 값을 반환하도록 정의해 놓았다.

  • 사칙연산 이외에도 나머지연산을 위해 함수를 한가지 더 추가했다.

  • 아쉽게도 이 계산기는 직접 값을 입력받아 오는 기능이 없기에 우리는 미리 값을 정의해 놓았다.

  • Calculator클래스를 인스턴스로 생성 후 두 값을 각 각의 해당 함수를 실행해 출력해준다.

    # 트러블 슈팅

    • 나머지 연산자를 구현하는 와중에 예상치 못한 오류를 만나게 되었다.
      Int타입에서는 오류가 발생하지 않지만 Double타입에서는 "%"을 사용할 수 없다고 에러가 발생한다.

      그렇다고 Double타입이 나머지연산을 할 수 없다는건 아니다. truncationgRemainder(dividingBy:)을 이용한다면 Double타입도 쉽게 사용이 가능했다.

# Lv3

# 가이드라인

3. Lv3

  • 아래 각각의 클래스들을 만들고 클래스간의 관계를 고려하여 Calculator 클래스와 관계 맺기

    • AddOperation(더하기)
    • SubstractOperation(빼기)
    • MultiplyOperation(곱하기)
    • DivideOperation(나누기)
  • Calculator 클래스의 내부코드를 변경
    관계를 맺은 후 필요하다면 별도로 만든 연산 클래스의 인스턴스를 Calculator 내부에서 사용


  • 이번에는 아까 만들어 사용했던 함수들을 Calculator클래스가 아닌 각 함수가 개별적인 class를 갖도록 한 후 클래스들간의 관계를 맺어야한다.

    보시는거와 같이 AddOperation이라는 class를 정의 후 그 안에 메서드로 calculate를 정의하듯이 연산자별로 class를 정의한 후에 메서드를 정의했다.

  • 앞의 Lv1,2에서는 두개의 값을 미리 변수에 선언하였지만, 이번에는 class내에서 변수를 선언하고 선언한 변수를 init를 통해 초기화를 해준다.(class는 initializer를 해줘야하지만 struct는 자체에서 memberwise initializer가 존재하기에 필요 없다.)

  • 두 값 뿐만 아니라 연산자를 입력 받아와 switch-case을 이용해 분기처리를 했으며, 다른 값이 들어온다면 에러를 출력하도록 구성했다.

  • 입력받은 연산자에 맞게 위와 같이 미리 class로 선언했던 class를 인스턴스로 생성 후 그 안의 메서드를 호출해 계산을 한 값을 리턴해서 받아오는 코드로 구현했다.

  • 하나의 파일에 모든 코드를 넣기보단 디렉토리를 나눠서 코드를 작성을 했다.

    # 트러블 슈팅

    • 처음에 파일을 분리해서 코드를 작성했더니 같은 파일에 있지 않은 코드들이 범위에 없다고 경고문을 띄어주었다.

      원인을 찾아보니 각 파일에 선언한 클래스나 메서드는 기본적으로 internal타입으로 설정되어 있다. swift에서는 5가지의 접근제어가 open, public, ìnternal, fileprivate, private 이루어져있다. 그래서 기본 타입은 하나의 모듈 내부에서만 접근이 가능하다. 그래서 public접근자로 바꿔주면 모듈 외부에서도 접근이 가능하게 변경되게 되어 다른 파일에 있는 클래스와 메서드를 사용하게 될 수 있었다.

    # Lv2에서 달라진 개선점

    • 기존에 Calculator 클래스 내부에서 모든 함수를 정의해서 사용했었다. 하지만 우리는 이 함수들 중 하나를 수정하게 되더라도 많은 에러를 마주하게 될 것이다. 그렇기에 클래스는 하나의 책임만을 가져가야하는 그 책임은 변경의 이유가 하나여야 한다는 단일 책임 원칙에 맞게 수정을 하게된 것이다. 이렇게 클래스별로 책임을 분산하여 수정이 이루어지더라도 다른 기능에는 영향이 가지않게 된다.
    • 기존에는 하나의 파일에 모든 코드를 작성했었다. 하지만 코드의 구조화나 유지보수에 용이하지 않기에 기능별로 파일을 나누어서 코드를 작성해 가독성과 유지보수에 용이하도록 바꿨다.

# Lv4

# 가이드라인

4. Lv4

  • AbstractOperation라는 추상화된 프로토콜 만들기

  • 기존에 구현한 AddOperation(더하기), SubtractOperation(빼기), MultiplyOperation(곱하기), DivideOperation(나누기) 클래스들과 관계를 맺고 Calculator 클래스의 내부 코드를 변경


  • 다음으로는 추상화된 프로토콜을 만들어 클래스간의 결합도, 의존성을 낮추는 과정이다.
    현재 각 연산자별 계산하는 함수들은 공통된 특징이 있다. 두 값을 받아와 계산 후 값을 반환한다.
    그렇기에 위와 같이 추상화된 프로토콜을 선언해주었다.

    그렇게 선언한 프로토콜을 각 연산자의 클래스에서 채택해 사용하고 있다.

    그 다음으로 operation이라는 변수에 프로토콜 타입을 사용할 수 있게 만들었다. 그리고 이니셜라이져할 때도 추상화 프로토콜을 채택한 클래스들 중에서 사용할 클래스를 받아오도록 변경을 했다. 그리고 operating이라는 메서드를 만들어 계산을 하도록 정의했다.

    # 트러블 슈팅

    • 코드를 다 작성한 후 실행을 했는데 또 다른 오류가 발생했다. 다른 파일에 있는 연산자별 클래스들도 모두 Public으로 선언했지만 아래와 같은 오류가 발생했다.

      자세히보니 클래스안에 initializer가 접근레벨이 internal이라고 한다. 하지만 나는 클래스안에 메서드외에는 저장프로퍼티를 작성하지 않았었다. 하지만 찾아보니 클래스가 모든 저장 프로퍼티에 대한 초기값을 가지고 있지 않을때, 직접 초기화해줘야한다고 한다. 그래서 현재 클래스 안에 저장프로퍼티가 없기에 직접 초기화 메서드를 선언해줘야했다.

      그래서 아무것도 넣지않은 init()을 직접 선언해주었고, 접근레벨도 public으로 변경해주었더니 이상없이 잘 작동하게 되었다.

    # Lv3에서 달라진 개선점

    • 이전에는 switch-case문을 활용해 연산자를 받아와 구별 후 그에 맞는 클래스의 메서드를 실행해 값을 받아오는 로직이였다. 하지만 이 또한 Calculator 클래스 안에 정의되어 있기에 만약 연산자 클래스 중 하나가 없어지거나 수정이 된다면 switch-case문에 에러가 발생하게 된다. 이렇듯 Calculator 클래스는 연산자 클래스에 의존을 하고있다고 보면된다. 그렇기에 이런 의존성을 낮추기위해 의존성 역전 원칙(DIP)을 적용해서 코드를 개선해보았다. 직접 의존하지 않고 AbstractOperation이라는 추상화한 프로토콜을 거쳐서 Calculator 클래스에 사용하게 된다면 연산자 클래스가 변경이 이루어지더라도 Calculator 클래스에는 영향이 가지않고 직접 의존을 하지않게 된다. 이렇게 결합도를 낮추고 의존성을 낮추어 유지보수에 용이하게 만들 수 있었다.

마치며

이번 과제를 하면서 모르는게 많았고, 객체지향에 대해서 많이 배우는 계기가 되었으며 앞으로 어떤 방향으로 프로그래밍을 설계해야하는지 조금은 느낌이 오는거 같다. 이 과정까지 오는데는 튜터님과 팀원들의 정말 많은 도움으로 완성할 수 있게된거 같다.

profile
기본에 충실한 개발자가 목표!

1개의 댓글

comment-user-thumbnail
2024년 11월 1일

눈물 업시 볼 수 없네요

답글 달기