클린코더

[클린코더]클린코더스_Function Structure

초보개발자.. 2022. 2. 7. 23:00

Function Structure

첫 시작은 

1.Function Structure에서 Arguments입니다.

인자가 많아지면 복잡도가 증가한다.

인자의 개수는 3개가 최대이다.

클린 코딩의 예시를 말해줍니다.

startdate, enddate의 경우 Date Range로 선언하여 한 클래스로 만들어버리는 걸 추천!

 

생성자에 많은 수의 인자를 넘겨야 한다면?

이럴때는 Builder 패턴을 적용하는 것이 더 좋다.!

위와 같은 코드는 자기가 선언해줘서 위치는 자기가 알지만 남들이 보면 어떠한 의도로 파라미터를 전달하는지 알기가 어렵다. 이와 같은 경우에 Builder를 사용해주면 좋다

 

이렇게 필수 파라미터와 옵션 파라미터를 나누어 사용합니다.

위와 같이 조금 더 이해하기 쉬운 코드로 바뀐 모습을 볼 수 있습니다.!

 

코드를 Null, 에러 체크로 더럽히지 말아라~!

null 체크를 하는 것 자체가 잘못되었다는 단서..

팀원이나 단위 테스트를 못 믿는다는 말

null 여부를 지속적으로 조사할 것이 아니라 단위 테스트에서 검증해야 함..

null체크보다 차라리 에러 처리를 잡는 것이.. 더 좋은.. 

 

2. Stepdown Rule

간단하게 말하면 모든 public은 위에 모든 private은 아래에

private함수는 주로 public에서 사용하는 메서드인데 

여기서 함수명만 잘 작명했다면 public part만 사용자들에게 전달한다면 이해가 될 것이다라는 말입니다.

중요한 부분은 위로, 상세한 부분은 아래로 ex) 잡지의 예 : 헤드라인, ( 하이라이트만 보면 된다..라는 의미)

작성자는 자기의 의도를 남들에게 잘 전달할 수 있다.

독자는 맨 위로 이해가 된다면 아래는 안 읽어도 된다.

 

3.Switches and Cases 

Switch 문장은 객체 지향적이지 않다. 라는 말이 많이 있습니다.

OOP의 가장 큰 이점 중 하나는 의존성 관리 능력입니다. 이게왜  switch랑 관련이 있는지 알아봅시당.

위의 사진은 모듈 A가 모듈 B의 함수를 사용 하는 경우이고, A 가 고수준(비즈니스) B가 저수준(구현체) 라고 가정해봅시당..

Source Code의 의존성 면에서 A가 B의 함수를 사용하고 있으므로, A안에 B의 객체를 가지고 있고, Runtime의존성 면에서는 실제로 메모리에 올라가서 실행될 때는 A에서 B의 호출이 일어나게 됩니다. 만약 B의 클래스에 변경이 일어난다면 A는 필연적으로 영향을 많이받게됩니다. (강한결합)

이번엔 OOP를 적용해봅시다. 

OOP를 적용시킨다면 B에 변형이 가해진다해도  A가 영향을 받지 않게할 수 있습니다.

중간에 I라는 인터페이스를 두고 느슨하게 결합하는 것이 중요한 점입니다.

A가 I라는 인터페이스를 호출하면 결국 I의 구현체인 B가 실행되는 것입니다.

결국 OOP를 적용하기 전과 똑같은 의존성이 생기지만 인터페이스를 사용하게되면 변경이 가해져도 유연하게 대처할 수 있습니다.

이를 DI(의존성역전 법칙)이라고합니다.

 

Switch-Case는 각각의 case에서 다른 모듈을 호출하여 사용하는데, case들 중 하나만 다른 모듈을 호출할 경우, switch 문은 영향을 받게 됩니다. -> Case문쪽에 변경이 가해진다 입니다.

Switch-case 문장을 제거하는 절차는 간단합니다. 다형성을 이용하여 런타임에 디스패치가 일어나도록 변환하는 것입니다. case에 있는 문장들을 별도의 클래스로 추출하여, 변경 영향이 발생하지 않도록 하는 것입니다.

 

`

왼쪽에 있는 다이어그램이 이전, 그리고 오른쪽에 있는 다이어그램이 리팩토링된 상태입니다

화살표를 보면 기존은 Switch 가 각각의 모듈을 호출하고있지만, 리팩토링된 이미지는 Switch 문이 제거되었고, Base class 를 통해 Derivatives 가 참조되는 방식으로 제어가 역전이 된 상태(DIP)입니다.

---Part2--

Temporal Couplling

함수들이 순서를 지키며 호출되어야 한다.

JDBC의 경우 connenction 연결 ->스테이트먼트 열고->...->연결 종료하고 이러한 과정이 있습니다. 

 

// file should be opened before processing
fileCommand.process(file);
// file should be closed after processing
위 방법은 실수할 여지도 많고 좋지않다.(누군가는 open안하고 실행하거나 실행하고 close를 안할 수도 있으니?)
 
전략패턴
fileCommandTemplate.process(myfile, new FileCommand(){
    public void process(File f){
        // file processing codes here
    }
});

class FileCommandTemplate{
    public void process(File file, FileCommand command){
        file.open();
        command.process(file);
        file.close();
    }
}

 

전략패턴과 컨텍스트 템플릿이라 불리우고, 외부에 전략을 둠으로 확장성 또한 고려되었고, 파일을 열고 닫는 부분까지 구성되어 있으므로 사용자는 주의하여 코딩하는 부담감을 줄일 수 있습니다.

 

CQS(Command Query Separate)

Command- 내부상태 변경

Query- 내부 상태 변경 x

상태를 변경하는 함수는 값을 변경하면 안된다.

값을 반환하는 함수는 상태를 변경하면 안된다.

일종의 약속..!입니다.(신뢰에 기반-> 저 사람의 행동이 예측가능하다..)

당신 코드의 독자들을 혼란스럽게 만들지 말라..

 

2번과 같은 경우는 User를 사용하기 위해서 매번 로그인을 해야하는 상황 혹은 User를 받고 싶지않은데 로그인할 때마다 User의 정보를 받는다는 것..

 

Tell Don't Ask

 

What에 대해서 생각하라. How에 대해서 생각하지 마라. 

1번째 방식이 제일 나쁜 방법이고 2 번째와 세번째가 올바른 예시입니다. 

Tell Don't Ask를 잘 지킨다면, Query 로직이 없어지게 됩니다. 위의 예제에서도 user.isLoggedIn과 같은 로그인 상태를 가져오는 쿼리 부분이 사라졌습니다.

 

첫 사진의 o는 함수에서 너무 많은 부분을 알아야 한다는 점이 잘못 되었습니다. 최종적으로 o.DoSomething() 만 하면 된다. 배보다 배꼽이 더 큰 경우?

 

early returns

메소드내에서 return이 있다면 위에 나타나야한다. 

함수를 다 읽지 않고, 현재 어떤 조건일 때 return 이 되는지 보고 싶을 경우가 존재. 다양한 경우를 생각한다면 return은 빠르게 나올 수록 좋다.

Loop문 중간에 return이 되는 경우는 문제가 된다(코드를 읽어나가는데 어려움)

코드가 동작하도록 하는 것보다 이해할 수 있게 하는 것이 더 중요.

 

Null is not a error

에러를 null 로 처리하지 말자

에러는 예외를 던지는 방식으로 처리하자.