2010-06-03 6 views
1

우선 내가 '상수 개체'라는 용어는 아마도 옳지 않은 것이며 이미 내가 생각한 것과 완전히 다른 것을 의미 할 수도 있지만 그것이 내가 무엇인지 설명하기 위해 생각할 수있는 가장 좋은 용어라고 말할 수 있습니다. 에 대해 말하다.상수 개체를 구현하는 가장 좋은 방법은 무엇입니까?

기본적으로 나는 응용 프로그램을 설계하고 있으며 아마도 기존 설계 패턴이있는 것처럼 보였지만 그 장치가 무엇인지 또는 무엇을 검색해야하는지 모르기 때문에 무엇을 설명 할 것인가? 그것은 노력하고 있으며 그것을 구현하는 가장 좋은 방법에 대한 제안을 찾고 있습니다.

는 클래스가 있다고 가정하자 :

public class MyClass { 
    private String name; 
    private String description; 
    private int value; 

    public MyClass(String name, String description, int value) { 
     this.name = name; 
     this.description = description; 
     this.value = value; 
    } 

    // And I guess some getters and setters here. 
} 

이제 당신은 오직이 클래스의 3 개 인스턴스가 말을 할 것이라는 점을 미리 알고, 데이터도 사전에 알려져 있다고 (또는에서 할 수 있습니다 런타임시 파일에서 최소한 읽을 것이고 정확한 파일 이름은 미리 알려짐). 기본적으로 내가 얻는 것은 런타임 중에 데이터가 변경되지 않는다는 것입니다 (일단 설정되면).

처음에는 정적 정수를 어딘가에 선언해야한다고 생각했습니다. 난 그냥 상수를 직접 참조 할 수있는 3 개 인스턴스 중 하나를 사용할 때마다 지금 Obvisouly

public static final String INSTANCE_1_DATA_FILE = "path/to/instance1/file"; 
public static final String INSTANCE_2_DATA_FILE = "path/to/instance2/file"; 
public static final String INSTANCE_3_DATA_FILE = "path/to/instance3/file"; 
public static final MyClass INSTANCE_1 = new MyClass(getNameFromFile(INSTANCE_1_DATA_FILE), getDescriptionFromFile(INSTANCE_1_DATA_FILE), getValueFromFile(INSTANCE_1_DATA_FILE)); 
public static final MyClass INSTANCE_2 = new MyClass(getNameFromFile(INSTANCE_2_DATA_FILE), getDescriptionFromFile(INSTANCE_2_DATA_FILE), getValueFromFile(INSTANCE_2_DATA_FILE)); 
public static final MyClass INSTANCE_3 = new MyClass(getNameFromFile(INSTANCE_3_DATA_FILE), getDescriptionFromFile(INSTANCE_3_DATA_FILE), getValueFromFile(INSTANCE_3_DATA_FILE)); 

.

하지만이 처리하는 깔끔한 방법과 내가 좋아하는 일을했다 생각 다음 일은있을 수 있다고 생각하기 시작 : 나는 인스턴스를 사용할 때마다 이제

public MyClassInstance1 extends MyClass { 
    private static final String FILE_NAME = "path/to/instance1/file"; 

    public String getName() { 
     if (name == null) { 
      name = getNameFromFile(FILE_NAME); 
     } 
     return name; 
    } 

    // etc. 

}

MyClass를 사용하면 원하는대로 사용할 수 있습니다.

private MyClass myInstance = new MyClassInstance2(); 

또는 아마 더 나은 그들에게 싱글을 만들 수 그냥 할 것 :

private MyClass myInstance = MyClassInstance3.getInstance(); 

하지만 도움이되지만이이 상황을 처리 할 수있는 올바른 방법이 아니라고 생각 할 수 없다. 문제를 너무 과소 평가하고 있습니까? 예를 들어 switch 문을 어딘가에 사용해야합니까?

public class MyClass { 
    public enum Instance { ONE, TWO, THREE } 

    public static String getName(Instance instance) { 
     switch(instance) { 
     case ONE: 
      return getNameFromFile(INSTANCE_1_DATA_FILE); 
      break; 
     case TWO: 
      etc. 
     } 
    } 
} 

