정적 메소드를 남용합니까?


13

몇 달 전에 나는 새로운 프로젝트에서 일하기 시작했으며 코드를 통과 할 때 사용되는 정적 메소드의 양을 획기적으로 나타냅니다. 와 같은 유틸리티 메소드 collectionToCsvString(Collection<E> elements)뿐만 아니라 많은 비즈니스 로직이 유지됩니다.

내가이 이론적 근거에 책임이있는 사람에게 물었을 때, 그는 그것이 봄의 폭정에서 벗어나는 방법이라고 말했다 . 이 사고 과정과 관련이 있습니다. 고객 영수증 생성 방법을 구현하기 위해 서비스를 가질 수 있습니다.

@Service
public class CustomerReceiptCreationService {

    public CustomerReceipt createReceipt(Object... args) {
        CustomerReceipt receipt = new CustomerReceipt();
        // creation logic
        return receipt;
    }
}

이제 그는 기본적으로 클라이언트 클래스가 Spring Bean 자체이어야한다는 제한을 부과하기 때문에 Spring이 불필요하게 클래스를 관리하는 것을 싫어한다고 말했다. 우리는 Spring에 의해 모든 것을 관리하게되는데, 결국 절차없는 방식으로 stateless 객체로 작업하게됩니다. https://www.javacodegeeks.com/2011/02/domain-driven-design-spring-aspectj.html에 명시된 내용

위 코드 대신에 그는

public class CustomerReceiptCreator {

    public static CustomerReceipt createReceipt(Object... args) {
        CustomerReceipt receipt = new CustomerReceipt();
        // creation logic
        return receipt;
    }
}

가능한 경우 Spring이 클래스를 관리하는 것을 피할 수 있다고 주장 할 수는 있지만 모든 것이 정적 인 것의 이점은 보이지 않습니다. 이러한 정적 메소드는 상태 비 저장이므로 OO도 아닙니다. 나는 무언가에 더 편안하게 느낄 것입니다

new CustomerReceiptCreator().createReceipt()

그는 정적 메소드에는 추가적인 이점이 있다고 주장합니다. 즉:

  • 더 쉽게 읽을 수 있습니다. 정적 메서드를 가져 오면 클래스가 수행하는 작업이 아니라 작업 만 신경 써야합니다.
  • 분명히 DB 호출이없는 메소드이므로 성능면에서 저렴합니다. 잠재적 인 고객이 코드에 들어가서 확인해야하도록 명확하게하는 것이 좋습니다.
  • 테스트를보다 쉽게 ​​작성할 수 있습니다.

그러나 나는 이것에 완전히 맞지 않는 것이 있다고 생각하므로 이에 대한 노련한 개발자의 생각을 듣고 싶습니다.

제 질문은 이런 프로그래밍 방식의 잠재적 인 함정은 무엇입니까?




4
static위에서 설명되는 방법은 그냥 평범한 공장 방법이다. 팩토리 메소드를 정적으로 만드는 것은 여러 가지 강력한 이유로 일반적으로 수용되는 규칙입니다. 공장 방법이 적절한 지 여부는 다른 문제입니다.
Robert Harvey

답변:


23

차이 무엇 new CustomerReceiptCreator().createReceipt()CustomerReceiptCreator.createReceipt()? 거의 없습니다. 유일한 중요한 차이점은 첫 번째 경우에는 훨씬 더 어색한 구문이 있다는 것입니다. 정적 메서드를 피하는 것이 코드를 더 좋게 만든다는 생각에서 첫 번째를 따르면, 당신은 심각하게 착각합니다. 단일 메소드를 호출하기 위해 오브젝트를 작성하는 것은 둔한 구문에 의한 정적 메소드입니다.

사물이 달라지는 부분은 주입하는 CustomerReceiptCreator대신 주사 할 때 new입니다. 예를 보자.

class OrderProcessor {
    @Inject CustomerReceiptCreator customerReceiptCreator;

    void processOrder(Order order) {
        ...
        CustomerReceipt receipt = customerReceiptCreator.createReceipt(order);
        ...
    }
}

이것을 정적 메소드 버전과 비교해 봅시다 :

void processOrder(Order order) {
    ...
    CustomerReceipt receipt = CustomerReceiptCreator.createReceipt(order);
    ...
}

정적 버전의 장점은 다른 시스템과 어떻게 상호 작용하는지 쉽게 알 수 있다는 것입니다. 그렇지 않습니다. 전역 변수를 사용하지 않은 경우 나머지 시스템이 변경되지 않았다는 것을 알고 있습니다. 나는 시스템의 다른 부분이 여기 영수증에 영향을 미치지 않을 것이라는 것을 알고 있습니다. 불변의 객체를 사용한 경우 순서가 변경되지 않았으며 createReceipt가 순수한 함수라는 것을 알고 있습니다. 이 경우 다른 곳에서는 예측할 수없는 임의의 효과에 대해 걱정하지 않고이 호출을 자유롭게 이동 / 제거 / 등화 할 수 있습니다.

을 주입하면 동일한 보장을 할 수 없습니다 CustomerReceiptCreator. 호출에 의해 변경되는 내부 상태가있을 수 있으며 다른 상태의 영향을 받거나 변경 될 수 있습니다. 순서를 변경하면 놀라운 버그가 발생하도록 내 함수의 문 사이에 예기치 않은 관계가있을 수 있습니다.

반면에 CustomerReceiptCreator갑자기 새로운 의존성이 필요할 경우 어떻게됩니까 ? 기능 플래그를 확인해야한다고 가정 해 봅시다. 주사하는 경우 다음과 같은 일을 할 수 있습니다.

public class CustomerReceiptCreator {
    @Injected FeatureFlags featureFlags;

    public CustomerReceipt createReceipt(Order order) {
        CustomerReceipt receipt = new CustomerReceipt();
        // creation logic
        if (featureFlags.isFlagSet(Flags::FOOBAR)) {
           ...
        }
        return receipt;
    }
}

그런 다음 호출 코드에가 주입되어 CustomerReceiptCreator자동으로가 주입 되기 때문에 완료 됩니다 FeatureFlags.

정적 방법을 사용하면 어떻게 되나요?

public class CustomerReceiptCreator {
    public static CustomerReceipt createReceipt(Order order, FeatureFlags featureFlags) {
        CustomerReceipt receipt = new CustomerReceipt();
        // creation logic
        if (featureFlags.isFlagSet(Flags::FOOBAR)) {
           ...
        }
        return receipt;
    }
}

하지만 기다려! 호출 코드도 업데이트해야합니다.

void processOrder(Order order) {
    ...
    CustomerReceipt receipt = CustomerReceiptCreator.createReceipt(order, featureFlags);
    ...
}

물론 이것은 여전히 ​​어디에서 processOrder오는가에 대한 의문을 남깁니다 FeatureFlags. 운이 좋으면 트레일이 끝나고 그렇지 않으면 FeatureFlags를 통과해야 할 필요성이 스택 위로 더 높아집니다.

여기에 절충점이 있습니다. 종속성을 명시 적으로 전달해야하는 정적 메소드로 더 많은 작업이 발생합니다. 주입 된 방법은 작업을 줄이지 만 종속성을 암시 적으로 만들어 숨기고 코드를 추론하기 어렵게 만듭니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.