반응형

내가 이걸 이해하고 있나? 싶어서

책읽는걸 그만두고 다른걸 해보다가 다시 내가 정리한 글들을 읽어봤다

 

체감상 10~30%만 실제로 응용하고 나머지는 안하고 있는 기분인데

%를 더 끌어올리게 노력해야겠다.

class

* SOLID

 

S : SRP(Single Responsibility Principle, 단일책임원칙)

하나의 객체는 하나의 책임(객체간 영향을 최소화하고 한가지 동작만을 수행)만을 가지도록 설계.

 

O : OCP(Open Closed Principle, 개방폐쇄원칙)

변경에는 닫혀있고 개방에는 열려있다.

→ 기존의 코드를 변경하지 않으면서 기능을 추가할 수 있도록 로직이 설계되어야 한다.

→ 클래스, 객체를 변경하지 않고도 클래스 환경을 변화할 수 있도록 구성한다.

 

L : LSP(Liskov Substitution Principle)

부모 클래스와 자식 클래스 간의 일관성이 있어야 한다.

→ 클래스를 재정의하지 않으며, 자식 클래스는 최소한 부모 클래스의 행위는 수행할 수 있어야 한다.

 

I : ISP(Interface Segregation Principle)

구체화보다는 추상화

→ 범용 인터페이스로 인해 여러 클라이언트(클래스)가 영향을 받기 보다는, 개별적인 클라이언트에 특화되어 서로의 영향을 최소화할 수 있도록 구성

 

D : DIP(Depedency Inversion Principle)

의존관계 역전 원칙

상위 계층(정책 결정)이 하위 계층(세부 사항)에 의존하는 전통적인 의존관계를 반전(역전)

→ 상위 모듈은 하위 모듈의 구현 내용에 의존하면 안 되고 상위 모듈과 하위 모듈 모두 추상화된 내용에 의존 

 

 

깨끗한 클래스를 만드는법에 대해 다루는 챕터

그렇다면 깨끗한 클래스란?

캡슐화 

클래스 외부 사용자는 사용하면서 내부 구현을 알 필요가 없고, 알아야할 이유도 없다.

 

클래스에서 꺼내와서 데이터 처리 X

객체에게 처리할 행위(method) 요청 O

 

캡슐화를 유지해야 좋은 클래스

풀어주는건 최후의 수단?

작은 클래스 

클래스는 함수와 마찬가지로 하나의 역할만(SRP), 작은 크기

 

- 만능 클래스보다는 하나의 책임

- 이름은 해당 클래스의 책임

- 모호한 이름 금지(Processor, Manager, Super 등) -> 여러 역할을 한다는걸 내포

- and, but 등 사용금지 -> 여러 역할을 한다는것 내포

 

응집도 up

보통 응집도를 높은 클래스 선호

클래스에 속한 메소드, 변수가 서로 의존하며 논리적인 단위로 묶인다

 

-> 큰 함수(클래스)를 여러개의 작은 역할로 나누면 응집도가 높아짐

 

변경하기 쉬운 클래스 & 변경으로부터 격리된 클래스

 

상세한 구현은 구현 요구사항이 바뀔때마다 위험에 빠진다 -> 인터페이스 & 추상 클래스를 통해

구현

 

클래스가 SRP, OCP 등 SOLID 원칙을 잘 만족했다면 요구사항이 변경되어도 유연한 대처가 가능 

 

결합도가 낮다 -> 변경에 영향받는 클래스가 격리되어 있어서 대처가 유연하다

 

ex) 레포지토리 패턴

https://0391kjy.tistory.com/39

 

Repository Pattern 이해하기

Repository Pattern? Repository(리포지토리) 패턴은 디자인 패턴 중 하나로, 데이터가 있는 여러 저장소(Local, Remote)를 추상화하여 중앙 집중처리 방식을 구성하고, 데이터를 사용하는 로직을 분리시키기

0391kjy.tistory.com

 

템플릿 메소드 패턴

https://gmlwjd9405.github.io/2018/07/13/template-method-pattern.html

 

반응형

'독서 > cleancode' 카테고리의 다른 글

cleancode - unit test  (0) 2022.01.25
clean code - 경계  (0) 2022.01.24
cleancode - 오류처리  (0) 2022.01.22
cleancode - 객체와 자료구조  (0) 2022.01.21
clean code - 주석 & 형식 맞추기  (0) 2022.01.20
반응형

 

test 종류에 관한 이야기는 간략히 

 

https://lodado.tistory.com/category/%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4%EA%B3%B5%ED%95%99

 

'소프트웨어공학' 카테고리의 글 목록

https://github.com/lodado 이전 블로그 https://blog.naver.com/ycp998

lodado.tistory.com

에 적었다.

 

