레이블이 programming인 게시물을 표시합니다. 모든 게시물 표시
레이블이 programming인 게시물을 표시합니다. 모든 게시물 표시

20130903

easy used function

  함수가 없는 프로그래밍은 생각할 수 없다. 어떤 프로그래밍 언어를 선택하든지 함수가 기본 단위가 된다. 이렇게 기본이 되는 함수를 잘 만들어야 이해하기 쉬운 코드를 만들수 있다는데 다들 공감할 것이다. 이야기를 꺼내기에 앞서 학부시절 에피소드를 소개하고자 한다.

  학부시절 과제로 만든 C언어 프로그램 코드는 그 크기는 작고 수준도 낮았겟지만 다양한 코드를 읽어볼 수 있는 좋은 기회였던 것 같다. 어려운 과제가 나오면 삼삼오오 모여서 간밤에 서로 만들었던 코드를 비교해 보곤했다. 다른 사람은 200~300줄로 구현한것을 A4 1장에 구현한 친구를 보고 다들 신기해한 기억이 난다. 성적도 좋아 장학금을 받는 친구들이 대부분이다. 돌려보면 동작 속도도 빠르다. '나와는 다른 세계의 똑똑한 친구가 만든 코드라서 내가 이해할 수 없는 것이다'라고 다들 생각했던 것 같다. 우리는 컴퓨터가 잘 이해하는 코드에 무슨 문제가 있겠는가 생각했다. 우리가 그들보다 지적 수준이 낮아서 그들의 고귀한 코드를 이해하지 못할 뿐이라고 자책할뿐이였다.

  중복을 줄이다보면 코드가 줄어들고 좋은 코드가 된다고 한다. 그러나 앞에서 소개한 이야기는 그러한 것과는 다른 이야기 이다. 주석도 없이 변수는 3~4자의 알파벳으로 되어 있고 for, if등은 가로로 가지런히 줄을 서있다. 창의적인 압축코드였다. 얼마전 TV에서 초코렛에 밥을 비벼먹는 사람이 소개되었다. 당신의 애인이 창의력을 발휘해서 초코렛 비빔밥을 정성껏 만들어준다면 맛있게 먹겠는가? 한두번은 몰라도 매일은 힘들것 같다. 코드도 그렇다. 내가 만든다고 내 마음대로 해서는 안된다.
  자, 그렇다면 좋은 함수는 어떤 것일까? 물론 다들 생각이 조금씩 다를 것이다. 나는 로버트 마틴의 주장에 공감하고 그의 주장을 이야기 하고자 한다.


1. 한가지만 하는 짧은 코드가 좋은 코드이다.
  십수만 라인의 C 프로그램을 만들다가 어느 날 3000 라인이 넘는 함수를 몇시간만에 작석하고는 무척 괴로워했던 기억이 있다. 매우 복잡한 자료구조와 정교한 포인터들로 범벅이된 코드이기 때문에 함수들로 추상화하면 길을 잃을 것 같았다. 동료들은 내 C언어 실력이 뛰어나다는 말을 해주었지만 동료들이 이해할 수 없는 암호를 만들어 버린 것 같아 괴로웠다. 그 함수는 무척 빨랐고 메모리도 효율적으로 사용했고 수년간 버그가 발견되지 않았다. 그러나 난 그 함수를 읽기 쉽게 할 수 없었다. 아이스크림을 바닥에 떨어뜨리고 어쩔줄 몰라 울어버리는 아이와 바를바 없었다.
  어느날 3000줄 짜리 함수를 고쳐야 한다고 생각해보자. if와 for가 여기 저기 4중 5중 중접되어있다. 주석은 있는데 도대체 왜 달아놨는지 한심하기만 하다. 일주일을 봤지만 이해가 안간다. 고쳐보니 프로그램이 뻗어버린다. 원인을 알 수 없다. 이런 상황을 힘들게 이겨낸 것을 무용담처럼 후배들에게 떠들지 말자. 그냥 똥을 치웠을뿐이다.
  이쯤되면 긴 함수가 나쁘다는 생각이 들것이다. 그렇다면 길다는 기준이 있어야 하는데 그게 참 어렵다. 길다는 말자체가 모호하기 짝이 없다. 예전에 어떤 분은 자기 모니터가 해상도가 얼마라서 font크기를 몇으로 하니 한 화면에 몇줄이 들어가서 함수 길이를 그정도 이내로 한다고 한다. 쉽게 말해 자기 컴퓨터에서 한 화면에 나오면 좋아한다. 좋아보이는가? 그러나 난 이것도 길다고 생각한다. 함수 본문은 5줄을 넘으면 안된다. 애정남처럼 딱 정했으면 좋겠다. 사람은 한번에 6~8개 정도를 기억하고 처리할 수 있다고 한다(심리학에서 이 단위를 chunk라고 한다). 우리의 메모리 용량이 7인것이다. 메모리보다 큰 파일을 한번에 읽으면 어떻게 되는지 프로그래머라면 잘 알것이다. 함수의 signature 선언으로 함수 앞뒤에 2줄을 사용한다. 5+2=7 . That's it.
  경험이 많지 않거나 관성으로만 코딩을 해오신 분이라면 당황할지도 모르겠다. 필자를 사기꾼이라 하고 싶은가? 자신이 할 수 없으니 불가능하다고 생각하지 말자. 잘 짜여진 코드는 하나의 작업 책임만 가진다. if문 안에 주절주절 떠들지 않으면 된다. try catch 구문속에 수다를 널어놓지 말자. 한줄씩이면 충분하다. if else 구문은 1줄씩 해서 총 4줄로 구현하는 식으로 추상화 레벨을 높일 수 있다. 그런식으로 했가다는 함수가 너무 많아져서 관리 할 수 없다고? 1000줄짜리 함수 하나와 5줄자리 함수 50개중에 어느것이 더 이해하기 쉽겠는가? (200개가 아니라 50개라고 한것은 대부분 추상화 수준을 올리면 중복이 줄일 기회가 많아지기 때문이다.)


