250x250
반응형
Recent Posts
Recent Comments
Link
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Archives
Today
Total
관리 메뉴

재 현

객체지향 생활체조 9가지 원칙 본문

Development

객체지향 생활체조 9가지 원칙

본명은이점례 2023. 11. 1. 18:23
728x90

 

소프트웨어 개발은 빠르게 진화하고, 복잡성이 증가함에 따라 좋은 코드를 작성하는 것이 더 중요해졌다.

 

객체지향 생활체조는 이러한 도전에 대한 해법 중 하나로, 더 나은 코드 작성을 돕는 9가지 원칙을 제시한다.

 

이 글에서는 객체지향 생활체조의 각 원칙을 살펴보고, 어떻게 이를 실제 코드 작성에 적용할 수 있는지 작성한다.

 

1. 한 메서드에 오직 한 단계의 들여 쓰기만 한다

 

코드를 읽기 쉽게 만들기 위해 메서드 내에서는 들여 쓰기를 최소화하고, 메서드가 한 가지 일만 수행하도록 유지해야 한다. 이렇게 하면 코드가 단순해지고 이해하기 쉬워진다.

 

// 수정 전

public static int operate(int[] numArray, String op) {
    int result = 0;
    if(op.equuals("+")) {
        for(int i = 0; i < numArray.length; i++) {
            result += numArray[i];
        }
        return result;
    }
    if(op.equals("*")) {
        // ..생략
    }
}

 

기존 코드에선, depth가 많아 이해하기 복잡해진다. 그래서 깔끔하게 정리해줘야 한다.

// 수정 후

public static int operate(int[] numArray, String op) {
    if(op.equals("+")) {
        return sum(numArray);
    }
    // 생략
}

private static int sum(int[] numArray) {
    int result = 0;
    for(int i = 0; i < numArray.length; i++) {
        result += numArray[i];
    }
    return result;
}

 

2. else 예약어를 사용하지 않는다

if-else 블록을 사용할 때는 가급적이면 else 예약어를 피해야 한다. else문은 의사결정문이다. 즉 하드코딩과 다를 게 없어서 우린 최대한 피해야 한다. 대신, 조건문을 분리하고 각 조건에 따라 메서드를 작성하거나 객체를 분리하는 방식으로 코드를 작성한다.

 

// 수정 전

public static int operate(int[] numArray, String op) {
    int result = 0;
    if(op.equals("+")) {
        result = sum(numArray);
    } else if(op.equals("-")) {
        result = minus(numArray);
    } else {
        result = avg(numArray);
    }
    return result;
}

 

 

기존 코드에선 result가 수많은 if문을 돌면서, 조건에 맞으면 result 값을 저장한다. 이는 변수의 흐름을 따라가기 벅차다. 

// 수정 후

public static int operate(int[] numArray, String op) {
    if(op.equals("+")) {
        return sum(numArray);
    } 
    if(op.equals("-")) {
        return minus(numArray);
    }
    return avg(numArray);
}

 

 

3. 모든 원시값과 문자열을 포장한다

원시 자료형을 객체로 포장하면 코드의 가독성과 유지보수성을 향상시킬 수 있다. 예를 들어, 클래스로 분리하여 사용하면 더 많은 기능을 활용할 수 있다.

 

// 수정 전

private static int sum(int[] numArray) {
    int result = 0;
    for(int i = 0; i < numArray.length; i++) {
        int num = numArray[i];
        if(num < 0) {
            throw new RuntimeException();
        }
        result += num;
    }
    return result;
}

 

// 수정 후

public class Positive {
    private int number;

    public Positive(int number) {
        if (number < 0) {
            throw new RuntimeException();
        }
        this.number = number;
    }

    public Poisitive add(Positive other) {
        return new Positive(this.number + other.number);
    }

    public int getNumber() {
        return number;
    }
}

private static Positive[] toPositives(int[] values) {
    Positive[] numbers = new Positive[values.length];
    for (int i = 0; i < values.length; i++) {
        numbers[i] = new Positive(values[i]);
    }
    return numbers;
}

private static int sum(Positive[] numbers) {
    Positive result = new Positive(0);
    for (Positive number : numbers) {
        result = result.add(number);
    }
    return result.getNumber();
}

 

4. 한 줄에 점을 하나만 찍는다

 

객체에 연속적으로 메서드를 호출하는 것은 코드를 이해하기 어렵게 만들 수 있다. 한 줄에는 하나의 동작만을 수행하도록 코드를 작성하는 것이 좋다.

 

// 수정 전

if(user.getMoney().getValue() > 100_000L) {
    throw new IllegalArgumentException("소지금은 100_000원을 초과할 수 없습니다.");
}

 

