Question

Une application de base de données sur laquelle je travaille actuellement stocke toutes sortes de paramètres dans la base de données.La plupart de ces paramètres sont là pour personnaliser certaines règles métier, mais il y a aussi d'autres éléments.

L'application contient des objets qui effectuent spécifiquement une certaine tâche, par exemple un certain calcul complexe.Ces objets non-UI sont testés unitairement, mais doivent également accéder à un grand nombre de ces paramètres globaux.La façon dont nous avons implémenté cela actuellement consiste à attribuer aux objets des propriétés qui sont remplies par le contrôleur d'application au moment de l'exécution.Lors du test, nous créons les objets dans le test et remplissons les valeurs à tester (pas à partir de la base de données).

Cela fonctionne mieux, en tout cas bien mieux que d'avoir tous ces objets nécessitant une Paramètres object --- cela rend bien sûr les tests unitaires impossibles :) L'inconvénient peut être que vous devez parfois définir une douzaine de propriétés, ou que vous devez laisser ces propriétés « s'infiltrer » dans des sous-objets.

La question générale est donc:comment donner accès aux paramètres globaux de l'application dans vos projets, sans avoir besoin de variables globales, tout en étant capable de tester unitairement votre code ?Ce doit être un problème qui a été résolu des centaines de fois...

(Note:Je ne suis pas vraiment un programmeur expérimenté, comme vous l'aurez remarqué ;mais j'aime apprendre!Et bien sûr, j'ai déjà fait des recherches sur ce sujet, mais je recherche vraiment des expériences concrètes)

Était-ce utile?

La solution

Vous pouvez utiliser le modèle Martin Fowlers ServiceLocator.En php, cela pourrait ressembler à ceci :

class ServiceLocator {
  private static $soleInstance;
  private $globalSettings;

  public static function load($locator) {
    self::$soleInstance = $locator;
  }

  public static function globalSettings() {
    if (!isset(self::$soleInstance->globalSettings)) {
      self::$soleInstance->setGlobalSettings(new GlobalSettings());
    }
    return self::$soleInstance->globalSettings;
  }
}

Votre code de production initialise ensuite le localisateur de services comme ceci :

ServiceLocator::load(new ServiceLocator());

Dans votre code de test, vous insérez vos paramètres fictifs comme ceci :

ServiceLocator s = new ServiceLocator();
s->setGlobalSettings(new MockGlobalSettings());
ServiceLocator::load(s);

Il s'agit d'un référentiel de singletons qui peuvent être échangés à des fins de tests.

Autres conseils

J'aime modéliser mon accès à la configuration à partir du modèle Service Locator.Cela me donne un point unique pour obtenir n'importe quelle valeur de configuration dont j'ai besoin et en la plaçant en dehors de l'application dans une bibliothèque distincte, cela permet la réutilisation et la testabilité.Voici un exemple de code, je ne suis pas sûr du langage que vous utilisez, mais je l'ai écrit en C#.

Je crée d’abord une classe générique qui modélisera mon ConfigurationItem.

public class ConfigurationItem<T>
{
    private T item;

    public ConfigurationItem(T item)
    {
        this.item = item;
    }

    public T GetValue()
    {
        return item;
    }
}

Ensuite, je crée une classe qui expose des variables publiques statiques en lecture seule pour l'élément de configuration.Ici, je lis simplement les ConnectionStringSettings à partir d'un fichier de configuration, qui est juste du XML.Bien sûr, pour plus d’éléments, vous pouvez lire les valeurs depuis n’importe quelle source.

public class ConfigurationItems
{
    public static ConfigurationItem<ConnectionStringSettings> ConnectionSettings = new ConfigurationItem<ConnectionStringSettings>(RetrieveConnectionString());

    private static ConnectionStringSettings RetrieveConnectionString()
    {
        // In .Net, we store our connection string in the application/web config file.
        // We can access those values through the ConfigurationManager class.
        return ConfigurationManager.ConnectionStrings[ConfigurationManager.AppSettings["ConnectionKey"]];
    }
}

Ensuite, lorsque j'ai besoin d'un ConfigurationItem, je l'appelle comme ceci :

ConfigurationItems.ConnectionSettings.GetValue();

Et cela me renverra une valeur de type sécurisé, que je pourrai ensuite mettre en cache ou faire ce que je veux.

Voici un exemple de test :

[TestFixture]
public class ConfigurationItemsTest
{
    [Test]
    public void ShouldBeAbleToAccessConnectionStringSettings()
    {
        ConnectionStringSettings item = ConfigurationItems.ConnectionSettings.GetValue();
        Assert.IsNotNull(item);
    }
}

J'espère que cela t'aides.

Cela est généralement géré par un fichier ini ou un fichier de configuration XML.Ensuite, vous avez juste une classe qui lit le paramètre en cas de besoin.

.NET a cela intégré aux classes ConfigurationManager, mais c'est assez facile à implémenter, il suffit de lire des fichiers texte, de charger du XML dans DOM ou de les analyser manuellement dans le code.

Avoir des fichiers de configuration dans la base de données est acceptable, mais cela vous lie à la base de données et crée une dépendance supplémentaire pour votre application que les fichiers ini/xml résolvent.

J'ai fait ça:

public class MySettings
{
    public static double Setting1
        { get { return SettingsCache.Instance.GetDouble("Setting1"); } }

    public static string Setting2
        { get { return SettingsCache.Instance.GetString("Setting2"); } }
}

J'ai mis cela dans un module d'infrastructure distinct pour supprimer tout problème lié aux dépendances circulaires.
En faisant cela, je ne suis lié à aucune méthode de configuration spécifique et je n'ai aucune chaîne qui fait des ravages dans le code de mes applications.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top