테스트가 좋다는건 많은 사람들이 알지만 제대로 된 테스트 케이스를 작성하는건 어려운것 같다.

 

TDD(Test Driven Development)란?

 

테스트가 개발을 이끌어 나간다.

기본 개발 방식은 요구사항 분석 -> 구현 -> 테스트라면 

TDD는 요구사항 분석 -> 테스트 -> 구현

 

그렇다면 TDD는 어떤 상황에서 해야할까?

처음해보는 프로그램 주제
나에 대한 불확실성이 높은 경우
고객의 요구조건이 바뀔 수 있는 프로젝트
외부적인 불확실성이 높은 경우
개발하는 중에 코드를 많이 바꿔야 된다고 생각하는 경우
내가 개발하고 나서 이 코드를 누가 유지보수할지 모르는 경우
즉, 불확실성이 높을 때 TDD를 하면 된다.

불확실성이 높다 -> 피드백 & 협력이 중요한 경우 

 

TDD 법칙

 

1. 실패하는 단위 테스트를 작성할 때까지 실제 코드를 작성하지 않는다.

2. 컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트를 작성한다.

3. 현재 실패하는 테스트를 통과할 정도로만 실제 코드를 작성한다.

중요한것은 지저분한 테스트코드는 안하느니만 못하다는 것이다.

중요도로 따지면 기능 === 테스트 코드이다.

 

좋은 테스트 코드는 유연성, 유지보수성, 재사용성을 제공한다. 

즉, 테스트 코드가 좋으면 변경이 쉬워진다.

리펙토링에 거부감이 없어진다.

 

 

깨끗한 테스트 코드란?

 

가독성이 좋은 코드

명료성, 단순성, 풍부한 표현력 (중복성 제거)

 

최소의 표현으로 많은것을 나타냄

 

BUILD-OPERATE-CHECK 패턴을 추천

 

1. BUILD - 테스트 자료

2. OPERATE - 테스트 조작

3. CHECK - 조작 결과 체크(boolean식으로 체크하는게 좋음)

 

도메인(DSL)에 특화된 테스트 언어

API 위에다 함수, 유틸리티 구현 후 사용(경계-분리 엄청 좋아하시는거 같다)

테스트 당사자와 독자를 도와주는 테스트 언어

 

이중 표준

제한된 자원, 환경적 문제로 인해서

테스트 코드가 기능 코드랑 똑같은 환경, 컨벤션일 필요는 없다.

(mocking 관련?)

 

테스트당 개념 하나

 

테스트당 assert 하나라는 개념도 있다.

그것도 나쁘지는 않지만 저자는 기능 '하나'당 테스트 '하나' 추천

 

F.I.R.S.T 개념

 

Fast

테스트는 빠르게

(tmi로 cypress보다는 jest가 당연히 빠르고, 느리면 테스트를 주저하게됨)

 

Independent

각 테스트는 서로 의존적이지 않다.

독립된 환경설정

이유 : 독립되야 어디서 진짜 문제가 생겼는지 알기 쉽다 

 

Repeatable

어느 환경에서도 반복 가능, 동일한 결과

 

Self-Validating

bool 값으로 테스트 결과를 내야한다.

테스트를 파헤치지 않아도 결과를 알 수 있어야 한다.


Timely

테스트를 적시에 작성

단위 테스트는 테스트하려는 실제 코드를 구현하기 직전에 구현

 

결론

 

사실 '테스트' 라는 주제 하나만으로도 책 한권을 쓸 정도로 어려운 부분이다.

테스트 코드는 실제 코드만큼 중요하다

 

개인적 경험으로는 프로젝트때 테스트 코드를 한번 도입해보려고 시도했는데 

무작정 도입하고 보니 '깨끗하지 않은 테스트 코드'의 표본이 된거 같아서

이 글에서 말한 없느니만 못한 테스트가 된거 같기도 하다 😂

 

그래서 요즘 괜찮은 테스트를 짜기 위해 이론적으로 공부중이다.

 

테스트는 개인적으로 팀원간의 '공유 작업 파트'를 변경시 문제점을 빨리 캐치하고

커뮤니케이션 비용을 줄이는 측면에서 큰 도움이 된다고 생각되고,

또한 리펙토링의 거부감을 줄이는데 이점이 있다고 생각된다.

 

그밖의 tmi)

 

tistory에서 글자 색깔을 바꿀 수 있는걸 배웠다!

 

reference 

 

https://gmlwjd9405.github.io/2018/06/03/agile-tdd.html

 

[Agile] TDD(테스트 주도 개발)란 - Heee's Development Blog

Step by step goes a long way.

gmlwjd9405.github.io

 

반응형

'독서 > cleancode' 카테고리의 다른 글

