4

[갭을 염두에 두십시오 : 최선의 해결책은 열거 형을 완전히 없애는 것이지만 그 것은 코멘트에 언급 된 바와 같이 오늘은 선택 사항이 아니라는 것을 알고 있지만 (먼 미래) 계획되어 있습니다.]Java 열거 형 값에 대한 종속성을 제거하는 방법은 무엇입니까?

두 가지 배포 단위 인 frontend와 backend가 있습니다. 프론트 엔드는 enum을 사용하고 enum을 매개 변수로 사용하여 백엔드에서 EJB 서비스를 호출합니다. 그러나 enum은 자주 변경되므로 백엔드가 그 값을 알기를 원하지 않습니다.

문자열 열거하는 것은 아니고 문자열 상수를 사용하는 것입니다

가능한 솔루션을 상수,하지만 프론트 엔드에서 작은 많은 변화를 일으킬 것입니다. 나는 프론트 엔드에서 가능한 한 적은 변화를 일으키는 해결책을 찾고있다.

래퍼 클래스

다른 해결책은 열거 같은 인터페이스 래퍼 클래스의 사용이다. 열거 형은 래퍼 클래스가되고 열거 형 값은 해당 래퍼 내의 상수가됩니다. 객체 식별 (enum이하는 것처럼)을 보장하기 위해 일부 비 직렬화 코드를 작성해야했지만 올바른 해결책인지 여부는 알 수 없습니다. 다른 클래스 로더가 사용된다면 어떨까요? 래퍼 클래스는 백엔드의 열거 형을 대체 할 Java 인터페이스를 구현합니다. 하지만 deserialiaztion 코드가 백엔드에서도 실행됩니까? 래퍼 클래스에 대한

예 :

public class Locomotion implements Serializable { 
    private static final long serialVersionUID = -6359307469030924650L; 

    public static final List<Locomotion> list = new ArrayList<Locomotion>(); 

    public static final Locomotion CAR = createValue(4654L); 
    public static final Locomotion CYCLE = createValue(34235656L); 
    public static final Locomotion FEET = createValue(87687L); 

    public static final Locomotion createValue(long type) { 
     Locomotion enumValue = new Locomotion(type); 
     list.add(enumValue); 
     return enumValue; 
    } 

    private final long ppId; 

    private Locomotion(long type) { 
     this.ppId = type; 
    } 

    private Object readResolve() throws ObjectStreamException { 
     for (Locomotion enumValue : list) { 
      if (this.equals(enumValue)) { 
       return enumValue; 
      } 
     } 
     throw new InvalidObjectException("Unknown enum value '" + ppId + "'"); 
    } 

    @Override 
    public int hashCode() { 
     final int prime = 31; 
     int result = 1; 
     result = prime * result + (int) (ppId^(ppId >>> 32)); 
     return result; 
    } 

    @Override 
    public boolean equals(Object obj) { 
     if (this == obj) { 
      return true; 
     } 
     if (obj == null) { 
      return false; 
     } 
     if (!(obj instanceof Locomotion)) { 
      return false; 
     } 
     Locomotion other = (Locomotion) obj; 
     if (ppId != other.ppId) { 
      return false; 
     } 
     return true; 
    } 
} 

이미 같은 문제가 있었나요? 어떻게 그걸 해결 했니?

답변

2

내가 알면 알았지. 당신은

는 "프론트 엔드는 열거를 사용하고 매개 변수로 열거와 백엔드에서 EJB 서비스를 호출합니다. 그러나 열거 변화가 자주, 우리가 알고 백엔드를 원하지 않는다고 말했다 그 값은 "

당신이"값 "이라고 말할 때 당신은 열거 형 생성자에서 전달한 수치 값을 말하는 것으로 간주하고 열거 형 상수 자체는 아닙니다.

따라서 프론트 엔드와 백엔드는 서로 다른 두 가지 버전의 enum 클래스를 갖지만 열거 형 상수는 동일합니다.

나는 통신이 RMI를 통해서만 이루어지고 있다고 가정하고 있습니다 (그러나 이것은 귀하의 게시물에서 완전히 명확하지 않습니다).

이제 열거 형의 serialization/deserialization이 다른 개체와 다르게 작동합니다. Java 직렬화 스펙에 의하면, enum가 직렬화되면 (자), 그 이름 만이 직렬화됩니다. 또한 deserialize 될 때 Enum.valueOf (name) 메서드를 사용하여 빌드됩니다.

