문제

나는 내가 쓰고있는 Java 프로그램을위한 미니 orm을 만들고있다 ... 내 DB의 각 테이블에 대한 수업이있다. ModelBase.

ModelBase 예를 들어 DB에서 물체를 찾고 바인딩하기위한 정적 방법을 제공합니다.

public static ArrayList findAll(Class cast_to_class) {
  //build the sql query & execute it 
}

그래서 당신은 같은 일을 할 수 있습니다 ModelBase.findAll(Albums.class) 모든 지속 된 앨범의 목록을 얻으려면. 내 문제는이 정적 컨텍스트에서 콘크리트 클래스 앨범에서 적절한 SQL 문자열을 가져와야한다는 것입니다. 나는 같은 정적 방법을 가질 수 없습니다

public class Album extends ModelBase {
  public static String getSelectSQL() { return "select * from albums.....";}
}

자바에서 정적 방법에 대한 다형성이 없기 때문입니다. 그러나 나는 만들고 싶지 않습니다 getSelectSQL() 인스턴스 메소드 Album 그렇다면 행동에서 정적 인 문자열을 얻으려면 인스턴스를 만들어야합니다.

현재, findAll() 반사를 사용하여 해당 클래스에 적합한 SQL을 얻습니다.

select_sql = (String)cast_to_class.getDeclaredMethod("getSelectSql", new Class[]{} ).invoke(null, null);

그러나 그것은 꽤 거칠다.

그래서 어떤 아이디어가 있습니까? 클래스 나 인터페이스에서 추상 정적 메소드를 지정할 수없는 시간과 시간이 걸리는 일반적인 문제입니다. 알아요 정적 방법 다형성은 효과가없고 작동하지 않지만 다시 시간을 사용하고 싶지는 않습니다!

콘크리트 서브 클래스 x와 y가 클래스 메소드 (또는 클래스 상수)를 구현할 수있는 패턴/구성이 있습니까?

도움이 되었습니까?

해결책

그럼에도 불구하고, 나는 "정적은 여기에서 사용하는 것이 잘못된 것"의 시점에 전적으로 동의합니다. 나는 당신이 여기서 해결하려는 것을 이해합니다. 여전히 인스턴스 동작은 일을하는 방법이어야하지만, 당신이 주장하면 이것이 내가하는 일입니다.

당신의 의견에서 시작하여 "행동에서 정적 인 문자열을 얻으려면 인스턴스를 만들어야합니다."

완전히 정확하지 않습니다. 잘 보이면 기본 클래스의 동작을 변경하지 않고 메소드의 매개 변수를 변경합니다. 다시 말해 알고리즘이 아닌 데이터를 변경합니다.

상속은 새로운 서브 클래스가 메소드의 작동 방식을 변경하려는 경우 더 유용합니다. 클래스가 "데이터"를 변경 해야하는 경우 클래스가 작동하는 데 사용하는 "데이터"를 변경 해야하는 경우 이와 같은 접근 방식이 트릭을 수행 할 것입니다.

class ModelBase {
    // Initialize the queries
    private static Map<String,String> selectMap = new HashMap<String,String>(); static {
        selectMap.put( "Album", "select field_1, field_2 from album");
        selectMap.put( "Artist", "select field_1, field_2 from artist");
        selectMap.put( "Track", "select field_1, field_2 from track");
    }

    // Finds all the objects for the specified class...
    // Note: it is better to use "List" rather than "ArrayList" I'll explain this later.
    public static List findAll(Class classToFind ) {
        String sql = getSelectSQL( classToFind );
        results = execute( sql );
        //etc...
        return ....
    }

    // Return the correct select sql..
    private static String getSelectSQL( Class classToFind ){
        String statement = tableMap.get( classToFind.getSimpleName() );
        if( statement == null ) {
            throw new IllegalArgumentException("Class " + 
                 classToFind.getSimpleName + " is not mapped");
        }
        return statement;

    }
}