cleancode - class  (0) 2022.02.26
clean code - 경계  (0) 2022.01.24
cleancode - 오류처리  (0) 2022.01.22
cleancode - 객체와 자료구조  (0) 2022.01.21
clean code - 주석 & 형식 맞추기  (0) 2022.01.20
반응형

시스템에 들어가는 모든 소프트웨어를 직접 개발하는 경우는 드물다

이 장에서는 소프트웨어 경계를 깔끔하게 처리하는 기법과 기교를 살펴본다.

 

외부 코드 사용하기


더 많은 환경에서 돌아가야 더 많은 고객이 구매하니까 패키지 제공자나 프레임워크 제공자는 적용성을 최대한 넓히려 애쓴다. 반면 사용자는 자신의 요구에 집중하는 인터페이스를 바란다. 

이런 차이로 시스템 경계에 문제가 생길 소지가 많다.

 

ex) java.util.Map

 

설명이 완전히 java적인 내용이긴 한데.. 뭘 의미하고 있는지를 알아두면 좋을것 같다.

 

Map은 다양한 인터페이스로 수많은 기능을 제공한다.

이러한 기능성과 유연성은 확실히 유용하지만

Map을 그대로 넘긴다는 뜻은 예를들어 Map 내용을 지우는 clear()함수를 누구나 지울 권한을 가질 수 있다는 뜻과 같다.

 

Map sensors = new HashMap(); 
Sensor s = (Sensor) sensors.get(sensorId);

또한 위와 같이 Map을 올바른 유형의 Object로 변환할 책임은 Map을 구현한 곳이 아닌 실제로 사용하는 클라이언트 쪽에 있게 된다.

또한 의도가 드러나지 않는다. 대신 다음과 같이 Generics를 사용하면 코드 가독성이 크게 높아진다.

Map<String, Sensor> sensors = new HashMap<Sensor>();
Sensor s = sensors.get(sensorId);

하지만 여전히 사용자가 필요하지 않은 기능까지 넘기는것이 좋지 않다.

 

다음과 같이 제네릭스 사용 여부라던지 경계 인터페이스인 Map을 Sensor 클래스 안에 숨겨보자.

public class Sensors { 
	private Map sensors = new HashMap();
    
	public Sensor getById(String id) { 
	return (Sensor) sensors.get(id);
	} 
}

 

그러면 Map 인터페이스가 변하더라도 Sensor 클래스 안에서 객체 유형을 관리하고 변환할 수 있게 된다.

또한, 필요한 인터페이스만 제공할 수 있게 된다.

 

경계 살피고 익히기

 

외부 코드를 익히기는 어렵다. 외부 코드를 통합하기도 어렵다. 만약 곧바로 우리쪽 코드를 작성해 외부 코드를 호출하는 대신 먼저 간단한 테스트 케이스를 작성해 외부 코드를 익히면 어떨까?

 

학습 테스트 는 프로그램에서 사용하려는 방식대로 외부 API를 호출한다. 통제된 환경에서 API를 제대로 이해하는지를 확인하는 셈이다. 학습 테스트는 API를 사용하려는 목적에 초점을 맞춘다.

 

학습 테스트는 공짜 이상이다.

 

학습 테스트는 이해도를 높여주는 정확한 실험이다.

학습 테스트에 드는 비용은 없고 필요한 지식만 확보하는 쉬운 방식이다.

 

패키지가 새로운 버젼이 나오면 학습 테스트를 돌려 차이가 있는지 확인한다.

새 버젼이 호환되지 않으면 바로 학습 테스트가 밝혀낸다.

 

이런 테스트가 존재한다면 어느 부분이 호환성 문제가 생기는지 빨리 밝혀내고 새 버젼으로 빨리 이전하기 쉬워진다.

 

아직 존재하지 않는 코드를 사용하기

경계와 관련해 또 다른 유형은 아는 코드와 모르는 코드를 분리하는 경계다.

 

ex) 저쪽 팀이 아직 API를 설계하지 않았으므로 구체적인 방법은 모를때
구현을 미루고 이쪽 코드를 진행하기 위해 자체적으로 인터페이스를 정의한다.

 

어뎁터 패턴을 이용해서 다른 팀이 개발하고 있는 코드를 분리하고, API 사용을 캡슐화해 API 바뀔때 수정할 코드를 한곳으로 모은다.

(부가적으로 테스트도 쉬워짐)

 

깨끗한 경계

 

통제하지 못하는 코드(오픈소스나 외부 라이브러리 등)를 사용할때는 주의해야한다.

경계에 위치하는 코드는 깔끔히 분리한다.

 

이쪽에서 상대 코드를 세세히 알 필요는 없다. 외부패키지에 의존하지 말고 통제가 가능한 우리 코드에 의존한다.

외부 패키지를 호출하는 코드를 가능한 줄여 경계를 관리하자.

 