그래서 Enums의 직렬화로 인해 서버가 클라이언트의 열거 형의 실제 값을 알 수 없으므로 원래의 래퍼 제안은 작동하지 않습니다.

결론 서버에 열거 형을 전달하려는 경우 직렬화가 암시 된 경우 프론트 엔드의 값이 백엔드에 도달하지 않으므로 수행하려는 작업을 수행 할 수 없습니다.

RMI가 암시 된 경우 좋은 해결책은 코드 이동성을 사용하는 것입니다.이 방법으로 문제가되는 클래스를 서버, 클라이언트 모두에서 액세스 할 수있는 저장소에 배치 할 수 있으며 프런트 엔드 개발자가 클래스 정의를 변경하면 저장소에 클래스를 게시하면 서버가 거기에서 가져올 수 있습니다.

동적 코드는 RMI에 http://download.oracle.com/javase/6/docs/technotes/guides/rmi/codebase.html

또 다른 가능한 솔루션을 코드베이스 속성을 사용하여 다운로드하는 방법에 대한이 문서를 참조하십시오 우리가에서 할 사용하면, 자바 열거 사용을 중지하고 최종 상수 Java 클래스를 사용할 수 있다는 것입니다 옛날에는 열거 형 (enum)을 사용하기 전에 값을 백엔드로 보낼 때 해당 값이 적절히 직렬화되도록 할 수 있습니다.

다소이

public class Fruit implements Serializable{ 

    private static final long serialVersionUID = 1L; 
    public final Fruit ORANGE = new Fruit("orange"); 
    public final Fruit LEMON = new Fruit("lemon"); 

    private String name; 

    private Fruit(String name){ 
     this.name = name; 
    } 
} 

이 방법처럼, 직렬화 복원에 발생하고 래퍼 패턴이 방법을 작동 할 수 있습니다 무엇을 완벽하게 제어 할 수 있습니다.

이 구성 유형은 열거 형을 완전히 대체 할 수 없습니다. 예를 들어, 스위치 명령문에서 사용할 수 없습니다. 그러나 이것이 문제인 경우이 객체를 서버에 전송 된 매개 변수로 사용하고 서버가 해당 enum 클래스 버전으로 열거 형을 다시 만들도록 할 수 있습니다.

귀하의 열거 따라서, 열거 자체에서 자바 인스턴스를 구축 한 두 가지 새로운 방법을 할 수 :

public static Fruit toFruit(FruitEnum enum); 
public FruitEnum valueOf(Fruit fruit); 

을 그리고 당신은 서버에 앞뒤로 매개 변수의 버전을 변환하는 사람들을 사용할 수 있습니다 .

+0

+1 열거 형 직렬화 논리 및 "스위치 문제". 하지만 서버 측에서 열거 형을 다시 작성하면 새로운 값을 추가 할 때 재배포해야하므로 전체가 무의미 해집니다. –

+1

@Chris 직렬화로 인해 열거 형을 매개 변수로 사용하려는 경우이를 수행 할 방법이 없습니다. 여전히 코드 이동성이 있습니다. 그것이 당신을 위해 그것을 만들지 않는다면 Mandrake만이 :-) –

1

서버가 데이터베이스에 들어가는 값에 대해 알아야한다고 생각하기 때문에 이상한 요청입니다.하지만 확인해 드리겠습니다. 아마 당신은 프론트 엔드와 모두 정렬 화 매개 변수 중에 있기 때문에 가능한 직렬화의 같은 클래스 버전을 사용할 필요가 백엔드 사이의 데이터 전송을 위해이

public enum Giant {Fee, Fi, Fo, Fum}; 

public void client() { 
    Giant giant = Giant.Fee; 
    server(giant); 
} 

public void server(Enum e) { 
    String valueForDB = e.name(); 
     //or perhaps 
    String valueForDB = e.toString(); 
} 
+0

서버는 데이터베이스에서 관련 정보를 얻기 위해 열거 형을 ID로 사용하기 때문에 허용되는 값을 알 필요가 없습니다. 그게 훨씬 더 미칠 수도 있습니다. 그러나 그것은 역사적인 부서입니다. 우리는 쉽게 제거 할 수 없습니다. (나는 데이터베이스에 저장된 ID에 대한 열거 형을 만들지 않을 것이다 ...) –

+0

확실히 작동하지만 프론트 엔드 녀석은 강력하게 형식화 된 인터페이스를 고집합니다. 이 인터페이스로 모든 열거 형을 전달할 수 있습니다. 그러나 나는 그 접근 방법을 논의 할 것이다. –