아무에게도 이것을 구현하는 가장 좋은 방법을 말할 수 있습니까? Java에서 샘플 코드를 작성했는데 이것이 가장 강한 언어이기 때문에 C++로 응용 프로그램을 구현할 것입니다. 그래서 현재 언어 독립적 인 디자인 패턴을 찾고 있습니다. 이미 언급 한 간단한 해결책 중 하나를 선택하십시오).

+0

수업이 실제로하는 일에 대해 자세히 설명해 주시겠습니까? 전역 변수 노출은 객체 지향적이지 않으며 코드를 변경하기가 매우 어렵습니다 (이러한 변수와 높은 결합이있는 경우). 어쩌면 3 가지 글로벌 인스턴스가 필요한 것보다 나은 방법이있을 수 있습니다. 자바 세계에서는 스프링과 같은 프레임 워크를 사용하여 필요한 곳에 변수를 삽입 할 수 있습니다. C++에는 확실하지 않습니다. –

+0

좋아, 그래서 내 응용 프로그램에 대한 도메인 모델링 오전 '일'잔뜩있을 것입니다. 매일 매일 활동 평가가 있으며 활동 등급은이 세 가지 상수 (기본적으로 낮음, 중간 및 높음)입니다. 응용 프로그램의 UI가 특정 날짜의 활동 등급에 대한 이름과 설명을 표시해야하기 때문에 해당 객체를 객체로 사용해야한다고 생각했습니다. 내부적으로이 값은 계산에 사용됩니다. 그냥 할 수 있다면 좋을 거라 생각 했어 .setActivityRating (MEDIUM); 중간 행위 평가 객체는 모든 행동을 처리 할 것이다. – DaveJohnston

+0

위의 의견에 동의합니다. 나는 이걸 정적으로 만들지는 않겠지 만, DayDAO (!) 인터페이스 뒤에 그 일을 넣어 둡니다. 그런 다음이 인스턴스를 UI에 전달합니다. 그것은 dayDAO.getDay (MEDIUM)을 호출하고 액세스 레이어가이를 처리합니다. 통계학 필요 없음. 그러면 다른 행동을 조롱하고 테스트하는 것이 훨씬 쉬워집니다. – mdma

답변

5

값을 일정하게 유지하려면 설정자가 필요하지 않습니다. 그렇지 않으면 코드가 상수의 값을 간단히 변경할 수 있으므로 매우 일정하지 않게됩니다. C++에서 인스턴스 const을 선언 할 수는 있지만 setter를 제거 할 수는 있습니다. 왜냐하면 누군가 const를 항상 버릴 수도 있기 때문입니다.

패턴이 괜찮은 것처럼 보이지만, 매번 요청할 때마다 새 인스턴스를 작성한다는 사실은 상수에는 일반적이지 않습니다.

자바에서는 "똑똑한"열거 형을 만들 수 있습니다. 예 :C++에서

public enum MyClass { 
    ONE(INSTANCE_1_DATA_FILE), 
    TWO(INSTANCE_2_DATA_FILE), 
    //etc... 

    private MyClass(String dataFile) 
    { 
     this(getNameFromDataFile(dataFile), other values...) 
    } 

    private MyClass(String name, String data, etc...) 
    { 
     this.name = name; 
     // etc.. 
    } 

    public String getName() 
    { 
     return name; 
    } 
} 

, 당신은 초기화 할 필요가 어떤 다른 파일 이름과 소요 개인 생성자와 함께, 귀하의 MyClass의를 만들 것이고, 값이의 새로운 인스턴스를 할당하여, 각 인스턴스에 대해 MyClass의에서 static const 회원을 만들 MyClass는 private 생성자를 사용하여 생성됩니다.