Adapter pattern을 사용하던지 위에서 보았던것처럼 새로운 클래스로 경계를 감싸서 우리가 인터페이스를 만들어 제어하자.

 

 

핵심 

 

-> 외부 라이브러리와 맞닿는 부분은 직접 쓰지말고 인터페이스 같은 방식으로 분리하고, 주도적으로 제어

이 파트는 좀 심오한 내용인것 같다.. 

일단 '분리' 자체에는 동의한다.

반응형

'독서 > cleancode' 카테고리의 다른 글

cleancode - class  (0) 2022.02.26
cleancode - unit test  (0) 2022.01.25
cleancode - 오류처리  (0) 2022.01.22
cleancode - 객체와 자료구조  (0) 2022.01.21
clean code - 주석 & 형식 맞추기  (0) 2022.01.20
반응형

오류처리(예외처리) 관련 파트이다

 

요즘 기존에 했던 프로젝트 복습?하면서 react의 Suspense 관련 글을 보고 있는데 Suspense가 좋은 이유가

'로직'과 '에러' 처리 부분을 나눌 수 있어서 좋은 코드를 만들 수 있다고 한다. 

Suspense에 관련해서 더욱 흥미로운 내용도 더 많은데 이건 따로 글을 하나 파서 조만간 써봐야겠다. 

 

오류 처리

 

오류 처리는 매우 중요하다.

소프트웨어는 복잡계이고 사람이 작성하다보면 뭔가 빼먹은게 있을 수 있다. 버그는 거의 필연적으로 일어나기 때문에

오류 처리를 우아하게? 해내는것도 중요하다.

오류 코드보다는 예외를 사용하자

if문으로도 오류 처리를 할 수 있지만 그렇게 되면 '로직' 과 오류 처리 코드를 혼동하기 쉽다.

역할을 분리하자

 

try-catch-finally 문을 애용하자

예외처리 부분은 try 부분에 넣고, 꼭 실행되야 하고 오류나 예외처리 부분이 절대로 일어나지 않는다고 가정되는 부분은 finally 부분에 넣는다.

 

미확인 예외를 사용해라 

 

자바스크립트의 경우 거의 반강제(?)로 미확인 예외를 사용하지만...

 

자바의 경우 기본 Exception이 있고 Exception을 상속받아 온갖 종류의 Exception이 있는것으로 알고 있다.

확인 예외(구체화된 Exception 종류)는 명확하지만

에러를 여러단계를 거쳐 throw 받는경우 콜백의 상위 함수에서는 그 오류에 알맞은 Exception handling을 해줘야한다. 이것은 OCP(Open Closed Principle) 를 위반한다.

 

즉, 하위 단계에서 코드를 고치면 상위 단계 메서드 선언부를 전부 고쳐야한다.

일반적으로 애플리케이션은 의존성이라는 비용이 이득보다 크니 미확인 예외를 사용하자.

 

예외에 의미를 제공하자

예외를 던질때는 전후 맥락을 짚을 수 있는 단서를 제공한다. 그럼 오류의 원인과 위치를 찾기 수월해진다.

오류 메세지에 정보를 담아 예외를 던지자.

 

* 다만 production 코드에서 오류 hint를 주는것은 보안 문제가 발생할 수 있으니 주의?

 

 호출자를 고려해 예외 클래스를 정의하자

 

오류를 정의시 프로그래머의 가장 중요한 관심사는 '오류를 잡아내는 방법'

외부 API를 사용 시 감싸기 방식(wrapper)이 최선

 

public class LocalPort {
    private ACMEPort innerPort;

    public LocalPort(int portNumber) {
        innerPort = new ACMEPort(portNumber);
    }

    public void open() {
        try {
            innerPort.open();
        } catch (DeviceResponseException e) {
            throw new PortDeviceFailure(e);
        } catch (ATM1212UnlockedException e) {
            throw new PortDeviceFailure(e);
        } catch (GMXEError e) {
            throw new PortDeviceFailure(e);
        }
    }
}

LocalPort는 단순한 wrapper class이다.

 

외부 API를 감싸면

1. 외부 라이브러리와 프로그램 사이에서 의존성이 크게 줄어든다.

2. 다른 라이브러리로 갈아타기 쉬워진다.

3. 테스트 할때 API대신 테스트 코드를 넣어주는 방식으로 테스트하기 쉬워진다. 

 

정상 흐름 정의

 

'비즈니스 로직'와 '오류 처리'가 잘 분리된 코드가 좋은 코드

클라이언트 코드가 특수 상황을 처리할 필요가 없게 하자.

 

특수 상황이란 논리와 예외처리를 뒤섞어 막 사용하는 경우를 뜻한다

예를 들어 

try {
    MealExpenses expenses = expenseReportDAO.getMeals(employee.getID());
    m_total += expenses.getTotal();
} catch (MealExpensesNotFound e) {
    m_total += getMealPerDiem();
}

