새소식

독서

사례를 통해 배우는 SRP

  • -

목표

  • 한개의 클래스로 문제를 구현해보고, 프로젝트를 진행하면서 바뀌는 요구사항이나 유지보수에 대응하며 기존 구조의 한계가 무엇인지 확인한다.

이를 통하여 SRP(단일책임원칙)을 배운다.

요구사항

30-01-2017,-100,asd
30-01-2017,500,dsa
...

KISS(Keep it short and simple)원칙을 이용해 한개의 클래스로 구현하자.

내용

// 입출금 내역 합계 계산
package com.studyjava.chapter2;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

public class BankTransactionAnalyzer {
    private static final String RESOURCES = "/src/com.studyjava/chapter2/";

    public static void main(String[] args) throws IOException {
        final Path path = Paths.get(RESOURCES + args[0]);
        final List<String> lines = Files.readAllLines(path);

        double total = 0d;

        for(final String line : lines){
            final String[] columns = line.split(",");
            final double amount = Double.parseDouble(columns[1]);
            total += amount;
        }

        System.out.println("The total for all transaction is " + total);
    }
}

컴마로 파싱하여 금액을 추출하고 금액을 Double로 형변환하여 전체 합계를 구하는 코드다.

만약 실제로 제품이 출시되었을때 아래와 같이 발생할만한 문제가 생긴다면 어떻게 처리할지 고민해야한다.

  • 파일이 비어있다면?
  • 데이터에 문제가 있어서 금액을 파싱하지 못한다면?
  • 행의 데이터가 완벽하지 않다면?

항상 발생할 만한 문제를 미리 생각해 염두하고 질문하는 습관을 들이자.

// 특정(1월) 달엔 몇건의 입출금 내역이 발생하는가?
// 복붙을 통해 생성
   public static void main(String[] args) throws IOException {
        final Path path = Paths.get(RESOURCES + args[0]);
        final List<String> lines = Files.readAllLines(path);
        double total = 0d;
        final DateTimeFormatter DATE_PATTERN = DateTimeFormatter.ofPattern("dd-MM-yyyy");

        for(final String line : lines){
            final String[] columns = line.split(",");
            final LocalDate date = LocalDate.parse(columns[0], DATE_PATTERN);
            if(date.getMonth() == Month.JANUARY) {
                final double amount = Double.parseDouble(columns[1]);
                total += amount;
            }
        }

        System.out.println("The total for all transaction is " + total);
    }

final 변수

지역변수나 필드를 final로 정의하기 때문에 이 변수에 값을 재할당할 수 없다.
final 사용에 따른 장단점이 모두 있으므로 사용여부는 팀과 프로젝트에 따라 달라진다.
어떤 객체의 상태가 바뀔수 있는지 없는지 명확하게 구분할수 있다.

하지만 final 키워드를 적용한다고 해서 객체가 바뀌지 못하도록 강요하는것은 아니다.
final 필드로 가리키는 객체라도 가변상태(mutable state)를 포함하기 때문이다.
메서드 파라미터에 final 필드를 포함시켜 이들 변수가 지역변수도 아니며, 다시 할당 할수 없음을 명시 하기도 한다.

추상메서드(ex)인터페이스 내)의 메서드 파라미터에 final을 사용하는 상황은 아주 final 키워드가 쓸모없는 상황이 된다. 실제 구현이 없으므로 final 키워드의 의미가 무력화가 되는것이다.

자바 10에서 var 키워드가 등장하면서 final의 유용성이 크게 감소되었다.

코드 유지보수성과 안티패턴

2번째 코드는 복사 붙여넣기로 생성했다. 코드를 구현할 때는 코드유지보수성을 높이기 위해 노력해야한다.
구현하는 코드가 가졌으면 하는 속성을 목록으로 만들어보자.

  • 특정 기능을 담당하는 코드는 쉽게 찾을수 있어야한다.
  • 코드가 어떤 일을 수행하는지 쉽게 이해할 수 있어야 한다.
  • 새로운 기능을 쉽게 추가하거나 기존 기능을 제거해야 한다.
  • 캡슐화가 잘되어야한다. 즉 코드 사용자에게는 세부 구현 내용이 감춰져 있으므로 사용자가 쉽게 코드를 이해하고 기능을 바꿀수 있어야 한다.

