Pergunta

Eu estou fazendo um mini-ORM para um programa Java que estou escrevendo ... há uma classe para cada mesa no meu db, todos herdando de ModelBase.

ModelBase é abstrata e fornece um monte de métodos estáticos para encontrar e objetos de ligação do db, por exemplo:

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

Assim, você pode fazer coisas como ModelBase.findAll(Albums.class) para obter uma lista de todos persistiu álbuns. Meu problema é que, neste contexto estático, eu preciso para obter a cadeia de SQL apropriado na classe concreta Album. Eu não posso ter um método estático como

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

porque não existe polimorfismo para métodos estáticos em Java. Mas eu não quero fazer getSelectSQL() um método de instância em Album porque então eu preciso para criar uma instância dele apenas para obter uma cadeia que é realmente estática no comportamento.

No momento, findAll() usa a reflexão para obter o SQL apropriado para a classe em questão:

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

Mas isso é muito grosseiro.

Assim, qualquer idéias? É um problema geral que estou tendo uma e outra vez - a incapacidade de especificar métodos estáticos abstratos em classes ou interfaces. Sei por método estático polimorfismo não é e não pode trabalhar, mas isso não me impede de querer usá-lo outra vez!

Existe algum padrão / construção que me permite garantir que subclasses concretas X e Y implementar um método de classe (ou na sua falta, uma constante classe!)?

Foi útil?

Solução

Embora, eu concordo totalmente no ponto de "Static é a coisa errada a usar aqui", eu tipo de entender o que você está tentando endereço aqui. Ainda comportamento instância deve ser o caminho para o trabalho, mas se você insistir isso é o que eu faria:

A partir de seu comentário "Eu preciso criar uma instância dele apenas para obter uma cadeia que é realmente estática no comportamento"

Não é completamente correto. Se você olhar bem, você não está mudando o comportamento de sua classe base, apenas mudando o parâmetro para um método. Em outras palavras você está mudando os dados, não o algoritmo.

A herança é mais útil quando uma nova subclasse quer mudar a forma como um método funciona, se você só precisa mudar os "dados" os usos classe para trabalhar, provavelmente, uma abordagem como esta faria o truque.

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;

    }
}

Isto é, mapear todas as declarações com um mapa. O "óbvio" próximo passo para isso é para carregar o mapa de um recurso externo, como um arquivo de propriedades, ou um xml ou até mesmo (por que não) uma tabela de banco de dados, para uma maior flexibilidade.

Desta forma, você pode manter seus clientes de classe (e sua auto) feliz, porque você não necessário "criar uma instância" para fazer o trabalho.

// Client usage:

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

...

Outra abordagem é criar as instâncias de trás, e manter o seu cliente da interface intacta, enquanto usando métodos de instância, os métodos são marcados como "protegido" para evitar ter invocação externa. De forma semelhante do exemplo anterior, você também pode fazer isso

// 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";
    }
}

E você não precisa alterar o código do cliente, e ainda tem o poder de polimorfismo.

// Client usage:

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

...

Espero que isso ajude.

Uma nota final sobre o uso Lista vs. ArrayList. É sempre melhor programa para a interface do que para a implementação, desta forma você tornar seu código mais flexível. Você pode usar outra implementação lista que é mais rápido, ou faz outra coisa, sem alterar o código do cliente.

Outras dicas

estática é a coisa errada a usar aqui.

Conceitualmente estática é errado porque é apenas para serviços que não correspondem a um objeto real, física ou conceitual. Você tem um número de mesas, e cada um deve ser representado por um objeto real no sistema, e não apenas ser uma classe. Isso soa como se fosse um pouco teórica, mas tem consequências reais, como veremos.

Cada mesa é de uma classe diferente, e isso é OK. Desde que você só pode sempre ter um de cada mesa, limitar o número de instâncias de cada classe para um (usar uma bandeira - não torná-lo um Singleton). Tornar o programa cria uma instância da classe antes que acessa a tabela.

Agora você tem uma série de vantagens. Você pode usar todo o poder de herança e substituindo uma vez que seus métodos não são mais estáticas. Você pode usar o construtor para fazer qualquer inicialização, inclusive associando SQL com a tabela (SQL que seus métodos podem usar mais tarde). Isto deve fazer todos os seus problemas acima ir embora, ou pelo menos fica muito mais simples.

Parece que não há trabalho extra em ter que criar o objeto, e memória extra, mas é realmente trivial em comparação com as vantagens. A poucos bytes de memória para o objeto não será notado, e um punhado de chamadas do construtor terá talvez dez minutos para adicionar. Contra essa é a vantagem que o código para inicializar todas as tabelas não precisa ser executado se a tabela não é usado (o construtor não deve ser chamado). Você vai encontrá-lo simplifica muito as coisas.

Por que não usar anotações? Eles fitpretty bem o que está fazendo:. Adicionar meta-informação (aqui uma consulta SQL) para uma classe

Como sugerido, você poderia usar anotações, ou você pode mover os métodos estáticos para objetos de fábrica:

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....."; }
}

Mas não é um cheiro muito bom ter objetos sem qualquer estado.

Se você está passando uma classe para findAll, porque você não pode passar uma classe para getSelectSQL em ModelBase?

asterite: quer dizer que getSelectSQL só existe em ModelBase, e ele usa o passado em sala de aula para fazer um tablename ou algo parecido? Eu não posso fazer isso, porque alguns dos modelos têm wildy differeing selecione construções, então eu não posso usar um universal "select * from" + classToTableName () ;. E qualquer tentativa de obter informações a partir dos modelos sobre suas selecione corridas construto para o mesmo problema a partir da pergunta original - você precisa de uma instância do modelo ou alguma fantasia reflexão

.

aparelho: Eu definitivamente ter um olhar para anotações. Embora eu não posso ajudar, mas pergunto o que as pessoas fizeram com esses problemas antes que houvesse reflexão?

Você pode ter seus métodos de SQL como métodos de instância em uma classe separada.
Em seguida, passe o objeto modelo para o construtor desta nova classe e chamar seus métodos para obter SQL.

Uau - este é um exemplo muito melhor de algo que eu perguntei anteriormente em termos mais gerais - como implementar propriedades ou métodos que são estáticos para cada classe de implementação de uma forma que evita a duplicação, fornece acesso estática sem precisar instanciar a classe preocupado e se sente 'Right'.

A resposta curta (Java ou .NET): Você não pode. resposta mais longa - você pode, se você não se importa de usar uma anotação de nível Class (reflexão) ou instanciar um objeto (método de instância), mas não são verdadeiramente 'clean'

.

Veja a minha pergunta anterior (relacionados) aqui: como lidar com campos estáticos que variam através da implementação de classe Eu pensei que as respostas eram todos muito coxo e perdeu o ponto. Sua pergunta é muito melhor formulada.

Eu concordo com Gizmo: ou você está olhando para anotações ou algum tipo de arquivo de configuração. Eu dar uma olhada em Hibernate e outros frameworks ORM (e talvez até mesmo bibliotecas como log4j!) Para ver como eles lidam com carregamento de nível de classe meta-informação.

Nem tudo pode ou deve ser feito por meio de programação, eu sinto que este pode ser um desses casos.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top