해당 코드는 직원이 식비를 청구했으면 expense.getTotal() 함수로 식비 청구한 만큼 값을 계산하고

청구하지 않았다면 예외처리를 일으켜서 catch문에서 기본 식비를 계산하는 상황이다.

 

이런경우엔 '비즈니스 로직'으로만 처리해야 좋은 코드이니 주의하자.

 

(* 해당 로직을 올바르게 구현한 객체를 MealExpense 객체로 반환해서 처리하는것을 특수 사례 패턴이라 부른다, 즉 클래스를 만들거나 객체를 조작해 특수 사례를 처리하는 방식) 

 

null을 반환하지 말자

만약 메소드가 제대로 처리 안되었다는 뜻으로 가끔 null을 반환할때도 있는데

그 대신 예외를 던지거나 특수 사례 객체를 반환한다.

 

TS를 쓰면 그나마 양반인데  JS에서 null을 반환하는 순간 스스로 지옥을 만들수도 있을꺼 같다ㅋㅋ

null을 반환하는 대신 빈 리스트를 반환한다면 훨씬 깔끔하다.

외부 API가 null 반환시 감싸기 메서드를 구현해 예외를 던진다.

 

null을 전달하지 말자 

(oauth2 passport 에서 하던거 같던데??)

null을 반환하는거보다 치명적

null을 기대하는 메서드가 아니라면 null을 전달하지말자

 

assert 문으로 null 예외처리를 한다. 

js의 경운 assert 없으니 이런식으로 구현..?

 

function assert(condition, message) {
    if (!condition) {
        throw new Error(message || "Assertion failed");
    }
}

https://stackoverflow.com/questions/15313418/what-is-assert-in-javascript

 

 

결론

 

깨끗한 코드란 가독성뿐만 아니라 안정성도 높아야 한다.

오류 처리와 논리를 분리하면 튼튼하고 깨끗한 코드를 작성 가능, 유지보수성도 크게 높아짐

 

toss 컨퍼런스의 suspense에 대한 내용도 비슷한 내용을 담고 있으니 한번 보는거도 추천한다

 

https://toss.im/slash-21/sessions/3-1

 

프론트엔드 웹 서비스에서 우아하게 비동기 처리하기

API를 호출하거나 네이티브 앱과 통신할 때 프론트엔드 웹 서비스에서는 반드시 비동기 작업이 일어나게 됩니다. 일상처럼 다루고 있지만 정작 UI에서 다루기 힘든 비동기 프로그래밍. React Suspens

toss.im

반응형

'독서 > cleancode' 카테고리의 다른 글

cleancode - unit test  (0) 2022.01.25
clean code - 경계  (0) 2022.01.24
cleancode - 객체와 자료구조  (0) 2022.01.21
clean code - 주석 & 형식 맞추기  (0) 2022.01.20
clean code - 함수  (0) 2022.01.19
반응형

이번 챕터는 객체지향 개념의 챕터인거 같다.

사실 react의 함수형 컴포넌트를 사용하다보면 객체지향과 멀어지는 경향이 있는것 같다.

그래도 사고를 말랑말랑하게 하기위해 읽어보면 좋을듯 싶다.

 

외부 사용자가 클래스 내부 변수에 의존하지 않게 하고, 

외부에서의 조작으로 인해 의도치 않은 변경이나 버그를 막기 위해

흔히 private를 사용한다. (캡슐화, 정보 은닉)

 

이때 의무적으로? get, set 함수를 public 하게 외부에 노출시키는데 기계적으로 노출시키기보다는 

이러한 방식을 좀 더 깊게 고찰해보자는 챕터인것 같다.

 

객체/자료구조

  • 객체는 추상화 뒤로 자료를 숨긴 채 자료를 다루는 함수만 공개(내부 구조 은닉)
  • 자료 구조는 자료를 그대로 공개 (내부 구조 노출)
  •  

객체의 자료 추상화

구현을 감추기 위해선 추상화가 필요하다.

단순히 함수로 변수를 감춘다고 구현이 감춰지는것은 아니다. => 좀 더 고차원적인 사고가 필요

사용자가 어떻게 구현되었는지 몰라도(메소드 내부를 몰라도) 자료를 사용할수 있어야 좋은 코드 작성 방식이다. 

 

자바 코드로 생각해보자. (js는 interface 개념이 없어서 java로 보는게 편하..다?)

//구체적인 Point class이고 명시적 직교 좌표계라는것을 알 수 있다.
public class Point {
	public double x;
	public double y;
}