즉, 모든 진술을지도로 매핑하십시오. 이에 대한 "명백한"다음 단계는 추가 유연성을 위해 속성 파일, XML 또는 데이터베이스 테이블과 같은 외부 리소스에서 맵을로드하는 것입니다.

이렇게하면 작업을 수행하기 위해 "인스턴스 생성"이 필요하지 않기 때문에 수업 고객 (및 자기 자신)을 행복하게 유지할 수 있습니다.

// Client usage:

...
List albums = ModelBase.findAll( Album.class );

...

또 다른 접근법은 뒤에서 인스턴스를 생성하고 인스턴스 메소드를 사용하는 동안 클라이언트 인터페이스를 그대로 유지하는 것입니다. 메소드는 외부 호출을 피하기 위해 "보호 된"것으로 표시됩니다. 이전 샘플과 비슷한 방식으로이를 수행 할 수 있습니다.

// Second option, instance used under the hood.
class ModelBase {
    // Initialize the queries
    private static Map<String,ModelBase> daoMap = new HashMap<String,ModelBase>(); static {
        selectMap.put( "Album", new AlbumModel() );
        selectMap.put( "Artist", new ArtistModel());
        selectMap.put( "Track", new TrackModel());
    }

    // Finds all the objects for the specified class...
    // Note: it is better to use "List" rather than "ArrayList" I'll explain this later.
    public static List findAll(Class classToFind ) {
        String sql = getSelectSQL( classToFind );
        results = execute( sql );
        //etc...
        return ....
    }

    // Return the correct select sql..
    private static String getSelectSQL( Class classToFind ){
        ModelBase dao = tableMap.get( classToFind.getSimpleName() );
        if( statement == null ) {
            throw new IllegalArgumentException("Class " + 
                 classToFind.getSimpleName + " is not mapped");
        }
        return dao.selectSql();
    }
    // Instance class to be overrided... 
    // this is "protected" ... 
    protected abstract String selectSql();
}
class AlbumModel  extends ModelBase {
    public String selectSql(){
        return "select ... from album";
    }
}
class ArtistModel  extends ModelBase {
    public String selectSql(){
        return "select ... from artist";
    }
}
class TrackModel  extends ModelBase {
    public String selectSql(){
        return "select ... from track";
    }
}

그리고 클라이언트 코드를 변경할 필요는 없으며 여전히 다형성의 힘이 있습니다.

// Client usage:

...
List albums = ModelBase.findAll( Album.class ); // Does not know , behind the scenes you use instances.

...

이게 도움이 되길 바란다.

목록 대 배열리스트 사용에 대한 최종 메모. 구현보다 인터페이스로 프로그래밍하는 것이 항상 좋습니다.이 방법으로 코드를보다 유연하게 만듭니다. 클라이언트 코드를 변경하지 않고 더 빠르거나 다른 일을하는 다른 목록 구현을 사용할 수 있습니다.

다른 팁

정적은 여기서 사용하는 것이 잘못된 것입니다.

개념적으로 정적은 실제 객체, 물리적 또는 개념에 해당하지 않는 서비스에만 해당되기 때문에 잘못된 것입니다. 당신은 많은 테이블이 있으며, 각각은 클래스가 아니라 시스템의 실제 객체로 표시되어야합니다. 그것은 약간 이론적 인 것처럼 들리지만 우리가 볼 수 있듯이 실제 결과를 가져옵니다.

각 테이블은 다른 클래스이며 괜찮습니다. 각 테이블 중 하나만 가질 수 있으므로 각 클래스의 인스턴스 수를 하나로 제한하십시오 (플래그 사용 - 싱글 톤으로 만들지 마십시오). 프로그램이 테이블에 액세스하기 전에 클래스의 인스턴스를 만듭니다.

이제 몇 가지 장점이 있습니다. 메소드가 더 이상 정적이지 않기 때문에 상속 및 재정의 전체의 힘을 사용할 수 있습니다. 생성자를 사용하여 SQL을 테이블과 연결하는 것을 포함하여 모든 초기화를 수행 할 수 있습니다 (나중에 방법이 사용할 수있는 SQL). 이것은 위의 모든 문제가 사라지거나 최소한 훨씬 간단하게 만들어야합니다.

