[개발 도서] Clean Code :: 12장 - 창발성

🎓 창발

하위 계층(구성 요소)에는 없는 특성이나 행동이 상위 계층(전체 구조)에서 자발적으로 돌연히 출현하는 현상

  • 단순한 결합이 복잡한 결과를 나타내는 것을 의미
  • 인간의 뇌를 예로 들면 하나의 뉴런은 인식능력이 없지만 수십억개의 뉴런이 결합하게 되면 자기 인식이 발생하는 현상을 말하는 것이다.
  • 이 창발성은 명령을 내리는 조정자 없이 각 부분의 의사소통으로 자기 조직화를 이루게 되고 이러한 밑으로 부터의 힘은 예기치 못한 기능을 발현하는 힘을 말한다.

즉 창발적 설계란 어떤 규칙과 원칙에 따라 설계를 하게 되면, 그것들이 모여 아주 좋은 거시적 설계가 될 수 있음을 말한다.

📕 창발적 설계로 깔끔한 코드를 구현하자

켄트 벡은 다음 규칙을 따르면 설계는 '단순하다'고 말한다 (중요도 순으로 나열)

  • 모든 테스트를 실행한다.
  • 중복을 없앤다.
  • 프로그래머 의도를 표현한다.
  • 클래스와 메서드 수를 최소로 줄인다.

네가지 규칙을 따르면 코드 구조와 설계를 파악하기 쉬워져 SRP, DIP와 같은 원칙을 적용하기 쉬워지며 우수한 설계의 창발성을 촉진시킬 수 있다.


📗 단순한 설계 규칙 1 - 모든 테스트를 실행한다.

설계는 의도한대로 돌아가는 시스템을 내놓아야 한다.
모든 테스트를 거치게 되면 좋은 점은 아래와 같다.

  • 설계가 의도한 대로 돌아가는지 검증 가능하다.
  • 테스트가 가능한 시스템을 생성 가능하다.
    • 테스트가 불가능한 시스템은 검증 또한 불가능하다.
  • 원활한 테스트를 위해 단일 책임 원칙을 준수하는 클래스가 나오게 된다.
    • 다량의 테스트 케이스를 만들어 원활한 테스트가 가능하게 한다.
      • 다량의 테스트 케이스를 통해 DIP와 같은 원칙을 적용하고, 의존성 주입, 인터페이스, 추상화 등과 같은 도구를 사용하여 결합도를 낮출 수 있다.

테스트 케이스를 만들어 검증하는 과정을 통해 낮은 결합도와 높은 응집력에 부합하는 코드를 만들 수 있다.


📙 단순한 설계 규칙 2~4: 리팩터링

테스트 케이스를 모두 작성했다면, 코드와 클래스를 정리한다. -> "점진적 리팩터링"
코드 추가하고 테스트 케이스 돌려보는 과정 반복

소프트웨어 설계 품질 높이는 방법

  • 응집도 높이기
  • 결합도 낮추기
  • 관심사 분리
  • 관심사 모듈화
  • 함수와 클래스 크기를 줄이기
  • 이름 바꾸기
* 리팩터링 : 결과의 변경 없이 코드의 구조를 재조정함
* 응집도 : 하나의 프로그램을 구성하는 각각의 모듈이 그 고유의 기능을 잘 처리할 수 있는지를 나타내는 정도
* 결합도 : 서로 다른 모듈 간에 상호 의존하는 정도 또는 연관된 관계

📘 중복을 없애라

중복은 추가 작업, 추가 위험, 불필요한 복잡도를 의미하기 때문에 최소화 하는것이 좋다.

💻 구현 중복 예시 218p

📍 Templete Method 패턴

  • 고차원 중복 제거를 위한 기법
  • 특정 작업을 처리하는 일부분을 서브 클래스로 캡슐화
  • 전체적으로는 동일하면서 부분적으로는 다른 구문으로 구성된 메서드의 코드 중복을 최소화 할 때 유용
//추상 클래스 선생님
abstract class Teacher{
	
    public void start_class() {
        inside();
        attendance();
        teach();
        outside();
    }
	
    // 공통 메서드
    public void inside() {
        System.out.println("선생님이 강의실로 들어옵니다.");
    }
    
    public void attendance() {
        System.out.println("선생님이 출석을 부릅니다.");
    }
    
    public void outside() {
        System.out.println("선생님이 강의실을 나갑니다.");
    }
    
    // 추상 메서드
    abstract void teach();
}
 
// 국어 선생님
class Korean_Teacher extends Teacher{
    
    @Override
    public void teach() {
        System.out.println("선생님이 국어를 수업합니다.");
    }
}
 
//수학 선생님
class Math_Teacher extends Teacher{

    @Override
    public void teach() {
        System.out.println("선생님이 수학을 수업합니다.");
    }
}

//영어 선생님
class English_Teacher extends Teacher{

    @Override
    public void teach() {
        System.out.println("선생님이 영어를 수업합니다.");
    }
}

public class Main {
    public static void main(String[] args) {
        Korean_Teacher kr = new Korean_Teacher(); //국어
        Math_Teacher mt = new Math_Teacher(); //수학
        English_Teacher en = new English_Teacher(); //영어
        
        kr.start_class();
        System.out.println("----------------------------");
        mt.start_class();
        System.out.println("----------------------------");
        en.start_class();
    }
}

장점단점
중복코드를 줄일 수 있다.추상 메소드가 많아지면서 클래스 관리가 복잡해진다.
자식 클래스의 역할을 줄여 핵심 로직의 관리가 용이하다.클래스간의 관계와 코드가 꼬여버릴 염려가 있다.
코드를 객체지향적으로 구성할 수 있다.

📒 표현하라

소프트웨어의 대다수는 장기적인 유지보수에 들어간다. 적은 결함과 유지보수 비용을 위해서는 코드를 명백하게 짜야한다. 그렇기 위해서는 유지보수 개발자가 코드를 쉽게 이해할 수 있도록 의도를 명확하게 표현해야 한다.

이해하기 쉬운 코드를 짜는 방법

  1. 좋은 이름을 선택한다.
    • 이름과 기능을 관련있게 짓는다.
  2. 함수와 클래스 크기를 줄인다.
    • 이름 짓기가 쉬워지고, 구현도 쉽고, 이해도 쉬워진다.
  3. 표준 명칭을 사용한다.
    • ex) 표준 패턴을 사용할 경우 그 이름을 넣어주면 의도를 알기 쉬워진다.
  4. 단위 테스트 케이스를 꼼꼼하게 작성한다.
    • 잘 만든 테스트 케이스로 클래스의 기능을 이해 가능하다.
* 표준 패턴 ?? :

📕 클래스와 메서드 수를 최소로 줄여라

간혹 클래스와 메서드의 크기를 줄이기 위해 조그만 클래스와 메서드를 수없이 만드는 경우가 생긴다. 따라서 함수와 클래스 수를 가능한 줄여야 한다.

여기서의 목표는 함수와 클래스 크기를 작게 유지하면서 동시에 시스템 크기도 작게 유지하는 데 있다.

이 규칙은 간단한 설계 규칙 네 개 중 우선순위가 가장 낮다. 테스트 케이스를 만들고, 중복을 제거하고 의도를 표현하는 다른 작업이 더 중요하다.


📚 Reference

좋은 웹페이지 즐겨찾기