공부 연습장 :-)

좌표계산기 셀프 피드백

|

좌표계산기 리뷰

  • 미션 진행 기간 : 2017.11.16 ~ 2017.12.04
  • 좌표값을 입력받으면 4사분면 위에 입력받은 좌표값을 출력하고, 좌표값에 의해 표시된 도형의 길이나 넓이를 계산하여 출력하는 프로그램
  • 좌표계산기 미션 github 저장소 바로가기

배운 것

프로토콜을 사용했다. (+ 프로토콜 컴포지션)

이론만 들었을때는 어려운 개념이라고 생각했는데 사용하고 보니 오히려 프로토콜로 인해 더 쉽게 구현되는게 많았다.
추상화 : 각기 다른 객체를 한 메서드에서 사용해야할때 내부에서 하나의 이름, 하나의 타입으로 각각 다른 객체를 파라미터로 받을 수 있다는게 가장 유용했다. 만약 이렇게 하나의 프로토콜로 묶지 않았으면 각 객체는 하나의 메서드의 파라미터로 들어가는 조건을 충족하기 위해서 필요 이상으로 과도하게 많은 프로퍼티를 가지도록 설계하거나 다양한 객체를 받아서 처리해야하는 부분이 과도하게 복잡해졌을것이다.
그리고 프로토콜은 각 객체가 반드시 구현해야하는 메서드들을 정의해놓는데 이게 어떻게 보면 각 객체는 꼭 이 역할을 해야한다는 책임 분담과 같은 것이라 객체의 역할분담에도 유용히 쓰였던 것 같다.
예를들어 나는 각 도형이 성립되는 조건을 검사하는 책임을 어디에 두어야 할지 감이 잘 안왔는데 ‘도형’으로 추상화를 하고 모든 도형의 공통되는 부분인 유효성 체크 메서드를 준수하도록 설계하니까 input이나 factory객체의 책임이 과도하게 늘어나는걸 제한 할 수 있었던 것 같다.
“점 - 직선 - 삼각형 - 사각형” 순서로 미션이 확장되었는데 직선 구현 단계에서 프로토콜을 구현하니까 오히려 더 어려운 미션이었던 삼각형과 사각형을 추가는 구현 시간이 훨씬 더 짧았다.

접근제어자를 사용하는데 익숙해졌다.

private 함수를 쓰는 것이 처음엔 생각하기 까다롭다고 생각했었는데, 이제는 당연히 내부에서만 쓰는 함수와 아닌 것을 구분해서 접근제어자를 쓰려고 한다.
main 파일도 이런 식으로 접근하면 지금보다 줄어들 것 같다.

객체의 책임 분리가 덜 어색해졌다.

처음에는 “객체의 책임”이라는 단어 자체가 와닿지가 않아서 책임을 분리하는게 굉장히 어색했다.
그래서 설계가 나중에 가면 뒤틀어지고 대규모 공사를 하는…그런 경우가 있었는데 지금은 기능 하나, 객체 하나가 추가될 때마다 책임과 역할, 그리고 협력하는 객체는 무엇인지 자연스럽게 떠올리게 됐다.

객체의 생성과 소멸

  • 객체나 변수를 미리 생성해놓지 말고 사용하는 scope 안에서 생성되고 다른 scope에서 사용될 일이 없으면 해당 scope안에서 소멸되도록 사용하는 습관을 길러야 한다.
    예를들어 main파일에서 습관적으로 상위에 모든 인스턴스를 다 선언해놓고 아래쪽에서 인스턴스.메서드해서 사용하는데 적어도 사용하는 시점에 인스턴스를 만드는 습관을 들이자. (심지어 do-catch문 안에서도 인스턴스를 생성할 수 있다. 필요하면 그렇게 하자.)
  • 단지 객체의 프로퍼티값이 필요할 뿐이라고해서 객체를 또 생성할 필요는 없다. 객체는 필요할 때만 생성하고 프로퍼티값을 객체 생성 없이 가져올 방법이 있는지 고민하고 객체를 새로 또 생성하는 방법은 최대한 배제하자.

