Question

Dans le cadre d'un projet auquel je participe depuis de nombreuses années, j'ai progressivement développé un modèle de conception qui s'est révélé extrêmement utile pour moi. Je pense parfois que je devrais être un peu évangélique avec ça, mais je serais un peu gêné si j'essayais et découvrais que c'était juste ma version du vieux chapeau de quelqu'un. J'ai fouillé dans Design Patterns en le cherchant vainement, et je n'ai encore rencontré personne d'autre qui en parle, mais ma recherche n'a pas été exhaustive.

L’idée principale est de disposer d’un objet broker qui gère un ensemble d’objets de définition, chaque objet de définition constituant une valeur possible d’une propriété complexe. Par exemple, vous pouvez avoir des classes Car, Plane et Generator qui ont toutes un type EngineType. Car ne stocke pas son propre objet EngineType, mais une clé de référence indiquant le type de moteur dont elle dispose (par exemple, un entier ou un ID de chaîne). Lorsque nous voulons examiner les propriétés ou le comportement d'un EngineType, disons WankelEngine, nous demandons à l'objet singleton EngineTypeBroker de définir l'objet de WankelEngine, en lui transmettant la clé de référence. Cet objet encapsule tout ce qu'il est intéressant de savoir sur EngineTypes, il peut s'agir simplement d'une liste de propriétés, mais potentiellement d'un comportement également chargé.

Il s’agit donc d’une sorte d’agrégation partagée, à couplage lâche, où de nombreuses voitures peuvent avoir un WankelEngine mais qu’il n’existe qu’un seul objet de définition WankelEngine (et que EngineTypeBroker peut remplacer cet objet, exploitant ainsi le couplage lâche pour améliorer le morphisme d’exécution. ).

