한 번만 사용되는 경우 문자열 상수를 정의해야합니까? 총 1000

XPath를 사용하여 응용 프로그램의 데이터 모델에 액세스 할 수있는 Jaxen (Java 용 XPath 라이브러리) 용 어댑터를 구현하고 있습니다.

이것은 문자열을 Jaxen에서 우리에게 전달한 클래스를 데이터 모델의 요소로 매핑하는 클래스를 구현하여 수행됩니다. 총 1000 개의 문자열 비교를 통해 약 100 개의 클래스가 필요하다고 추정합니다.

이 작업을 수행하는 가장 좋은 방법은 각 문자열을 상수로 정의하는 것이 아니라 코드에 직접 작성된 문자열을 가진 간단한 if / else 문이라고 생각합니다. 예를 들면 다음과 같습니다.

public Object getNode(String name) {
    if ("name".equals(name)) {
        return contact.getFullName();
    } else if ("title".equals(name)) {
        return contact.getTitle();
    } else if ("first_name".equals(name)) {
        return contact.getFirstName();
    } else if ("last_name".equals(name)) {
        return contact.getLastName();
    ...

그러나 나는 항상 문자열 값을 코드에 직접 포함시키지 말고 대신 문자열 상수를 만들어야한다고 배웠습니다. 다음과 같이 보일 것입니다.

private static final String NAME = "name";
private static final String TITLE = "title";
private static final String FIRST_NAME = "first_name";
private static final String LAST_NAME = "last_name";

public Object getNode(String name) {
    if (NAME.equals(name)) {
        return contact.getFullName();
    } else if (TITLE.equals(name)) {
        return contact.getTitle();
    } else if (FIRST_NAME.equals(name)) {
        return contact.getFirstName();
    } else if (LAST_NAME.equals(name)) {
        return contact.getLastName();
    ...

이 경우 나는 그것이 나쁜 생각이라고 생각합니다. 이 방법에서는 상수가 한 번만 사용됩니다 getNode(). 상수를 사용하는 것만 큼 문자열을 직접 사용하는 것은 읽기 쉽고 이해하기 쉽고 최소한 1000 줄의 코드를 작성하는 것을 막아줍니다.

따라서 일회용으로 문자열 상수를 정의 해야하는 이유가 있습니까? 아니면 문자열을 직접 사용하는 것이 허용됩니까?


추신. 누군가 열거 형 대신을 제안하기 전에 프로토 타입을 프로토 타입으로 만들었지 만 열거 형 변환은 간단한 문자열 비교보다 15 배 느리므로 고려하지 않습니다.


결론 :
아래 답변은 문자열 상수 이상 으로이 질문의 범위를 확장 했으므로 두 가지 결론이 있습니다.

  • 그것은,이 시나리오에서 문자열을 직접이 아닌 문자열 상수를 사용하는 것이 아마 OK입니다
  • 문자열을 사용하지 않는 방법이 더 좋을 수도 있습니다.

따라서 문자열을 완전히 피하는 래퍼 기술을 시도해 보겠습니다. 불행히도 우리는 아직 Java 7을 사용하지 않기 때문에 string switch 문을 사용할 수 없습니다. 궁극적으로 우리에게 가장 좋은 대답은 각 기술을 시도하고 그 성능을 평가하는 것입니다. 실제로 하나의 기술이 더 빠르면 아름다움이나 컨벤션 준수 여부에 관계없이 기술을 선택할 것입니다.



답변

이 시도. 초기 반향은 확실히 비싸지 만, 여러 번 사용하려고한다면, 아마도 내가 생각하는 것, 이것은 당신이 제안하는 것보다 가장 좋은 해결책입니다. 나는 리플렉션을 사용하는 것을 좋아하지 않지만 리플렉션에 대한 대안이 마음에 들지 않을 때 그것을 사용한다는 것을 알게되었습니다. 이것이 팀의 많은 두통을 덜어 줄 것이라고 생각하지만 메소드 이름을 소문자로 전달해야합니다.

즉, get 메소드의 이름이 “getFullName ()”이므로 “name”을 전달하는 대신 “fullname”을 전달합니다.

Map<String, Method> methodMapping = null;

public Object getNode(String name) {
    Map<String, Method> methods = getMethodMapping(contact.getClass());
    return methods.get(name).invoke(contact);
}

public Map<String, Method> getMethodMapping(Class<?> contact) {
    if(methodMapping == null) {
        Map<String, Method> mapping = new HashMap<String, Method>();
        Method[] methods = contact.getDeclaredMethods();
        for(Method method : methods) {
            if(method.getParameterTypes().length() == 0) {
                if(method.getName().startsWith("get")) {
                    mapping.put(method.getName().substring(3).toLower(), method);
                } else if (method.getName().startsWith("is"))) {
                    mapping.put(method.getName().substring(2).toLower(), method);
                }
            }
        }
        methodMapping = mapping;
    }
    return methodMapping;
}

컨택 구성원에 포함 된 데이터에 액세스해야하는 경우 필요한 정보에 액세스하기위한 모든 메소드가있는 컨택에 대한 랩퍼 클래스 작성을 고려할 수 있습니다. 이것은 또한 액세스 필드의 이름이 항상 동일하게 유지되도록 보장하는 데 유용합니다 (랩퍼 클래스에 getFullName ()이 있고 전체 이름으로 호출하는 경우 연락처의 getFullName ()의 이름이 바뀌어도 항상 작동 함) 그렇게하기 전에 컴파일 오류가 발생합니다).

public class ContactWrapper {
    private Contact contact;

    public ContactWrapper(Contact contact) {
        this.contact = contact;
    }

    public String getFullName() {
        return contact.getFullName();
    }
    ...
}

이 솔루션은 jsf 데이터 테이블에서 사용할 단일 데이터 표현을 원하거나 jasper를 사용하여 보고서로 데이터를 내 보내야 할 때 (내 경험에서 복잡한 객체 접근자를 잘 처리하지 못함) 여러 번 저장했습니다. .


답변

가능하면 Java 7을 사용하면 switch명령문 에서 문자열을 사용할 수 있습니다 .

에서 http://docs.oracle.com/javase/tutorial/java/nutsandbolts/switch.html

public class StringSwitchDemo {

    public static int getMonthNumber(String month) {

        int monthNumber = 0;

        if (month == null) {
            return monthNumber;
        }

        switch (month.toLowerCase()) {
            case "january":
                monthNumber = 1;
                break;
            case "february":
                monthNumber = 2;
                break;
            case "march":
                monthNumber = 3;
                break;
            case "april":
                monthNumber = 4;
                break;
            case "may":
                monthNumber = 5;
                break;
            case "june":
                monthNumber = 6;
                break;
            case "july":
                monthNumber = 7;
                break;
            case "august":
                monthNumber = 8;
                break;
            case "september":
                monthNumber = 9;
                break;
            case "october":
                monthNumber = 10;
                break;
            case "november":
                monthNumber = 11;
                break;
            case "december":
                monthNumber = 12;
                break;
            default:
                monthNumber = 0;
                break;
        }

        return monthNumber;
    }

    public static void main(String[] args) {

        String month = "August";

        int returnedMonthNumber =
            StringSwitchDemo.getMonthNumber(month);

        if (returnedMonthNumber == 0) {
            System.out.println("Invalid month");
        } else {
            System.out.println(returnedMonthNumber);
        }
    }
}

나는 측정하지는 않았지만 스위치 문은 긴 비교 목록 대신 점프 테이블로 컴파일된다고 생각합니다. 이것은 더 빨라야합니다.

실제 질문과 관련하여 : 한 번만 사용 하면 상수로 만들 필요가 없습니다. 그러나 상수는 문서화 되어 Javadoc에 표시 될 수 있습니다 . 사소하지 않은 문자열 값에 중요 할 수 있습니다.


답변

이것을 유지하려면 (사소한 종류의 변경은하지 마십시오 ) 실제로 주석 기반 코드 생성 ( CGLib 를 통해 )을 사용하거나 모든 코드를 작성하는 스크립트를 사용하는 것이 좋습니다. 고려중인 접근 방식으로 발생할 수있는 오타 및 오류 수를 상상해보십시오.


답변

나는 여전히 클래스 상단에 정의 된 상수를 사용합니다. 나중에 변경 될 수있는 내용 (필요한 경우)을 더 쉽게 볼 수 있으므로 코드 유지 관리가 더 쉽습니다. 예를 들어, "first_name"될 수있는 "firstName"몇 가지 나중에.


답변

네이밍이 일관된 경우 (일명 "some_whatever"항상에 매핑 됨 getSomeWhatever()) 리플렉션을 사용하여 get 메소드를 결정하고 실행할 수 있습니다.


답변

주석이 없어도 주석 처리가 해결책이 될 수 있습니다. 그것은 당신을 위해 모든 지루한 코드를 생성 할 수있는 것입니다. 단점은 N 개의 모델 클래스에 대해 N 개의 생성 된 클래스를 얻게된다는 것입니다. 기존 클래스에는 아무것도 추가 할 수 없지만 다음과 같은 내용을 작성하십시오.

public Object getNode(String name) {
    return SomeModelClassHelper.getNode(this, name);
}

수업 당 한 번 문제가되지 않아야합니다. 또는 다음과 같이 쓸 수 있습니다.

public Object getNode(String name) {
    return getHelper(getClass()).getNode(this, name);
}

공통 슈퍼 클래스에서.


코드 생성을 위해 주석 처리 대신 리플렉션을 사용할 수 있습니다. 단점은 리플렉션을 사용하기 전에 컴파일하기 위해 코드가 필요하다는 것입니다. 이는 일부 스텁을 생성하지 않으면 모델 클래스에서 생성 된 코드에 의존 할 수 없음을 의미합니다.


나는 또한 리플렉션의 직접적인 사용을 고려할 것입니다. 물론, 반사는 느리지 만 왜 느립니까? 필드 이름을 켜는 등 필요한 모든 작업을 수행해야하기 때문입니다.


답변