에러처리

  • do-try-catch를 한꺼번에 다 쓰지 말고 하나의 에러케이스enum에 하나의 do-try-catch가 있어야 한다.
  • 에러체크를 할때는 습관적으로 if-else를 사용하기보다는 만약 에러처리하고 해당 메서드를 끝내는 로직이 자연스럽다면 guard를 사용하는 습관을 들이는게 더 좋을 것 같다. guard 더 자연스럽다.
  • 에러체크를 하는 함수 이름을 직관적으로 짓자.
    나는 invalid- 로 이름을 정해서 true일때 에러를 throw하고 false일때가 정상케이스일 경우로 짰는데(if !invalid() {return value} 이런식으로…) 나 말고 다른사람은 이해하기 힘들것이라는 리뷰를 받았다. 오히려 직관적으로 isValid()- 처럼 이름을 짓고 로직을 반대로 하니까 guard문으로 단순화 하는 것도 쉽고 더 읽기 쉬운 코드가 됐다.

부족했던 것

  • 좌표계산기 로직에 맞는진 모르겠지만 상속을 적용했다면 어땠을까 하는 생각이 든다.(‘점 > 직선 > 삼각형 > 사각형’의 흐름이기때문에. 점이 모든 도형을 포괄하는 가장 큰 개념이다.)
  • Set의 사용 : 좌표값이 서로 같은 좌표값은 없는지 체크해야했었는데 Set 타입으로 좌표를 담으면 쉽게 처리가 됐을텐데 하는 아쉬움이 있다.
  • 필요한 클래스나 메서드의 재정의 : Equatable프로토콜을 오버라이드해서 사용했지만 오버라이드를 하는 컨셉이 생소해서 간단한 코드였는데도 불구하고 구현도, 사용도 어려웠다. 그리고 sorting하는 기능이 필요해서 Comparable프로토콜을 오버라이드할 필요가 있었는데 결국 사용하지 못하고 미션이 끝났다.
  • main.swift 파일이 깔끔하지 못한 것. 지금은 반복문 내에서 에러처리를 하는 식으로 되어있는데 메서드로라도 프로그램을 실행하는 로직을 분리했으면 더 깔끔했을 것 같다.
  • 유닛테스트를 제대로 하지 못한 것. 이상적이게는 함수 하나 추가할때마다 테스트코드를 추가해야하는데 문제를 풀어가다보면 그 흐름을 끊고 테스트코드를 짜기 어렵다.
  • 커밋단위를 잘게 쪼개지 못한 것. 신경써서 하긴 했지만 기능 추가하다보면 계속 이어서 하게됐다…
  • 커밋메시지를 길게 쓴것 같다. 명확하게 쓰는게 좋다고 해서 썼는데 너무 구체적으로 쓴건 아닌가 싶다.

앞으로 할 것

  • 객체는 필요한 곳에서 선언하고 과도하게 생성하지 않기
  • main.swift 내의 로직을 단순화해보기
  • 에러처리 시 습관적으로 if-else만 쓰지 않고guard를 사용하기
  • 에러처리 시 do-try-catch를 한 에러타입 당 하나의 구문을 사용하기
  • 객체의 협력을 건너뛰거나 거슬러올라가지 않기 : 똑같은 데이터라도 현재 협력하고 있는 객체에게 그 데이터를 요청해야지 협력하고 있지 않은 객체에게서 데이터를 받아는 형태가 되면 의존성이 높아짐
  • 구조체나 클래스 이름은 -ing 형태보다는 의인화(마치 사람처럼) Manager, Checker, Player 형태로 네이밍하는게 보편적임
  • computed property를 사용했던 것 처럼 프로퍼티가 특정한 로직으로 계산되어야 할때는 get메서드를 만들지 말고 getter 프로퍼티를 사용하기
  • 커밋단위를 작게 나눠서 커밋하기 : 하나의 기능이 추가될 때마다 확인하고 커밋하기!
  • 유닛테스트 습관화하기

Comments