Pergunta

Eu estou com o objetivo de criar um conjunto de objetos, cada um dos quais tem um identificador único. Se um objeto já existe com esse identificador, eu quero usar o objeto existente. Caso contrário, eu quero criar um novo. Eu estou tentando não usar a palavra Singleton, porque eu sei que é uma palavra suja aqui ...

Eu posso usar um método de fábrica:

    // A map of existing nodes, for getInstance.
private static Map<String, MyClass> directory = new HashMap<String, MyClass>();

public static MyClass getInstance(String name) {
    MyClass node = directory.get(name);
    if(node == null) {
       node == new MyClass(name);
    }
    return node;
}

Ou igualmente, eu poderia ter um método MyClassFactory separado.

Mas eu tinha a intenção de MyClass subclasse:

public class MySubClass extends MyClass;

Se eu fizer mais nada, e invocar MySubClass.getInstance ():

MyClass subclassObj = MySubClass.getInstance("new name");

... então subclassObj será um MyClass simples, não um MySubClass.

No entanto, substituindo getInstance () em cada subclasse parece hacky.

Existe uma solução elegante que eu estou ausente?


Essa é a versão generalizada da questão. Mais detalhes, desde os respondentes pediu para eles.

O programa é para a geração de um grafo orientado de dependências entre os nós representam peças de software. Subclasses incluem programas Java, Web Services, SQL Stored Procedures, triggers orientados a mensagens, etc.

Assim, cada classe "é-um" elemento nesta rede, e tem métodos para navegar e modificar relações de dependência com outros nós. A diferença entre as subclasses será a implementação do método populate() usado para configurar o objeto da fonte apropriada.

Digamos que o nó chamado aprende 'login.java' que tem uma dependência em 'checkpasswd.sqlpl':

this.addDependency( NodeFactory.getInstance("checkpasswd.sqlpl"));

O problema é que o objeto checkpasswd.sqlpl pode ou não existir neste momento.

Foi útil?

Solução

O método estático é definido na classe pai, e é chamado estaticamente também. Assim, não há nenhuma maneira de saber no método que você chamou-o na subclasse. O compilador Java, provavelmente, ainda resolve a chamada estaticamente a uma chamada para a classe pai.

Assim você terá que quer Reimplementar o método estático em suas classes filhas como você propõe, ou torná-los não estática para que possa herança (em uma hierarquia de fábrica objetos , não classes), ou passar um parâmetro para indicar o tipo que você deseja criar.

Confira o método EnumSet.noneOf (). Ele tem um problema semelhante como você faz, e resolve-lo, passando o método java.lang.Class. Você poderia usar newInstance na classe. Mas, pessoalmente, eu só uso fábrica de objetos ao invés de classes com métodos estáticos.

Outras dicas

Você já olhou para Guice? Não tenho certeza se ele iria resolver o seu problema exatamente, mas ele age como uma fábrica e injeção de dependência contêiner genérico e elimina não-tipo chaves de corda de segurança.

depois de ler você explicação do problema eu acho que o seu tornando muito difícil para si mesmo, sub classing em sua construção gráfico. Acho que o problema se torna muito mais simples se você separar o gráfico de dependência do "informações sobre o programa"

usar uma interface como:

Public Interface Node<T> {
  public Object<T>    getObject();
  public String       getKey();
  public List<String> getDependencies();
  public void         addDependence(String dep);
}

e, em seguida, usar uma fábrica para instanciar você nós

Você parece implicar que em algum lugar você sabe que classe que deveria ser se ele não existe. Se você implementar essa lógica em sua fábrica você deve obter as classes corretas.

Desta forma, você também não deve ter necessidade de saber que classe realmente foi devolvido a partir da fábrica.

Gostaria também provável make 'MyClass' uma interface se você estiver considerando um padrão de fábrica.

classe

A classe é parametrizada com o tipo de instância que pode criar. (Ex:. Class é uma fábrica para instâncias String)

Eu não vejo qualquer maneira de contornar saber o tipo de exemplo que deve ser criado quando você getOrCreate usando o método de fábrica aqui, então eu recomendaria passar para o método e parametrização do tipo que está sendo gerado:

private static Map<String, MyClass> directory = new HashMap<String, MyClass>();

public static <T extends MyClass> T getInstance(String name, Class<T> generatorClass)
{
  MyClass node = directory.get(name);    
  if(node == null) {
    node = generatorClass.getConstructor(String.class).newInstance(name);
    directory.put(name, node);
  }
  return node;
}

Além disso, eu notei que você não estava realmente colocando os nós recém-construídos no diretório - Estou assumindo que é um descuido. Você também pode sobrecarregar este método com outro que não tomar um gerador e codificado para o tipo padrão:

public static MyClass getInstance(String name) {
  return getInstance(name, MyClass.class);
}

Você provavelmente quer injeção de dependência. Seria generalizar o que você está tentando fazer um pouco.

Além disso, Herança provavelmente não é exatamente o que você precisa também. Nunca usá-lo a menos que você pode passar o "é-um" teste. Se a sua classe herdada "tem-um" tag string única (essencialmente é isso que sua classe parece ser), isso não significa que "é-um" ...

Você poderia ter uma segurar o outro embora. Há provavelmente algumas soluções, mas A) você não postar sua lista inteira de exigências para que possamos, mas acho, e B) o que você provavelmente quer é injeção de dependência. :)

O padrão parece ser uma espécie de Flyweight (estruturalmente , se não for uma combinação perfeita para a intenção.)

O método populate, como descrito, pode ser mapeado para o padrão Template, embora não necessariamente abordar as preocupações expressas.

O que eu sugiro é uma generalização da fábrica, com um create (em vez de getInstance, o que implica numa Singleton, pelo menos para mim) método para os vários tipos que você espera.

public static MyClass createJavaNode(String name, <differentiator>);
public static MyClass createSqlPlNode (String name, <differentiator>);
.
.
.

O conhecimento sobre como um name mapeia para um <differentiator> é realmente uma escolha de implementação. Idealmente, haveria uma create polimórfica ea diferenciação seria por tipo node. O método create retorna MyClass, mas é realmente retornando as subclasses. Eu consideraria fortemente tornando MyClass quer uma interface ou uma classe abstrata, com um método abstract populate (há o Template).

A partir da descrição, ele realmente parece que é o comportamento criação que é diferente entre os tipos, não o comportamento das sub-classes si mesmos, para o caso da refatoração de MyClass a uma interface ou classe abstrata fica mais forte.

Para parafrasear R. A. Heinlein, TANSTAAFL - não há nenhuma coisa como um almoço grátis - em algum lugar o conhecimento de como criar os vários tipos tem que existir no aplicativo. Então sua escolha é colocar esse conhecimento na classe de fábrica, como algumas das outras respostas têm expressado, ou para separar a decisão do o implementação é criado a partir de como é criada. Parece que há uma capacidade de digitalização de um sistema de arquivos e recolher as Nodes dele. Esse código vai (ou pode) saber o tipo do Node (a resposta para a o ), que deve ser criado.

Se isso será implementado como um switch ou de uma forma mais OO (um dos momentos onde impulsionado tabela de expedição seria bom) é até você. Se isso é algo que pode ser útil, posso expandir em que aqui.

BTW, se o comportamento do núcleo das necessidades MyClass ser estendido ou modificado para algumas subclasses, que é onde um Decorator pode ser útil.


Resposta Original:

Você pode considerar a Decorator ou Template design Patterns como alternativas.

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