2011-05-27 6 views
4

J'ai un problème suivant je veux résoudre ellegantly:Design Patterns question

public interface IMyclass 
{ 
} 
public class A 
{ 
    public void Init(IMyclass class){?} 
    public IMyclass CreateMyClass(){?} 
} 

Au début du système que je veux définir le type dynamique de IMyClass en utilisant Init() et pendant l'exécution du système Je voudrais créer de nouvelles instances du type I défini à init.

Notes: 1.
IMyClass doit être l'interface
2. Le type dynamique de IMyClass connu seulement à init (je n'ai pas constructeur après :))
3. Je pourrais le faire en utilisant une réflexion ou une définition clone de la méthode chez IMyclass existe-t-il de meilleures solutions?

Merci.

+0

Vous pouvez prendre note que vos conventions de codage sont non standard. I pour les déclarations d'interface et les majuscules pour une méthode ne sont généralement pas utilisés en Java. (Certains vont argumenter sur l'interface, mais Eclipse est le seul morceau majeur de code que j'ai jamais vu utilisé). – Robin

Répondre

2

Vous pouvez passer un fournisseur dans class A

public class A 
{ 
    IMyClassProvider _provider; 

    public void Init(IMyClassProvider provider) 
    { 
     _provider = provider; 
    } 

    public IMyclass CreateMyClass() 
    { 
     return _provider.Create(); 
    } 
} 

Ou peut-être avec un délégué du constructeur

public class A 
{ 
    Func<IMyclass> _ctor; 

    public void Init(Func<IMyclass> ctor) 
    { 
     _ctor = ctor; 
    } 

    public IMyclass CreateMyClass() 
    { 
     return _ctor(); 
    } 
} 

Notez que ces deux exemples explosera si Init n'a pas été appelé avant CreateMyClass , vous auriez besoin de vérifier ou mieux de faire votre init dans le constructeur.

Est-ce que j'ai bien compris la question?

+0

Pourriez-vous expliquer ce que vous entendez par code d'utilisation du fournisseur ou de l'approvisionnement? –

+0

Je quitte juste le travail, mettra à jour plus tard si vous ne l'avez pas résolu d'ici là – fearofawhackplanet

+0

Oui, vous avez bien compris la question. –

1

Si vous souhaitez simplement instancier la même classe dans CreateMyClass() sans autre configuration, vous pouvez utiliser la réflexion. Je soupçonne que vous voulez plus que cela, et si c'est le cas, vous devrez expliquer comment vous voulez utiliser cela. Vous recherchez peut-être les modèles Builder ou Factory.

+0

Merci cela résout le problème. Mais pouvez-vous me dire quel est le problème de performance - ce paramètre que je crée très fréquemment. Si cela peut causer des problèmes de performance, avez-vous une solution sans faire appel à la réflexion? –

+0

Merci beaucoup! –

+0

Si vous voulez instancier dynamiquement des objets basés sur la classe d'un autre à l'exécution, non. Mais si vous n'avez qu'une poignée de classes de ce genre pour le faire, vous pouvez aussi écrire des usines individuelles pour chaque classe démontrée par fearofawhackplanet. –

2

C'est une sorte de injection de dépendance, vous devriez lire:

Fondamentalement, vous avez une classe A qui est rempli avec des usines (ou fournisseurs) à l'initialisation. Ensuite, vous utilisez A au lieu d'appeler new.

Un exemple rapide:

interface Provider<V> { 
    V instance(Object... args); 
} 
class Dispatch { 
    // you can make a singleton out of this class 
    Map<Class, Provider> map; 
    <T> void register(Class<T> cl, Provider<? extends T> p) { 
     // you can also bind to superclasses of cl 
     map.put(cl, p); 
    } 
    <T, I extends T> void register(Class<T> cl, final Class<I> impl) { 
     register(cl, new Provider<I>() { 
     I instance(Object... args) { 
      // this class should be refactored and put in a separate file 
      // a constructor with arguments could be found based on types of args values 
      // moreover, exceptions should be handled 
      return impl.newInstace(); 
     } 
     }); 
    } 
    <T> T instance(Class<T> cl, Object... args) { 
     return map.get(cl).instance(args); 
    } 
} 

// usage 
interface MyIf { ... } 
class MyIfImpl implements MyIf { ... } 

Dispatch d = new Dispatch(); 
d.register(MyIf.class, new Provider<MyIf>() { 
    MyIf instance(Object... args) { 
     return new MyIfImpl(); 
    } 
}); 
// or just 
d.register(MyIf.class, MyIfImpl.class); 
MyIf i = d.instance(MyIf.class); 

Edit: ajouté register(Class, Class)

+0

Merci beaucoup, vous avez une excellente solution pour ce problème et j'ai appris beaucoup de choses mais je voudrais éviter d'utiliser la réflexion. –

+0

Si vous voulez le faire sans réflexion, il suffit de supprimer la fonction 'register (Class, Class)', cela peut être pratique, mais pas tout à fait sûr. – Kru

1

Vous aurez besoin de réflexion à un moment donné en raison de la visibilité. Si vous pouvez accepter Reflection une fois et ne pas l'utiliser à nouveau, ce serait probablement idéal, oui?

Vous pouvez mettre une méthode getInstance() sur une interface cachée (situé dans le même paquet que IMyClass, MyClassImpl et A, mais pas ClientOfA), puis passer un prototype de MyClassImpl-A.init().

// -- You wish you would have thought of the word prototypeable! ...maybe? 
interface IMyClassPrototypeable extends IMyClass 
{ 
public IMyClass getInstance(); 
} 

class MyClassImpl implements IMyClassPrototypeable // -- and IMyClass by extension. 
{ 
// -- Still not visible outside this package. 
public IMyClass getInstance() 
{ 
    return new MyClassImpl(); 
} 
} 

class A 
{ 
private IMyClassPrototypeable prototype; 

// -- This method is package-private. 
void init(IMyClassPrototypeable prototype) 
{ 
    this.prototype = prototype; 
} 

public IMyClass createMyClass() 
{ 
    return prototype.getInstance(); 
} 
} 

Cette solution nécessiterait réflexion pour créer l'instance prototype de MyClassImpl, ce qui pourrait être fait via Spring (ou une autre forme d'injection de dépendance). Il utilise le pattern Prototype, le pattern Factory-method, et supporte facilement le pattern Singleton/Pool, mais rappelez-vous que plus de patterns de design utilisés ne sont pas toujours meilleurs. En fait, cela peut rendre le design (et le code) plus complexe et plus difficile à comprendre pour un débutant. Pour l'anecdote, la seule raison pour laquelle je pense à préconiser cette solution, c'est parce qu'elle prend la réflexion une fois, à l'avant, plutôt qu'à chaque fois que createMyClass() est appelée, ce que l'affiche originale indiquait fréquemment.

+0

Merci sa très bonne solution !! –