2. "switch"는 묻어버려라.
  "switch"는 작게 만들기 어렵다. "switch"가 있으면 한가지가 아니라 N가지 일을 하는 함수가 된다. 그렇다고 쓰기 않으려고 하니 너무 불편하다. 추상화 레벨의 저수준에서만 딱한번씩만 switch를 사용하여 factory를 구현하고, 분기에 따른 로직은 다형성으로 구현하자. 이러라고 OOP가 있는 것이다. 지저분한 switch는 땅속 깊이 묻어버리고 우리는 고상한 코드만 볼 수 있을 것이다. 물론 어쩔수 없이 상위 로직에서 switch를 써야 할 일도 있을 것이다. 하지만 정말 불가피한지 고민해봐야 한다.


3. 함수의 매개변수는 2개 이하
  매개변수가 없는 함수는 최고다. 1개가 있어도 좋다. 2개까지는 봐주자. 3개는 불쾌하다. 4개의 매개변수를 가진 함수를 만들려면 만들지 말고 차라리 옆사람에게 키보드를 넘겨라. 그런건 필요 없다.
  정수 8개를 매개변수로 받는 함수를 생각해보자. 인기가 좋아 여기 저기 호출된다고 생각해보자. 인기가 좋다니 나도 한번 호출해본다. 8개중에 하나만 순서가 틀려도 찾기 힘든 버그가 된다. 아주 긴장해야 한다. 살짝 녹은 겨울강의 얼음을 밟고 건너듯 조심해야 한다. 다른 사람도 이러게 호출했을 것이다. 그러나 누군가는 얼음이 깨져 겨울 강물에 빠지게 된다. 당신 잘못이 아니다. 호출하기 어렵고 실수하기 쉽게 만든 사람이 나쁘다. 그러나 곤경에 빠진건 당신이다.


4. 매개변수에 boolean은 절대 금지
  매개변수에 true나 false가 들어가서 호출된 함수를 보면 눈살을 찌푸리게 된다. 이런 양다리 바람둥이 같으니!! 이 함수는 분명 2가지 일을 한다. 함수는 칫솔과 같다. 친구와 같이 쓰지 말고 각자의 것을 마련해라. 왜냐고? 더러우니까. 사소한 이유로 당신의 코드를 더럽히지 말자.
  saveReportFile(true);
  이런 코드가 무슨일을 짐작도 하기 어렵다. 이런 코드는 양다리 바람둥이니까.


5. Side effect를 발생시키지 말자.
  함수 이름과 관련 없는 일은 하지 말자. "isValidUser(User user)" 이 함수는 무엇을 하는 함수일지 짐작이 될 것이다. 올바른 사용자가 아니라고 user 객체의 필드들을 모두 null로 초기화 해버린다면 어떨까? 사용자가 알수 없게 저렇게 캡슐화 하는게 진정한 추상화라 생각하는가? 저 함수를 쓸 때마다 뒤에 숨은 저런 기능을 기억해야 한다. 난 그런 것들을 모두 다 기억할 정도로 머리가 좋지 않다. 물론 기억이라는 것도 저런 side effect 때문에 몇시간을 디버깅으로 고생한 다음이겠지만 말이다.
  이 예제는 몇가지 의미가 더 있다. 매개변수로 넘어온 객체의 내부상태를 변경하려 하지 말라는 것이다. 출력은 매개변수로 하지 말고 return하라는 말이다. 그리고 함수는 질문에 답하거나 어떤 행위를 하거나  하나만 해야 한다.
  boolean setSession(Session session);
  이 함수는 뭘 하는 함수 일까? session 객체값을 이용해 세션을 변경하는 것일까? 아니면 세션이 설정이 되었는지 확인 하는 것일까? 반환값은 세션 설정에 성공하면 true일까? 아니면 세션이 잘 설정되어 있으면 true일까? 이런 저런 짐작으로 저 함수의 코드를 열어봐야 한다면 함수를 작게 나눈 의미가 없다. 그냥 긴 함수로 만들어라. 작은 함수들로 나누면 이해하기 어렵다고 불평하는 당신은 추상화에 대한 무지함을 드러낼 뿐이다.


