밤에 쓴 코드

Clean Code) 함수 본문

Clean Code

Clean Code) 함수

붱이🦉 2019. 5. 2. 16:58

함수


  • 함수는 작게 작게 작게

    • 길어지면 읽기힘들다
    • 인덴트가 2단 이상이면 읽기 힘들다
  • 한가지만 해라

    • 큰개념을 하는 메소드는 더 작은 개념을 수행하는 메소드들로 나누어야한다.

    • 내부에서 의미있는 이름의 동작로 분할해 낼 수있다면 , 한가지 일을 하지 않는다는 증거이다.

      • // 안좋은 예
        func getCoffee()->Coffee{
          let coffeeMenu = readLine() // 커피 메뉴를 입력 받는 의미있는 동작을 내부에 포함하고있다.
          let coffee = Coffee(menu:coffeeMenu)
          return coffee
        }
        // 사실상 어떤 커피를 만들지 입력을 받고 출력을 하고있다.
    • TO문단으로 확인해보자

      • 메소드명 하기위해서 , 코드 의 동작을 해야한다.
    • func getCoffee(menu:String)->Coffee{
        let coffee = order(menu:menu)
        return coffee
      }
      // To 커피를 얻기위해서 , 커피를 주문한다.
  • 추상화 수준을 일정하게 해라

    • 하나의 메소드에서 호출되는 메소드들은 일정한 단계의 추상화 수준을 가져야한다.

    • // 안좋은 예
      func getCoffee()->Coffee{
        // 커피를 만드는 과정을 여러 과정으로 추상화했다.
          let hotWater = Water().heatUp() 
        let shot = Shot()
        let coffee = Coffee(base:hotWater,shot:Shot)
        // 커피를 포장하는 과정을 하나의 메소드로 추상화했다.
        coffee.package()
        return coffee
      }
  • 하나의 함수는 더 작은 추상화단계로 분리해라

    • func getCoffee()->Coffee{
               let coffee = makeCoffee() // 커피를 만든다.
            coffee = packageCoffee(coffee:coffee) // 커피를 포장한다
          return coffee
      }
      func makeCoffee()->Coffee{
          let hotWater = Water().heatUp()  // 물을 끓인다 - 커피를 만드는 동작을 조금더 낮은 추상화 수준으로 
        let shot = Shot() // 샷을 만든다
        let coffee = Coffee(base:hotWater,shot:Shot) //커피를 생성한다
        return coffee
      }
      func packageCoffee(coffee:Coffee){
        coffee.put(holder())
        return coffee
      }
  • Switch 문을 피해라

    • Switch는 근본적으로 N가지 일을 하려고 한다.

    • 다형성을 이용하면 충분히 피할 수있고 , 변화에 유연하지 않다.

    • 다형성을 적용한 객체를 생성하는 추상팩토리에서는 사용해도 괜찮다.

    • 예시

      • 다형성

        • // 다형성을 적용하지 않음
          func drink(_ :Americano){}
          func drink(_ :Latte){}
          func drink(_ :Cappuccino){}
          /// 다형성을 적용하여 유연하게 할수 있다.
          func drink(_:Coffee){}
      • 팩토리에서의 switch 적용

        • struct CoffeeFactory{
            func createCoffee(type:String)->Coffee{
              switch(type){
                case .americano:
                    return Americano()
                case .latte:
                    return Latte()
                case .cappuccino:
                    return Cappuccino()
              }
            }
          }
  • 서술적인 이름을 사용하자

    • 코드는 시스템을 풀어가는 이야기이다.
    • 길어도 좋다 . 이해하기 좋게 네이밍을 하자
    • 일관성 , 동일한 기능에 같은 문구 , 명사 ,동사를 적용하자
  • 인수는 적은게 좋다

    • 단항 함수

      • 질문 / 처리 두가지중 하나의 동작을 하게한다.

      • 플래그는 내부에서 여러개의 동작을 한다는 것을 명시하는 것이다 - > 사용하지말자

        • // 안좋은 예
          func change(mode:Mode){
            if (mode == "채널")
                // 채널 변경
            if (mode == "음량")
                // 음량 변경
          }
          // 여러가지 일을 하고있다.
    • 이항 함수

      • 이항 함수는 순서가 자연스럽지 않다.

        • func move( _ to:Location , _ from: Location){
          }
          move(출발지,도착지)?
          move(도착지,출발지)?
          // 자연적인 순서가 아니라 헷갈린다
      • 이항 함수면 인수의 순서가 드러나는 함수가 더 좋은 이름의 함수이다

        • func moveStartToDestination( _ to:Location , _ from: Location){
          }
          // 인수의 순서가 이름에 드러난다
    • 삼항 함수

      • 가능 하면 인수를 묶을 수 있는 클래스로 바꾸자

        • func makeCircle( x:Int , y:Int, radius:Int)->Circle {}
          // 의미상으로 x,y 묶을 수 있다
          
          func makeCircle( point:Point, radius:Int)->Circle {}
          // x,y -> Point 로 
  • 부수효과

    • 이름에 보이지 않는 효과는 옳지 않다.
    • 시간적 결합 / 순서적 종속성 에 얽히지 마라
  • 출력 인수

    • 출력인수보다는 상태를 이용해라

      • protocol Drinkable{
          var amount:Int { get set }
        }
        struct Coffee:Drinkable{
        }
        func drink(target:Drinkable,amount:Int){
          target.amount-=amount
        }
        // 수정할 객체를 인수로 집어 넣는 것보다는 상태로 표현해보자
      • protocol Drinkable{
          var amount:Int { get set }
        }
        struct Coffee:Drinkable{
          func drink(amount:Int){    
                  self.amount-=amount
            }
        }
        // self/this로 출력인수를 대신할 수 있다.
  • 명령과 조회의 분리

    • func reserve(seat:Seat,name:String)->Bool{
        if(seat.isEmpty){
          seat.setReservedPerson(name:name)
          seat.isEmpty = false
          return true
        }
        return false
      }
      
      if(reserve(seat:A , name:"부엉이")){} // 이상한 코드가 생성된다
      
  • 오류

    • 오류코드 보다는 예외처리를 이용하자
      • 오류코드는 바로 핸들링 해야하지만 , 예외처리로 전파하면 최상단에서의 핸들링이 가능하다
    • 예외처리에 대한 핸들링은 하나의 동작이다.

'Clean Code' 카테고리의 다른 글

Clean Code) 단위테스트  (0) 2019.07.10
Clean Code ) 객체와 자료구조  (0) 2019.06.23
Clean Code) 형식 맞추기  (0) 2019.06.08
Clean Code) 주석  (0) 2019.06.08
Clean Code) 의미있는 이름  (0) 2019.04.21
Comments