편집 :하지만 지금은 정적 값을 갖는 것이 좋다고 생각하지 않습니다. ActivityLevel 유형이 애플리케이션의 기본 요소 인 경우 다양한 유형의 활동 수준을 상수로 열거 할 수 있습니다 (예 : 자바 또는 문자열 열거 형이지만 그냥 자리 표시 자일뿐입니다. 실제 ActivityDescription 인스턴스는 데이터 액세스 계층 또는 특정 종류의 공급자에서 가져와야합니다.

예컨대

enum ActivityLevel { LOW, MED, HIGH } 

class ActivityDescription 
{ 
    String name; 
    String otherDetails; 
    String description; // etc.. 
    // perhaps also 
    // ActivityLevel activityLevel; 

    // constructor and getters 
    // this is an immutable value object 
} 

interface ActivityDescriptionProvider 
{ 
    ActivityDescription getDescription(ActivityLevel activityLevel); 
} 

당신은, 당신이 파일에서로드 더 나은 여전히 ​​당신이 원하는 경우 정적을 사용하여 공급자 또는 ActivityDescription의 instnaces의 열거, 또는 ActivityDescription에 됨 activityLevel의지도를 구현하는 스프링 설정 등 주요 지점에서 가져올 수 있습니다 주어진 ActivityLevel에 대한 실제 설명을 가져 오는 인터페이스를 사용하면 응용 프로그램 코드가 시스템에서 해당 설명이 생성되는 방식에서 분리됩니다. 또한 UI를 테스트 할 때 인터페이스 구현을 조롱 할 수 있습니다. 고정 된 정적 데이터 세트로는 불가능한 방식으로 모의 구현을 통해 UI에 스트레스를 줄 수 있습니다.

+0

그 점에 대해 감사 드리며 제 질문에 세터를 언급해서는 안됩니다. 하지만 진짜 문제는 내 개체가 일정하게 유지되는 방법을 확인하는 것이 아니라 클래스의 인스턴스가 3 개 밖에 없다는 점을 감안할 때 코드에서 사용하기 위해 이러한 인스턴스를 선언하기 가장 좋은 장소는 무엇입니까? – DaveJohnston

+0

괜찮습니다. 내 업데이트를 참조하십시오. 본질적으로 C++에서 열거 형 또는 정적 상수 멤버를 사용하는 것으로 귀결됩니다.java가 enum을 갖기 전에 비슷한 패턴이 사용되었습니다. static final. (예 : java.awt.Color 참조) – mdma

+0

(내 도메인 모델에 관한 내 의견과 혼동을 일으켰는지 확실하지 않기 때문에) 애플리케이션에 많은 Day 객체가있을 수 있습니다. 각 날 개체는 그날 일어난 일에 관한 많은 데이터를 가지고 있습니다. 데이터 조각 중 하나는 당일의 활동 등급입니다. 그러나 활동 등급은 낮음, 보통 또는 높음 일 수 있습니다. 열거 형에 대한 당신의 대답이 완벽 해 보였다고 생각했습니다. DayProvider 인터페이스로 무엇을 얻고 있는지 잘 모르겠습니다 만, 제 의견에 혼란 스러울 수도 있습니다. – DaveJohnston

1

이제, 오직이 클래스의 3 개 인스턴스가 말을 할 것이라는 점을 미리 알고, 데이터는 런타임에 파일도 미리 알려져있다 (또는 최소한 읽기 될 것이라고 말할 수 및 정확한 파일 이름은 미리 알려짐). 기본적으로 내가 얻는 것은 런타임 중에 데이터가 변경되지 않는다는 것입니다 (일단 설정되면).

나는 enum을 사용합니다. 그리고 오히려이 맛 :

public enum MyEnum { 

    ONE("path/to/instance1/file"), 
    TWO("path/to/instance2/file"), 
    THREE("path/to/instance3/file"); 

    private String name; 

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

    public String getName() { 
     return name; 
    } 

} 

로 사용할 수 있습니다 다음과

MyEnum one = MyEnum.ONE; 
String name = one.getName(); 
+0

MyEnum 생성자가 문자열 (데이터 파일 경로)을 사용하여 변수를 초기화했는지 확인 했습니까? (즉, 파일을 열고 열어 이름, 설명 및 값을 읽습니다)? 파일 문자열의 경로를 이름으로 설정하는 대신? – DaveJohnston

0

첫 번째 방법은 최고의 코드 부패에 가장 발생하기 쉬운처럼 날 것으로 보인다. 나는 객체를 서브 클래 싱 (subclassing)하여 단지 그것을 빌드하는데 사용되는 데이터를 담고있는 파일 이름을 바꾸는 것에 깊은 인상을받지는 않는다.

당연히, 열거 액세스의 일종을 제공하는 외부 클래스에서이 모든 것을 래핑하여 원래 아이디어를 개선 할 수 있습니다. MyClass의 컬렉션입니다. 하지만이 하위 클래 싱 아이디어는 버려야한다고 생각합니다.

0

먼저 코드에서 이러한 인스턴스를 사용하는 위치를 제한해야합니다. 가능한 한 적은 장소에서 사용하십시오. 이 파일 이름을 감안할 때 파일에 액세스하는 세 개의 클래스 인스턴스가 필요하다고 생각합니다. 얼마나 많은 수업이 필요한지에 달려 있습니다. 이 클래스들에 대한 싱글 톤 패턴을보십시오.

이제 상수는 필요 없지만 파일 이름이 들어있는 파일을 읽고이를 독자 클래스에 제공하는 도우미 클래스를 가질 수 있습니다. 이름을 찾는 코드는 또한 Singleton의 정적 초기화 프로그램에서 호출하는 메서드가 될 수 있습니다.

private static final Map<String, YouClass> mapIt = 
    new HashMap<String, YouClass>(){{ 
     put("one", new YourClass("/name", "desc", 1)), 
     put("two", new YourClass("/name/two", "desc2", 2)), 
     put("three", new YourClass("/name/three", "desc", 3)) 
    }} 


public static YourClass getInstance(String named) { 
    return mapIt.get(named); 
} 

다음 번에 당신이 그것을 필요 :

0

일반적인 방법은지도를 사용하는 가장 좋은 옵션이 아닙니다

YouClass toUse = YourClass.getInstance("one"); 

은 아마 키로 문자열을 사용하지만 당신은 아이디어를 얻을.

1

은 (난 당신이 이미 답을 받아, 다시 한번 너무 느린 해요,하지만 여기가 ... 어쨌든) 당신은 (a)는 MyClass에의 객체에있는 데이터에 대한 변경을 방지 할

하고, (b) 런타임 코드가 MyClass의 새 인스턴스를 생성 할 수 없어야 함을 의미하는 MyClass 객체의 고정 세트 만 존재하도록 허용합니다.

귀하의 초기 예는 공장 인스턴스를 만들 수있는 유일한 것은 그래서 나는 공장의 방법을 사용하는 것

위반하는 public 생성자의 (b)를 가지고 있으며, 클래스는 그래서 어떤 세터를 제공하지 않습니다 불변입니다.

미래에 원하는 유연성에 따라 팩토리와 클래스를 동일한 패키지에 넣고 범위를 제한 할 수 있습니다. 또는 MyClass를 팩토리 내에서 내부 클래스로 만들 수 있습니다. MyClass를 구현과 다른 인터페이스로 만드는 것도 고려해 볼 수 있습니다.

속성 파일을 사용하여 팩토리 자체를 구성 할 수 있습니다. 속성은 내가 아래 (자바) 예 대신 "MyClass에"의 "푸"를 사용

one=/path/to/datafile1 
two=/another/path/to/datafile2 
three=/path/to/datafile3 

같은 것을 볼 수 있었다 (예를 들어, "foo.properties을") 파일.

public class FooFactory 
{ 
    /** A place to hold the only existing instances of the class */ 
    private final Map<String, Foo> instances = new HashMap<String, Foo>(); 

    /** Creates a factory to manufacture Foo objects */ 
    // I'm using 'configFile' as the name of a properties file, 
    // but this could use a Properties object, or a File object. 
    public FooFactory(String configfile) 
    { 
     Properties p = new Properties(); 
     InputStream in = this.getClass().getResourceAsStream(); 
     p.load(in); // ignoring the fact that IOExceptions can be thrown 

     // Create all the objects as specified in the factory properties 
     for (String key : p.keys()) 
     { 
      String datafile = p.getProperty(key); 
      Foo obj = new Foo(datafile); 
      instances.put(key, obj); 
     } 
    } 

    public Foo getFoo(String which) 
    { 
     return instances.get(which); 
    } 

    /** The objects handed out by the factory - your "MyClass" */ 
    public class Foo 
    { 
     private String name; 
     private String description; 
     private int value; 

     private Foo(String datafile) 
     { 
      // read the datafile to set name, description, and value 
     } 
    } 
} 

님은 런타임에 변경할 수 없습니다 미리 정의 된 인스턴스를 허용하도록 설정하고 있지만 나중에 다른 실행을 위해 다른 모든 것을 설정할 수 있습니다.

관련 문제