33 votes

Comment implémenter un modèle singleton

J'ai un site dans rails et je veux avoir des paramètres pour tout le site. Une partie de mon application peut notifier l'administrateur par SMS si un événement spécifique se produit. Il s'agit d'un exemple de fonctionnalité que je souhaite configurer via les paramètres du site.

Alors je me suis dit que je devrais avoir un modèle de réglage ou quelque chose comme ça. Il doit s'agir d'un modèle car je veux pouvoir utiliser la fonction has_many :contacts pour la notification par SMS.

Le problème est qu'il ne peut y avoir qu'un seul message dans la base de données pour le modèle de paramètres. J'ai donc pensé à utiliser un modèle Singleton, mais cela ne fait qu'empêcher la création d'un nouvel objet, n'est-ce pas ?

Devrais-je encore créer des méthodes getter et setter pour chaque attribut, comme suit ?

def self.attribute=(param)
  Model.first.attribute = param
end

def self.attribute
  Model.first.attribute
end

N'est-il pas préférable de ne pas utiliser directement Model.attribute mais de toujours créer une instance de celui-ci et de l'utiliser ?

Que dois-je faire ici ?

3voto

JGeiser Points 43

Il y a de fortes chances que vous n'ayez pas besoin d'un singleton. Il est regrettable que l'une des pires habitudes de conception issues de l'engouement pour les patterns soit aussi l'une des plus communément adoptées. Je blâme l'apparence malheureuse de la simplicité, mais je m'égare. S'ils l'avaient appelé le pattern "Static Global", je suis sûr que les gens auraient été plus réticents à l'utiliser.

Je suggère d'utiliser une classe d'enveloppe avec une instance statique privée de la classe que vous voulez utiliser pour le singleton. Vous n'introduirez pas un couple serré à travers votre code comme vous le ferez avec le singleton.

Certaines personnes appellent cela un modèle monostate. J'ai tendance à considérer qu'il s'agit d'une autre variante du concept de stratégie/agent, car il est possible d'offrir une plus grande souplesse en mettant en œuvre différentes interfaces pour exposer ou masquer les fonctionnalités.

2voto

Lichtamberg Points 6221

Simple :

class AppSettings < ActiveRecord::Base 
  before_create do
    self.errors.add(:base, "already one setting object existing") and return false if AppSettings.exists?      
  end

  def self.instance
    AppSettings.first_or_create!(...) 
  end 
end

2voto

ka8725 Points 1074

Je vais faire quelques remarques aux réponses précédentes :

  • Il n'est pas nécessaire d'avoir un champ séparé pour l'indice uniq, nous pouvons mettre la contrainte sur le champ de l'indice uniq. id champ. Puisque nous devrions avoir id dans une application Rails, c'est un bon compromis.
  • pas besoin de validations spéciales sur le modèle et l'attribution explicite d'ID, il suffit de mettre une valeur par défaut sur la colonne.

Si nous appliquons ces modifications, la solution devient très facile :

# migration
create_table :settings, id: false do |t|
  t.integer :id, null: false, primary_key: true, default: 1, index: {unique: true}
  t.integer :setting1
  t.integer :setting2
  ...
end

# model
class Settings < ApplicationRecord
  def self.instance
    first_or_create!(...)
  end  
end

1voto

Otto Points 5166

Utilisation de has_many :contacts ne signifie pas que vous avez besoin d'un modèle. has_many fait un peu de magie, mais au final, il s'agit simplement d'ajouter une méthode avec un contrat spécifique. Il n'y a aucune raison pour que vous ne puissiez pas implémenter ces méthodes (ou un sous-ensemble dont vous avez besoin) pour que votre modèle se comporte comme lui. has_many :contacts n'utilise pas encore réellement un modèle ActiveRecord (ou un modèle tout court) pour Contact.

1voto

Michael Points 1

Vous pouvez également consulter Configatron :

http://configatron.mackframework.com/

Configatron rend la configuration de vos applications et scripts incroyablement facile. Il n'est plus nécessaire d'utiliser des constantes ou des variables globales. Maintenant, vous pouvez utiliser un système simple et sans douleur pour configurer votre vie. Et, parce que c'est tout Ruby, vous pouvez faire toutes les choses folles que vous voulez !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X