// 이게 직교 좌표계인지 무엇인지 알수는 없다(추상화 되어있다) 다만 사용하고 싶은
// 자료를 명백히 제공한다.
// 추상적인 Point interface이고 클래스 메서드로만 접근 가능하게 강제한다.
public interface Point {
	double getX();
	double getY();
	void setCartesian(double x, double y);
	double getR();
	double getTheta();
	void setPolar(double r, double theta);
}
//X,Y는 get할때 각각 구하는데 set은 한꺼번에 해야한다. => 개선 필요

위 코드의 public class는 구현을 외부로 노출하고

아래는 구현을 숨긴다.

 

일단 위의 point class는 private로 변수를 선언해도 getter, setter를 외부에 노출한다면 구현을 외부로 노출한다.

 

또 다음 예시를 보자.

//구체적 숫자 값으로 제공
public interface Vehicle {
	public getFuelThankCapacityInGallons();
	public getGallonsOfGasoline();
}

//연료 상태를 백분율이라는 추상적 값으로 제공
public interface Vehicle {
	double getPercentFuelRemaining();
}

위 구체적 인터페이스보다는 자료를 사용할때 추상적으로 제공하는 아래의 인터페이스가 더 좋다.

 

결론적으로, 

구현자는 사용자에게 '내부'를 알지 않아도 사용 가능하도록 추상화하는것이 좋다.

 

절차지향적 vs 객체지향적

본질적으로는 자료(구조)를 어떻게 사용할까에 대한 고민이다.

 

절차지향 

 

절차지향적인 코드는 기존 자료구조를 변경하지 않으면서 새 함수를 추가하기 쉽다.

다만 새로운 자료구조를 추가하기 어렵다. (기존 함수를 전부 바꿔야함)

 

* 새로운 동작을 추가하는 유연성 -> 자료구조, 절차적인 코드

 

객체지향

 

객체지향적인 코드는 기존 함수는 그대로 놔두고 새로운 클래스를 추가하기 쉽다. (상속을 통해?)

다만 새로운 함수를 추가하기 어렵다. (그러기 위해서 모든 클래스를 고쳐야 한다.)

 

* 새로운 자료 타입을 추가하는 유연성 -> 객체 지향

 

서로 상호보완적인 관계이니 무조건 한쪽만 사용하는것은 지양하자.

(객체지향적 사고가 100% 정답은 아님) 

그런데 둘을 혼합하면 잡종 구조가 되니 이런 구조는 서로의 단점만 보이게된다. 그래서 비추천

 

디미터 법칙

잘 알려진 휴리스틱(경험에 기반하여 문제를 해결하거나 학습하거나 발견해 내는 방법, 최고가 아닌 최적의 방식)

모듈은 자신이 조작하는 객체의 속사정을 몰라야 한다는 법칙

 

정확히는 클래스 C의 메서드 f는 아래 객체의 메서드만 호출해야 한다.

  1. 클래스 C
  2. f가 생성한 객체
  3. f 인수로 넘어온 객체
  4. C 인스턴스 변수에 저장된 객체

이때 객체가 반환하는 객체의 메소드를 바로 호출하면 안된다. (잘 모르는것은 경계)

 

기차 충돌

final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();

객체가 한 줄로 이어져서 사용하는 방식으로 디미터 법칙을 무시한다.

일반적으로 조잡하다고 느껴질수 있기 때문에 가능하면 피하는것이 좋다.

 

해당 방식은 다음처럼 고치면 어떻게 될까?

Options opts = ctxt.getOptions();
File scratchDir = opts.getScratchDir();
final String outputDir = scratchDir.getAbsolutePath();

 

이렇게 나눠도 ctext 객체가 opt를 가지고, scratchDir를 가지고 있다는 '내부 구조'를 알 수 있기 때문에

디미터 법칙을 위반할 수 있는 가능성이 존재한다.

그리고 이 위반 가능성은 ctxt가 '자료구조'인지 '객체' 인지에 따라 달라진다.

 

자료구조의 경우 

final String outputDir = ctxt.options.scratchDir.absolutePath;

처럼 사용해도 디미터 법칙의 대상이 되지 않는다.

(자료구조는 내부 구조를 노출해도 되므로 상관X)

 

그럼 자료구조가 아닌, 객체라면 코드를 어떻게 개선해야할까?

'내부 방식'을 몰라도 작동할 수 있도록 개선하면 된다.

 

final String outputDir = ctxt.getScratchAbsolutePath();

 

자료 전달 객체(DTO)

전형적인 자료구조

공개 변수만 있고 함수가 없는 클래스(get, set까진 있다)

 

- 데이터 저장용(데이터베이스와 통신, 소켓에서 받은 메세지 구문 분석시 유용)

- 가공되지 않은 정보를 받아와 애플리케이션 코드에서 사용할 객체로 변환하는 일련의 단계에서 사용 

(raw data)

 

class Address {

	private final String street;
	private final String streetExtra;
	private final String city;
    ...
}

활성 레코드

활성 레코드는 DTO의 특수한 형태

 