+0

프론트 엔드와 EJB 백엔드 사이의 매개 변수를 정렬하려면 직렬화가 필요할 수 있습니다. 그것의 버전 *! 따라서 프렌드 엔드가 사용하는 정확한 enum Giant를 모른다면 서버는 e.name/e.toString을 얻을 수 없으므로이 hier는 해결책이 아닙니다. –

1

을 할 수 있습니다. 그래서 다시 똑같은 열거 형이나 다른 클래스를 사용하려고 노력해야합니다. 열거 형을 다른 것으로 전환해도 작동하지 않습니다. 두 가지 모두에 대해 알려진 클래스 identiy를 설정해야합니다.

그래서 서버가 어떤 종류의 처리/매개 변수의 값을 계산할 때 문자열을 사용하거나 기타 변경되지 않는 클래스를 사용하여 결정하면 내부에 값을 넣을 수 있습니다. 문자열, 숫자 배열 또는 도대체 무엇이.

그래서 데이터베이스 ID를 랩퍼 오브젝트에 넣으면 서버는 오브젝트를 데이터베이스에서 가져올 수 있습니다. 하지만 여전히 - 클래스 패스에서 동일한 버전의 래퍼 클래스가 필요합니다.

+0

디자인상의 결함이 있음을 알고 있습니다. 리팩토링하면 더 큰 노력이 필요합니다. 따라서 여러분의 주장은 해결책이 아닌 문제의 일부입니다. –

+0

나는 이것을 설계 결함으로 바로 편집했다. - 그렇긴하지만 질문의 일부는 아니었다. 그러나 나는 당신이 동의했기 때문에 기쁩니다 :-) –

-1

좋아, 내가 너무 귀하의 코드를 볼 수 없기 때문에 내 경험에 변화가 뭔가 외부 데이터가 아닌 열거 형이어야합니다.

내가 거의 항상 발견하는 것은 열거 형에있는 정보를 외부화하면 몇 가지 다른 부분도 외부화해야한다는 것입니다. 그러나 모두 수행 한 후에는 많은 코드를 분해해야합니다.

실제로 열거 형 값을 사용할 때마다 거의 중복 코드가 작성됩니다.이제

switch(card.suit) 
case Suit.HEARTS: 
    load_graphic(Suit.HEARTS); 
    // or better yet: 
    Suit.HEARTS.loadGraphic(); 
    break; 
case Suit.SPADES: 
    Suit.SPADES.loadGraphic(); 

... 

: 내 말은 당신이 "하트", "다이아몬드"와 같은 열거 형을 ...있는 경우

그들은 당신의 코드에서 사용할 수있는 유일한 방법은 switch 문처럼 뭔가 있다는 것이다 , 이것은 분명히 어리석은 짓이다.하지만 나는 코드에서 값을 사용했다고 말하기 위해 어리석은 제약을했다.

card.suit.loadGraphic(); 

와우, 모두 사라 :의 코드의 값을 사용하지 말자 볼 - 내 주장은 당신이 열거를 필요로하지 않는 값을 사용하지 않는 경우이다. 하지만 갑자기 열거 형을 사용하는 전체 지점이 사라졌습니다. 대신에 "Heart.png"및 "Spade.png"와 같은 문자열이 포함 된 텍스트 파일에서 4 개의 인스턴스가있는 "Suit"factory를 전체 클래스에서 미리 제거하십시오.

거의 열거 형을 사용할 때마다 나는 이것들을 인수 분해하게됩니다.

열거 형을 사용할 수있는 코드가 없다는 것은 아닙니다.하지만 코드를 팩터링하고 데이터를 외부화 할 때 얻을 수있는 이점이 더 낫지 만 실제로 필요하지는 않습니다.

+0

나는 당신과 동의합니다, 그러나 이것은 어떻게 질문과 관련이 있는지 보지 못합니다. 나는 심지어 그것에 대해 생각하고 그것을 할 수 없다는 것을 설명했다. (#String 상수 참조). –

+0

죄송합니다. 리팩토링/래퍼 클래스 솔루션으로 이동 한 다음 코드에서 데이터로 데이터를 추출하려고 시도한 것 같습니다. 또한 열심히 사용하는 것을 고려하고 그런 질문을하는 사람들에게 지금과 같이 다음과 같은 종류의 중요한 사항에 유용합니다. –

관련 문제