Notez que certaines des autres réponses peuvent sans doute décrire des usines, mais ne décrivent pas le GOF Factory Pattern.
Maintenant, je veux remplacer cette ligne avec un motif d'usine , bien que je ne suis pas sûr que mon constructeur TestMode nécessite un objet supplémentaire et je ne suis pas sûr où je aurais besoin de passer cette valeur.
Eh bien, vous pourriez penser de cette façon: MainMode, pas TestMode, est celui qui fait quelque chose de spécial. La chose spéciale qu'il fait, est de ignorer le nombre donné, afin de s'assurer qu'il est vraiment aléatoire. Dans cette façon de penser, c'est MainMode qui fait quelque chose de plus. Ou, si autre que le caractère aléatoire, MainMode et TestMode ne sont pas différents, alors vous pensez peut-être que vous pouvez factoriser cette similarité dans une classe, qui est fourni une des deux stratégies pour calculer des nombres aléatoires. Une stratégie serait en fait aléatoire, et l'autre serait perverse, avec une fourchette aléatoire de seulement 1 valeur. Mais supposons qu'il existe d'autres différences entre MainMode et TestMode - il est probable que TestMode génère un débogage supplémentaire sur System.out ou quelque chose comme ça.
Nous pouvons encore factoriser « comment pouvons-nous fournissons hasard » de sommes que nous testons ou jouer le jeu pour de vrai ». Ce sont orthogonales préoccupations.
Alors maintenant, nous savons que, en plus de tout ce que un 'Mode fait, il devrait accepter une stratégie aléatoire.Alors nous pourrions, par exemple, quand on vous dit que la plate-forme standard aléatoire n'est pas vraiment assez aléatoire, vous pouvez le remplacer par un meilleur aléatoire
Ou vous peut faire des tests où la plage de randoms est limitée à seulement deux choix, ou alterne toujours de un à zéro, ou retourne sur chaque appel la valeur suivante dans certains Vecrtor ou Iterat ou.
nous utilisons donc les stratégies de modèle Stratégie GOF pour construire les de Hasard:
interface RandomStrategy {
public double random();
}
public class NotSoRandom implements RandomStrategy {
private double r;
public NotSoRandom(final double r) { this.r = r; }
public double random() { return r; }
}
public class PlatformRandom implements RandomStrategy {
public double random() { return Math.random(); }
}
Maintenant, si votre application entière ne jamais crée un « mode, il n'y a pas besoin d'une usine; vous utilisez une fabrique lorsque vous devez créer le même type de classe encore et encore; L'usine n'est en fait qu'une stratégie pour créer le bon type de (sous-) classe.
Dans le code de production, j'ai utilisé des usines où j'ai une classe générique qui crée des choses, et je dois dire comment créer la bonne sous-classe à créer; Je passe dans une usine pour le faire.
Maintenant, nous créons un motif d'usine pour le 'Mode'; ce sera étonnamment semblable au modèle de stratégie:
abstract class Mode() {
private RandomStrategy r;
public Mode(final RandomStrategy r) { this.r = r; }
// ... all the methods a Mode has
}
public class MainMode implements Mode {
public MainMode(final RandomStrategy r) { super(r); }
}
public class TestMode implements Mode {
public TestMode(final RandomStrategy r) { super(r); }
}
interface ModeFactory{
public Mode createMode(final RandomStrategy r);
}
public class MainFactory() {
public Mode createMode(final RandomStrategy r) {
return new MainMode(r);
}
}
public class TestFactory() {
public Mode createMode(final RandomStrategy r) {
return new TestMode(r);
}
}
Alors maintenant, vous savez sur le modèle de l'usine et modèle de stratégie, et la façon dont ils sont similaires en « forme », mais différent dans la façon dont ils sont utilisés: l'usine Pattern est Object Creational et renvoie un objet à utiliser; La stratégie est Object Behavioral et une instance est généralement créée explicitement et une référence est conservée dans l'instance pour encapsuler un algorithme. Mais en termes de structure, ils sont assez similaires. Editer: l'OP demande, dans un commentaire, "Comment est-ce que j'intégrerais ceci dans mon interface graphique?"
Eh bien, rien de tout cela n'appartient à l'interface graphique de votre programme, sauf peut-être le «Mode». Vous créez la ConcreteStrategy et la transmettez à l'Usine préférée dans une routine d'installation, en déterminant éventuellement celle à utiliser en fonction des arguments de la ligne de commande ou des fichiers de configuration. En fait, vous devez sélectionner la bonne usine en sélectionnant la bonne classe dans votre message d'origine. Encore une fois, si vous créez seulement un de quelque chose, vous n'avez pas besoin d'une usine; les usines sont destinées à la production de masse (ou à la création de familles de types concrets apparentés - bien que cela dépasse le cadre de cette question).
(Supposons que nous avons un jeu où l'utilisateur peut sélectionner sur la ligne de commande si pour combattre des robots ou des dragons, nous voudrions instancier un OpponentFactory qui produisent Adversaires (une interface), avec les classes dérivées RobotOpponent et DragonOpponent , et passez cette fabrique à la partie du jeu que spawnsNewOpponent() .De même, un utilisateur peut sélectionner des adversaires courageux ou lâches, que nous aurions mis en place en tant que stratégie.Nous n'avons pas besoin de faire plus d'instances de stratégie, en tant que La stratégie est généralement idempotente (sans état et singleton).)
static int main(String[] args) {
// setup game world
final RandomStrategy r = "random".equals(args[0])
? new PlatformRandom() : new NotSoRandom(Integer.intValue(args[0])) ;
// notice the simlarity to the code you originally posted;
// we factored out how to achieve "randomness" as a Strategy.
// now we will use our Strategy to setup our Factory;
final ModeFactory f = "test".equals(args[1])
? new TestFactory(r) : new MainFactory(r);
// also similar to your code
// we've just added an extra level of indirection:
// instead of creating a Mode, we've created an object that can create Modes
// of the right derived type, on demand.
// call something that uses our factory
functionThatRunsameAndNeedstoProduceModesWhenevertNeedsTo(f);
}
Alors, comment pourrais-je implémenter ceci dans mon interface graphique? –