기존 코드를 보면 money를 가져오고 또 value를 가져온다. 이는 디미터 법칙에 어긋난다. 결합도에 문제가 생기는 코드이다.

// 수정 후

if(user.hasMoney(100_000L)) {
    throw new IllegalArgumentException("소지금은 100_000원을 초과할 수 없습니다.");
}

 

5. 줄여쓰지 않는다

 

의미 있는 변수 이름과 메서드 이름을 사용하여 코드를 작성하고, 줄여 쓰기를 피해야 한다. 이렇게 하면 다른 개발자가 코드를 이해하기 쉽고 유지보수하기 쉬워진다.

// 수정 전

public boolean isAvailable() {
    return this.status == Status.LOCK;
}

 

기존 코드는 너무 추상적이다. 뭐가 가능한지 코드를 가봐야 안다.

// 수정 후

public void validUnlock() {
    if(this.status == Status.LOCK) {
        throw new IllegalArgumentException("잠금 상태에서는 수행할 수 없습니다.");
    }
}

 

6. 모든 엔터티를 작게 유지한다

클래스, 메서드, 모듈 등 모든 엔터티를 작고 단순하게 유지해야 한다. 작은 엔터티는 재사용성과 테스트 용이성을 높여준다.

 

7. 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다

클래스가 많은 인스턴스 변수를 가지면 이해하기 어렵고 복잡해진다. 클래스를 작고 단순하게 유지하고, 필요한 데이터를 캡슐화하여 접근하도록 한다.

 

// 수정 전

public class Car {
    private String brand;
    private String model;
    private int year;
    private String color;
    private int currentSpeed;
    private int maxSpeed;
    private boolean engineStarted;
    private boolean lightsOn;
    private boolean wipersOn;
    private boolean parkingBrakeEngaged;
    // ...
}

 

기존코드에선 Car가 너무 많은 인스턴스 변수를 가지고 있다. 이는 Car 클래스가 너무 많은 역할을 하고 있다는 반증이기도 하며, 유지보수할 때 걸림돌이 된다.

// 수정 후

public class Car {
    private String brand;
    private String model;

    public Car(String brand, String model) {
        this.brand = brand;
        this.model = model;
    }

    public String getBrand() {
        return brand;
    }

    public String getModel() {
        return model;
    }
}

public class Engine {};
public class Light {};
public class Wiper {};
public class Brake {};
// ..

 

8. 일급 콜렉션을 사용한다

여러 개의 변수를 하나의 컬렉션으로 묶어서 사용하면 코드를 단순화하고 가독성을 향상할 수 있다.

public class Store {
    private Set<Brand> brands;

    public Store(List<Brand> brands) {
        validSize(brands);
        this.brands = brands;
    }

    private void validSize(List<Brand> brands) {
        if(brands.size() >= 10) {
            throw new IllegalArgumentException("브랜드는 10개 초과로 입점할 수 없습니다.");
        }
    }
}

 

9. 게터/세터/프로퍼티를 사용하지 않는다

객체의 상태를 직접 수정하는 것보다 메서드를 통해 조작하도록 코드를 작성한다. 이렇게 하면 객체의 상태를 더욱 안전하게 유지할 수 있다.

// 수정 전

ShippingInfo shippingInfo = order.getShippingInfo();

ShippingStatus status = shippingInfo.getStatus();
if(state != OrderState.PATMENT_WATTING && state != OderState.WATTING) {
    throw new IllegalArguementException();
}
shippingInfo.setAddress(newAddress);

 

기존 코드에선, 모든 코드들이 데이터를 가져오기 때문에 수동적일 수밖에 없다. 그렇기 때문에 Tell Don't Ask를 활용해서 객체가 행위를 하도록 한다.

// 수정 후

public Order {
    private ShippingInfo shippingInfo;

    public void changeshippingInfo(ShippingInfo newShippingInfo) {
        verifyNotYetShipped();
        setShippingInfo(newShippingInfo);
    }

    private void setShippingInfo(ShippingInfo newShippingInfo) {
        this.shippingInfo = newShippingInfo;
    }
}

 

 

 

객체지향 생활체조는 더 나은 코드를 작성하고 유지보수하기 위한 유용한 원칙들을 제시하고 있다. 이러한 원칙을 지키면 코드의 가독성과 유지보수성을 향상하며, 더 나은 소프트웨어를 개발할 수 있을 것이다. 나 또한 미친 가독성의 코드를 원칙을 통해 고치면서 많이 개선했다. 결과적으로 코드 품질과 개발 생산성이 향상됨을 경험했다.

 

 

 

참고


728x90