6. 오류 코드보다는 예외 사용하자
  함수가 성공하면 1을 반환하고 실패하면 0을 반환하는 것과 비슷한 코드를 많이 보았다. 그런데 누구는 성공하면 0이고 실패하면 -1이란다. 실패하면 100~200 사이의 오류 코드를 반환하고 싶을 수도 있다. 오류 코드를 잘못 체크하거나 안해도 컴파일러는 알수 없다. 이런 것 때문에 발생하느 버그는 지옥이다. 이런것때문에 생일날 친구들과 약속을 잡았는데 새벽 2시까지 야근한다고 생각해보자. GG
  자바에서 예외는 checked exception과 runtime exception에 대해 구분해서 사용하는 것이 중요하다.  그러나 이 글의 주제에서 벗어나므로 나중에 기회가 다루면 예외의 사용법에 대해 포스팅하겠다. 예외를 사용하면 에러 체크를 가시화 할 수 있고 함수 호출부에서 에러코드를 체크하는 지저분한 if else의 반복을 제거하여 가독성이 올라간다는 것만 기억하면 좋겠다.

마치며..
  짧고 읽기 쉽게 잘 추상화 해놓은 코드를 보면 놀랍기만 하고 나는 저런 것을 절대 만들수 없을 것 같다는 생각을 할 수 있다. 원래 코드란게 그렇다. 그러나 대부분의 좋은 코드는 한번에 작성되는 경우는 거의 없다. 글짓기와 비슷하다. 우선 말이 되게 하고 고쳐보자.


출처 : http://dsmoon.tistory.com/entry/%EC%9D%BD%EA%B8%B0-%EC%89%AC%EC%9A%B4-%ED%95%A8%EC%88%98

클래스 설계시에...

  눈도 고치고, 코도 고치고, 입도 고치고, 여기 저기 성형 수술한 사람을 얼마전 TV에서 보았다. 다들 놀랐다. 그렇게 많은 돈을 들여서 수술했음에도 우리가 생각했던 그런 미인은 아니였다. 이상하게 보이기까지 했다. 눈도 크고, 코도 오똑하고, 입술도 도톰하게 돈을 들이셨지만 결과는 망했다. 디테일도 중요하지만 얼굴이라는 전체적인 관점에서도 신경써야 한다.
  예쁜 코드도 이와 같다. 변수와 함수등에 신경을 많이 쓰더라도 클래스 관점에서 신경쓰지 않는다면 결국엔 노력하고도 좌절하는 일이 생긴다. Clean code는 clean class 없이는 불가능하다. 

클래스는 최대한 작게 만들자.
  변수를 수십개, 함수를 수십개 가진 클래스는 수정뿐만 아니라 사용하기도 어렵고 혼란스럽기까지 하다. 이 클래스틑 여러가지 이유로 변경되므로 보수 유지도 어렵게 한다. 이것 저것 다 쑤셔 넣어놓고 맥가이버 칼 같이 만능 클래스를 만들었다고 자랑스러워 하지 말자. 이것저것 남은 음식을 모아놓은 음식쓰레기와 같은 것을 만들었을 뿐이다. 그런것은 더럽고 다시 보고 싶지도 않다.
  그렇다면 얼마나 작아야 할까? 함수 10개? 5개? 1개? 함수나 변수 갯수의 기준을 숫자로 제한하기는 어렵다. 우선 클래스 이름에 해당 클래스의 모든 책임이 드러나게 이름을 지어보자. Manager, Super, Processor 등의 모호한 단어가 이름에 들어간다면 여러가지 책임을 하고 있을 것이다. 명확한 이름을 짓고 그에 해당하는 함수들만 모여 있어야 한다.
  클래스에 대한 설명도 적어보자. "~할 때에", "그리고", "~이거나", "~하지만", "~하면서", "~라면" 등의 말을 사용하지 않고 25단어 이내로 기술해보자. 이게 어렵다면 클래스를 책임에 따라 여러개로 나누어야 한다는 신호일 수 있다.