공개 변수, 비공계 변수에 조회/설정 함수 제공

save, find 같은 탐색 함수 제공

 

데이터베이스, 다른 소스에서 자료를 직접 변환한 '결과'

비즈니스 로직 메서드를 활성 레코드에 넣는것은 옳지 않다. 자료구조로 취급하자.

 

결론 

 

나는 잡종 구조?를 좀 많이 쓴편이긴 한데 객체와 자료구조를 분리해서 생각해야겠다

또한, 훌륭한 개발자는 편견에 빠지지 않는다. 알맞은 상황에 알맞게 쓰자

* 새로운 동작을 추가하는 유연성 -> 자료구조, 절차적인 코드

* 새로운 자료 타입을 추가하는 유연성 -> 객체 지향

 

 

 

 

반응형

'독서 > cleancode' 카테고리의 다른 글

cleancode - unit test  (0) 2022.01.25
clean code - 경계  (0) 2022.01.24
cleancode - 오류처리  (0) 2022.01.22
clean code - 주석 & 형식 맞추기  (0) 2022.01.20
clean code - 함수  (0) 2022.01.19
반응형

주석

저자는 주석에 매우 부정적인 입장이다.

 

그 이유는

 

1. 주석은 (코드가 계속 변경되며) 최신 내용이 갱신되지 않을수도 있다. 

2. 나쁜 코드를 설명하는 역할을 한다.

3. 주관적 의미를 써놓으면 이해하기가 어렵다

 

주석을 쓰기보다는 명확한 함수, 변수명으로 코드를 표현하기를 원하는것 같다.

 

다만 다음 방식으로는 사용을 추천한다.

 

1. 주의사항

2. 법적인 주석(copyright ~~)

3. 기본적 정보 제공 

ex) 

// kk : mm : ss 형식
const timeMatcher = /\d\d:\d\d:\d\d\.exec('문자열 아무거나...');

4. 의미를 명료하게 밝히는 주석

5. TODO 주석 (다른 프로그래머에게 할일을 알려주는 주석)

 

다만 TODO 주석의 경우 사용할경우 경험상 아무도 맡지 않아서 혼자 미아가.. 되버리는 경우도 있었기 때문에 팀원과 소통을 잘해야한다.

 

결론 : 

 

주석을 달아야한다는 의무감에 빠져서 의미없는 주석을 생산하지 말자.

나도 변수, 함수명으로 내가 하는 목적을 설명하고자 노력하고 있다.

프론트엔드는 특히 주석 === 코드 용량이라 쓰지 말라는 이야기가 있기도..?(minify javascript를 쓰면 되긴한다.)

 

형식 맞추기

'돌아가는 코드' 보다는 코드의 품질을 높이자

팀으로 일한다면 팀이 합의해 규칙을 정하고 모두가 따라야 한다.

가능하면 팀 모두가 도구 사용 -> prettier, esLint?

 

좋은 코드를 만드는법

 

1. 잘게 쪼개자

 

2. 신문 기사처럼 작성(위에서 아래로 볼 수 있게 작성)

 

3. 개념은 빈 행으로 분리 (너무 코드를 빽빽하게 적지 말자), 같은 개념은 가깝게

 

4. 변수는 각 함수 맨 처음에 선언 

 

tmi인데 react에서 useState, hook 등도 항상 최상위에 선언한다.

그 이유는 가독성 장점도 있지만 코드의 흐름이 항상 일관되게 진행될 수 있도록 보장하기 위해서이다.

그리고 react 는 state를 일종의 배열 형태로 관리하고 순서는 보장하기 않기 때문에 항상 선언 순서는 보장되야 한다.

 

5. 인스턴스 변수는 함수 맨 처음에 선언(private 변수 등?)

이유는 2번 참고

 

6. 함수가 다른 함수를 호출하면 가까이 배치, 호출 하는 함수는 호출 되는 함수보다 앞에

이유는 2번 참고 

 

7. 유사한 코드(개념적 친화도가 유사한 코드)는 비슷한 곳에 배치 

 

8. 들여쓰기 기준 통일 

9. 팀 규칙 정하기 (가장 중요)

초기에 시간이 많이 걸려도 규칙은 확실히 정하고 팀원 모두 인지하는게 중요한거 같다.

 

 

 

 

읽은 후기 :

 

사소하지만 중요한 파트라고 생각된다.

특히 팀 규칙 정하는게 정말 중요하다..

팀원 전부의 코드 스타일이 다를 수 있기 때문에(경험상 팀플할때마다 모든 사람이 전부 다 달랐음) 규칙은 반드시 정해야한다.

요즘은 prettier, esLint같은 좋은 도구들이 많이 생겼으니 애용해 보자.

 

반응형

'독서 > cleancode' 카테고리의 다른 글