궁극적인 개발자의 목표는 애플리케이션의 복잡성을 관리하는것이다.
항상 팀장님께서 하셨던 말씀이 바로 생각났다.

너가 이직을하고 다른사람이 이 코드를 봤을때, 이해하는데 있어서 불편하지 않은게 바로 좋은코드라고

하지만 새로운 요구사항이 생길때마다 복사 붙여넣기로 이를 해결한다면 그게 바로 안티패턴이 되는거다

  • 한개의 거대한 갓클래스 때문에 코드를 이해하기 어렵다.
  • 코드 중복 때문에 코드가 불안정하고 변화에 쉽게 망가진다.

두 개의 안티패턴에 대해 알아보자

갓클래스(GodClass)

한개의 파일에 모든 코드를 구현하다보면 결국 하나의 거대한 클래스가 탄생하면서 클래스의 목적을 이해하기 어려워진다. 이 거대한 클래스가 모든 일을 수행하기 때문이다.

기존 코드의 로직을 갱신해야 한다면 어떻게 이 코드를 찾아서 바꿀수 있을까? 이런 문제를 갓 클래스 안티 패턴 이라고 부른다. 한 클래스로 모든것을 해결하는 패턴이다. 이에 도움을 주는 원칙으로 SRP(단일책임원칙) 을 배워야한다.

코드 중복

위 두 코드에선 입력을 읽고 파싱하는 로직이 중복된다. 만약 CSV대신 JSON으로 바뀐다면? 또 다른 여러 형식의 파일을 파싱해야한다면? 복사 붙여넣기로 해결하다보면 계속해서 여러곳에 코드가 중복되고 기존의 기능을 바꾸기 어렵게된다. 결과적으로 모든 곳의 코드를 다 바꿔야 하며, 새로운 버그가 발생할 가능성이 커지게 되는것이다.

DRY(Dont Repeat Yourself)원칙
반복을 제거하면 로직을 바꿔도 여러곳의 코드를 바꿔야 할 필요성이 없어진다.

위 코드에서 요구사항이 새로 생긴다면 새로운열을 추가해 이를 개선하거나 코드에서 많은 부분을 바꿔야한다.

결론적으로 코드를 간결하게 유지하는것도 좋지만, KISS 원칙을 남용하지 말아야한다. 애플리케이션의 설계를 되돌아보고, 문제를 작게작게 분리해 더 쉽게 관리 할 수 있는지 파악해야 할 필요가 있다.

단일 책임 원칙

단일책임원칙은 쉽게 관리하고 유지보수하는 코드를 구현하는데 도움을 주는 소프트웨어 개발 지침이다.

다음 두가지를 보완하기 위해 SRP를 적용한다.

  • 한 클래스는 한 기능만 책임진다.
  • 클래스가 바뀌어야 하는 이유는 오직 하나여야 한다.

SRP는 일반적으로 클래스와 메서드에 적용한다. SRP는 한가지 특정 동작, 개념, 카테고리와 관련된다. 이를 적용하면 코드가 바뀌어야 하는 이유가 한가지로 제한되므로 더 견고한 코드를 만들 수 있다.

위 코드 에서는 이를 개별로 분리해야한다.

  • 입력 읽기
  • 주어진 형식의 입력 파싱
  • 결과 처리
  • 결과 요약 리포트

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

Clean Code 제 5장. 형식 맞추기  (0) 2022.03.01
Clean Code 제 4장. 주석  (0) 2022.02.25
Clean Code 제 3장. 함수  (0) 2022.02.23
Clean Code 제 2장. 의미 있는 이름  (0) 2022.02.20
Clean Code 제 1장. 깨끗한 코드  (0) 2022.02.19
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.