Single Responsibility Principle (SRP)
  잘 알려진 SOLID 법칙중 하나인 SRP는 클래스나 모듈의 변경 이슈가 하나여야 한다는 규칙이다. 대부분 SRP를 설명하면서 쉬운 규칙인데 잘 적용하지 않는다고 한다. 솔직히 고백하자면 나는 아직도 SRP가 어렵다. 우선 "책임"이란 것의 크기가 모호하다는 것이다. 내가 생각하는 "책임"과 동료가 생각하는 "책임"의 크기가 다를 수도 있다. 내가 어제 생각한 "책임"의 크기와 오늘 생각한 "책임"의 크기도 같다고 보장할 수 없다. 이러한 모호함을 없애기 위해 책임을 수정에 대한 이유로 정의 하기도 하지만 이 경우도 애매하다. 로버트 마틴은 actor를 기준으로 하면 모호함이 더 줄어든다고 한다. SRP는 그 자체 만으로도 큰 주제이므로 여기서 접어두고 다음으로 이야기를 이어가겠다.
  실제로 하나의 책임만 가지는 클래스로 잘게 나눈다는 것은 많은 연습을 해도 하기가 정말 어렵다. 인간의 두뇌는 비슷한 사실을 모아서 개념와 하도록 발달되었다. "둥굴고 빨간색이며 나무에 달린 것은 사과다." 라고 세상의 수많은 사과들을 하나의 개념으로 모은다. 이런 이유로 프로그램도 여러 비슷한 개념들을 자꾸 하나의 클래스나 모듈로 모아야 안심이 된다. 만능 클래스는 심리적 안정감을 주기까지 한다. 이것만 있으면 무엇이든 할 수 있다는 생각이 든다. 그러나 이런 저런 이슈로 그 클래스들을 수정할 때 쯤 되서야 내가 무슨 짓을 저질렀는지 후회하며 초과 근무를 하게 된다.
  최대한 잘게 나누자. 작은 것이 아름답다고 생각하자. 수많은 클래스에서 긿을 잃을 것이라고? 당신이 vi로 코딩하고 터미널에서 javac로 하나씩 컴파일하고 있다면 맞는 말일 수도 있다. 그러나 되도록이면 IDE를 사용하자. IDE는 멋진 네비게이션이 되어 줄 것이다. 우리는 많은 기능을 제공하는 무료 IDE를 사용할 수 있는 멋진 시대에 살고 있지 않은가. 

High Cohesion(높은 응집도)
  클래스는 내부 구성요소들이 똘똘 뭉쳐 있어야 한다. 한가지 수정하면 클래스내 여기저기를 고쳐야 하거나 클래스를 새로 짜야 한다면 응집도가 높은 것이다. 이상하게 들릴수도 있다. 한군데만 수정되는게 좋지 클래스를 통채로 수정한다고? 낮은 결합도(low coupling)과 같이 생각하면 이해하기 쉬울 것이다. 하나의 변경의 이유가 하나의 클래스의 변경만 가져오도록 하자는 것이다. 클래스 내부의 응집도는 높게, 클래스 사이의 결합도는 낮게 해서, 변경시 사고의 폭을 하나의 클래스로 제한하자는 것이다.
  높은 응집도를 가진 클래스는 자연스럽게 그 크기가 작아진다. 클래스의 각 메소드들은 거의 모든 멤버변수들을 골고루 사용한다. 몇개의 함수들이 일부 변수만 집중적으로 사용한다면 이들은 별도의 클래스로 분리 해야 한다. 이때 분리해야 할 함수들이 privat 속성이라 망설이게 되는 경우도 있다. 모두 공개되지 말아야할 내부 함수처럼 보일 수도 있다. 이런 경우 별도의 클래스로 분리하게 되면 public으로 공개해도 되는 면제부가 생긴다고 생각하자. 새로운 클래스로 위치가 바뀌었으니 공개의 기준이 바뀌는 것은 당연하다. 브라질에서 후보였던 축구 선수가 다른 나라로 옮기고 주전이 된다고 해서 이상하게 생각할 것은 아닌듯하다.

  좋은 클래스를 만드는 방법은 여기서 이야기 한 것들이 전부는 아니겠지만, 기본적으로 중요한 부분들은 대부분 조금씩 언급한듯하다. 물론 너무 짧은 지면에 많은 이야기를 하려다보니 이해가 잘 안되는 내용도 있을 것이다. 짧은 글 하나로 하루 아침에 모든 것을 이룰 수는 없다. 보수유지 하기 쉬운 클래스를 만드는 방법을 알기위해 공부해야 할 것들에 대한 화두를 던질 글 정도로 봐줬으면 한다.



출처 : http://dsmoon.tistory.com/category/Programming/Clean%20code%20%28by%20D.%20Moon%29