cleancode - unit test  (0) 2022.01.25
clean code - 경계  (0) 2022.01.24
cleancode - 오류처리  (0) 2022.01.22
cleancode - 객체와 자료구조  (0) 2022.01.21
clean code - 함수  (0) 2022.01.19
반응형

책을 읽고 내용을 메모해두려고 합니다.

1. 함수는 작게

여러 역할을 하는 함수는 역할별로 분리해서 쪼갠다

특히, 함수는 한가지만 잘해야 한다, 한가지만 해야한다.

ex) 이미지가 들어왔을때 이미지 형식이 알맞은지 검사하고, 알맞지 않다면 이미지 정보를 삭제하고 맞다면 저장소에 저장하는 함수

(글로도 벌써 이해하기 힘들다ㅋㅋ)

위 예시 함수는 아래의 3가지 함수로 분리 가능하다.

1. 이미지 형식 검사

2. 이미지 정보 삭제

3. 이미지 저장소에 저장

function yourfuncExample(image){

    if(isVaildImageExt(image)){
        saveImage(image);
    }
    else{
        deleteImage(image);
    }
}

isVaildImageExt(image){
    ... // 형식 확인하는 함수
}

saveImage(image){
    .... // 이미지 저장하는 함수
}

deleteImage(image){
    ... // 이미지 삭제
}

내 경험상 한번 쓴 함수가 다시 뒤에 재사용되는 경우가 많았던걸로 기억한다.

그래서 최대한 잘게 쪼개놓으면 나중 재사용 할 수 있어서 시간절약 및 유지보수에 매우 도움이 된다.

 

2. 서술적인 이름으로

추상화 수준은 동일히

이름을 붙일땐 일관성이 있고 명확해야 한다.

보통 함수는 동작이므로 동사 형태의 이름을 지니며

함수는 같은 문구, 명사, 동사 순서를 지닌다

ex) includeTeardownPages, includeSuiteTearDownPage

 

3. 함수 인수

인수가 많아지면 많아질수록 코드를 이해하는데 어려움이 생긴다. 또한, 테스트할때 매우 복잡해진다.

3항부터는 조금 생각해보자

좀 많아진다고 생각되면 객체로 한번 감싸자

function notGood(a,b,c){
    //이거보다는 
}

function good({a,b,c}){
     // 객체로 한번 감싸서 구조 분해 할당을 활용
}

개발을 하면서 a,b,c의 순서를 외워야할 필요가 없기 때문에 객체(객체 리터럴이나 클래스)로 감싸는거 추천

 

4. side Effect를 일으키지 마라

함수형 프로그래밍의 side Effect 느낌보다는 함수가 자신의 역할 이외에 다른 역할을 추가적으로 한다든지

의도치않은 부수 효과를 일으키는 경우를 말하는것 같다.

예를 들어 dom tree의 element를 여러 함수가 공유해서 같이 조작한다든지 매개변수로 넘어온 객체의 내부 값을 변경한다든지 예상치 못한 결과를 내는 경우를 뜻한다.

사실 1번을 잘 지키면 4번은 저절로 따라온다.

모듈, 함수간 결합도(의존도)를 줄이고 응집도를 높이자.

 

5. 명령과 조회 분리

뭔가 수행하거나, 뭔가 답하거나 여러 종류중 하나만 하는것을 추천

조건이 맞나? isExist ~~

수행? do~~

설정? set~~

 

6. 오류 처리엔 예외처리 이용

function ex(code){

    if(isVaild(code)){ 
        console.log('error');
        return;  //이거보다는 
    }
}

function ex2(code){
    try{
     isVaild(code); // 여기서 throw error
    }
    catch(e){
        console.log(e);
    }

}

예시가 좀 엉성하긴 한데.. 에러처리에는 예외처리를 애용하자. (역할분리)

try catch문 자체도 따로 함수로 뽑아내는것이 좋다.

추가적으로 발생가능한 Error들을 정의하는 Error set을 만들자.

 

7. 반복하지 마라

(중요해서 또나옴..?)

동일한 로직을 함수로 분리하고 재사용하자.

유지보수 및 시간 절약에 큰 도움이 된다.

만약 동일한 로직인 코드를 4개를 만들었다면 뭔가 변경사항이 있을때 4곳을 각각 수정해야한다.

그런데 함수로 분리하고 변경사항을 해당 함수에만 적용한다면 한곳만 고치면 되니 매우 편리해진다.

반응형

'독서 > cleancode' 카테고리의 다른 글

cleancode - unit test  (0) 2022.01.25
clean code - 경계  (0) 2022.01.24
cleancode - 오류처리  (0) 2022.01.22
cleancode - 객체와 자료구조  (0) 2022.01.21
clean code - 주석 & 형식 맞추기  (0) 2022.01.20

+ Recent posts