객체와 추가 메모리를 만들어야하는 추가 작업이있는 것 같습니다. 그러나 장점에 비해 정말 사소한 일입니다. 객체에 대한 몇 바이트의 메모리는 눈에 띄지 않으며, 소수의 생성자 호출은 추가하는 데 10 분이 걸릴 수 있습니다. 이에 반대하는 것은 테이블을 사용하지 않으면 테이블을 실행할 필요가 없다는 장점입니다 (생성자를 호출해서는 안됩니다). 당신은 그것이 물건을 많이 단순화한다는 것을 알게 될 것입니다.

주석을 사용하지 않는 이유는 무엇입니까? 그들은 당신이하고있는 일에 잘 맞습니다 : 메타 정보 (여기서 SQL 쿼리)를 클래스에 추가합니다.

제안 된 바와 같이, 주석을 사용하거나 정적 메소드를 공장 객체로 이동할 수 있습니다.

public abstract class BaseFactory<E> {
    public abstract String getSelectSQL();
    public List<E> findAll(Class<E> clazz) {
       // Use getSelectSQL();
    }
}

public class AlbumFactory extends BaseFactory<Album> {
    public String getSelectSQL() { return "select * from albums....."; }
}

그러나 상태가없는 물건을 갖는 것은 아주 좋은 냄새가 아닙니다.

FindAll에 클래스를 통과한다면 왜 Modelbase에서 getSelectSQL로 클래스를 통과 할 수 없습니까?

ASTERITE : GetSelectSQL이 ModelBase에만 존재한다는 것을 의미합니까?이 통과 된 클래스를 사용하여 태블 이름이나 그와 비슷한 것을 만듭니다. 일부 모델에는 야생의 다른 구성이 있기 때문에 그렇게 할 수 없습니다. 또한 선택 구성에 대한 모델에서 정보를 얻으려는 시도는 원래 질문에서 동일한 문제에 실립니다. 모델의 인스턴스 또는 멋진 반사가 필요합니다.

기즈모 : 나는 주석을 분명히 살펴볼 것입니다. 반성하기 전에 사람들이 이러한 문제로 무엇을했는지 궁금해 할 수는 없지만?

별도의 클래스에서 SQL 메소드를 인스턴스 메소드로 가질 수 있습니다.
그런 다음 모델 객체를이 새로운 클래스의 생성자로 전달하고 SQL을 얻는 방법을 호출하십시오.

와우 - 이것은보다 일반적인 용어로 이전에 요청한 것의 훨씬 더 나은 예입니다. 중복을 피하는 방식으로 각 구현 클래스에 정적 인 속성 또는 메소드를 구현하는 방법은 해당 클래스를 인스턴스화 할 필요없이 정적 액세스를 제공합니다. '오른쪽'.

짧은 대답 (Java 또는 .NET) : 할 수 없습니다. 더 긴 답변 - 클래스 레벨 주석 (반사)을 사용하거나 객체 (인스턴스 메소드)를 인스턴스화하는 것이 마음에 들지 않지만 진정으로 '깨끗한'것은 아닙니다.

내 이전 (관련) 질문을 참조하십시오. 클래스를 구현함으로써 다른 정적 필드를 처리하는 방법나는 대답이 모두 정말 절름발이라고 생각하고 요점을 놓쳤다 고 생각했습니다. 당신의 질문은 훨씬 더 나은 말입니다.

나는 Gizmo에 동의합니다 : 당신은 주석이나 일종의 구성 파일을보고 있습니다. 클래스 수준 메타 정보의로드를 어떻게 처리하는지 확인하기 위해 최대 절전 모드 및 기타 ORM 프레임 워크 (및 Log4J와 같은 라이브러리)를 살펴 봅니다.

모든 것이 프로그래밍 방식으로 행할 수 있거나 수행되어야하는 것은 아닙니다. 저는 이것이 그러한 경우 중 하나 일 수 있다고 생각합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top