Certains éléments de ce modèle lors de l'utilisation (en continuant à utiliser EngineType à titre d'exemple):

  1. Il existe toujours des fonctions IsEngineType (x) et EngineType (x) pour déterminer si une valeur donnée est une clé de référence valide pour EngineType et pour récupérer l'objet de définition EngineType correspondant à une clé de référence, respectivement.
  2. J'autorise toujours plusieurs formes de clé de référence pour un EngineType donné, toujours au moins un nom de chaîne et l'objet de définition lui-même, le plus souvent un ID entier et parfois des types d'objet agrégeant un EngineType. Cela facilite le débogage, assouplit le code et, dans mon cas particulier, atténue de nombreux problèmes de compatibilité ascendante par rapport aux pratiques plus anciennes. (La manière habituelle de faire tout cela, dans le contexte de ce projet, consistait à définir des hachages pour chaque propriété qu'un EngineType pouvait avoir et à rechercher les propriétés par clé de référence.)
  3. En général, chaque instance de définition est une sous-classe d'une classe générale pour ce type de définition (c'est-à-dire que WankelEngine hérite de EngineType). Les fichiers de classe pour les objets de définition sont conservés dans un répertoire tel que / Def / EngineType (c'est-à-dire que la classe de WankelEngine serait / Def / EngineType / WankelEngine). Les définitions associées sont donc regroupées et les fichiers de classe ressemblent aux fichiers de configuration pour EngineType, mais avec la possibilité de définir du code (rarement trouvé dans les fichiers de configuration).

Quelques exemples de pseudocodes illustrant de manière triviale:

class Car {

    attribute Name;
    attribute EngineTypeCode;

    object GetEngineTypeDef() {
        return EngineTypeBroker->EngineType(this->GetEngineTypeCode());
    }

    string GetDescription() {
        object def = this->GetEngineTypeDef();
        return "I am a car called " . this->GetName() . ", whose " .
            def->GetEngineTypeName() . " engine can run at " .
            def->GetEngineTypeMaxRPM() . " RPM!";
    }

}

Alors, y a-t-il un nom pour ça?

Était-ce utile?

La solution

SingletonRegistry

Croyez-moi ou non. Je pensais exactement la même chose ce matin.

J'ai déjà utilisé ce modèle, mais je n'ai jamais trouvé de référence ni appris comment le nommer.

Je pense qu’il s’agit d’une sorte de " Keyed " singleton, où les instances sont stockées quelque part et sont obtenues à l'aide d'une clé.

La dernière fois que je l’ai utilisé, c’était pour récupérer des données provenant de différentes sources.

J'avais environ 50 tables de base de données (le rendre 10) et j'ai une "table" front-end où les données devaient être affichées, mais les données peuvent provenir de l’une de ces sources et chacune d’elles nécessite une logique différente (requêtes, jointures, clés, etc.)

Ce front-end était "configurable". donc je ne pouvais pas savoir quelles valeurs devaient être montrées et lesquelles ne le feraient pas.

La solution consistait à prendre la colonne Nom de la colonne (au début) et à obtenir l’instance appropriée pour créer la requête appropriée.

Cela a été installé dans une carte de hachage au début, puis récupéré à partir d'une table de base de données.

Le code ressemblait à ceci:

class DataFetcher {
    abstract Object getData( Object id );
}

class CustomerNameDataFetcher extends DataFetcher {
    Object getData( Object customerId ) { 
        // select name from customer where id = ? 
     }
}

class CompanyAdressDataFetcher extends DataFetcher { 
     Object getData( Object customerId ) { // don't ask why.
          // select name from company , customer where customer.co = company.co and cu = ?  etc.
     }
} 

class ProductColor extends DataFetcher { 
     Object getData( Object x ) { 
     // join from customer to color, to company to season to a bunch of table where id = ? 
}

// And the list goes on.

Chaque sous-classe utilisait une logique différente.

Au moment de l'exécution, l'utilisateur a configuré sa vue et sélectionne ce qu'il souhaite voir.

Lorsque l'utilisateur a sélectionné les colonnes à afficher, j'ai utilisé le nom de la colonne et un identifiant pour récupérer les données.

Les DataFetchers ont tous été installés dans la classe parent (je ne voulais pas avoir de classe separeate pour cela) dans une méthode de classe.

class DataFetcher {
    abstract Object getData( Object id );

    private static final Map fetchers = new HashMap();static { 
        fetchers.put("customer.name", new CustomerNameDataFetcher() );
        fetchers.put("company.address", new CompanyAdressDataFetcher () );
        fetchers.put("product.color", new ProductColor () );
        ...
    }
    public static DataFetcher getFetcher( String id ) { 
        return fetchers.get( id );
    }      

}

À la fin, pour remplir la table des serveurs, je l’appelle comme suit:

pseudocode

 for each row in table 
      for each column in row
          column.text = DataFetcher.getFetcher( column.id ).getData( row.id )
       end
 end

Est-ce comme ça? Ou puis-je mal interpréter votre description et la mienne est très différente.

Enfin, je pense que cela s’appelle SingletonRegistry ou quelque chose comme ça. J'ai (probablement) comme vous, créé cela par nécessité. Il y a de fortes chances que ce soit un schéma commun.

Autres conseils

J'ai déjà utilisé un modèle similaire à celui-ci, le plus souvent dans les jeux. J'aurais des classes WeaponDefinition et WeaponInstance (pas tout à fait avec ces noms). La classe WeaponDefinition (et diverses sous-classes, si j’ai différents types d’armes, par exemple mêlée contre projectile) serait chargée de garder une trace des données globales pour ce type d’arme (cadence de tir, munition maximale, nom, etc.) et a toute la logique. La classe WeaponInstance (et ses sous-classes) contient l’état actuel dans la séquence de tir (à utiliser pour comparer le taux de tir), le nombre d’armes en cours et un pointeur (il peut s’agir d’une clé dans une classe de responsable, comme dans votre exemple). cela ne semble pas être une exigence du modèle) à la définition de l'arme. WeaponInstance a un tas de fonctions pour le déclenchement, le rechargement, etc., qui appellent simplement la méthode appropriée sur l'instance WeaponDefinition, en se passant comme argument. Cela signifie que le système WeaponDefinition n'est pas dupliqué pour tous les chars / soldats / avions du monde du jeu, mais qu'ils ont tous leur propre compte de munitions, etc.

Je ne sais pas du tout comment ça s'appelle, et je ne suis pas sûr que ce soit tout à fait la même chose que ce dont vous parlez, mais je pense que c'est proche. C'est vraiment utile.

Cela ressemble à une combinaison de GoF Builder, Prototype et peut-être poids plume pour moi.

Cela ressemble à une variété de poids mouche (de nombreuses voitures partagent un moteur Wankel). Mais comment cela a-t-il un sens? La plupart des voitures ont un moteur, mais comment beaucoup peuvent-elles avoir la même instance de moteur? Ils n'iraient pas loin de cette façon. Ou voulez-vous dire que beaucoup de voitures ont un moteur de type WankelEngine? Spose qui fait plus de sens. Alors, à quoi sert "l'objet de définition WankelEngine"? Est-ce une usine qui crée des goûts de cet objet et les renvoie au demandeur? Si tel n'est pas le cas, cela ne ressemble pas à un objet de définition, mais plutôt à une usine qui prend en compte les paramètres de l'objet à construire et le lui rend.

Je vois ici quelques bonnes pratiques GoF, en particulier que vous composez au lieu d’hériter (ma voiture a un moteur et son moteur est un moteur Wankel). J'aimerais pouvoir rappeler la citation exactement, mais c'est quelque chose comme "l'héritage casse l'encapsulation". et "favoriser la composition par rapport à l'héritage".

Je suis curieux de savoir quel problème est résolu par ceci. Je pense que vous avez ajouté beaucoup de complexité et je ne vois pas le besoin d'une telle complexité. Peut-être que c'est quelque chose de spécifique à votre langue que je ne comprends pas.

Les gars du GoF discutent de la composition de motifs en motifs plus larges, MVC en particulier étant un agrégat de trois autres motifs. On dirait que vous avez fait quelque chose comme ça.

Cela ressemble un peu à Service Locator, dans lequel vos poids plume sont enregistrés en tant que singletons.

Ce que vous avez est une carte, également appelée Dictionnaire. Vous avez juste une touche unique en lui faisant correspondre plusieurs clés avec un seul objet.

Pourquoi c'est une carte:

  • garder une clé
  • Utilisez la clé pour interroger une valeur dans une structure de données.

Vous pouvez trouver des implémentations dans:

  • STL (inclure: carte)
  • Java (importation: java.util.Dictionary)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top