Je cherche à créer un singleton en Delphi. Je l'ai fait avant d'utiliser des versions plus anciennes de Delphi, et j'ai fini par utiliser des variables globales (dans la section implémentation) et en utilisant l'initialisation et la finalisation pour prendre soin de l'instance. De plus, il n'y avait aucun moyen d'empêcher l'utilisateur de créer une instance car vous ne pouviez pas masquer le constructeur standard. Je me demandais si l'une des nouvelles fonctionnalités telles que les constructeurs de classe et les destructeurs, et les variables de classe (ok, pas si nouveau), peut-être génériques, pourraient aider à créer une classe singleton générique. Je n'ai pas encore réussi à créer quelque chose à ma satisfaction.Création d'un singleton dans Delphi en utilisant les nouvelles fonctionnalités de D2009 et D2010
Répondre
Il est possible de gérer en surchargeant les méthodes TRUE allocateur et deallocator à Delphes, NewInstance et FreeInstance.Les constructeurs et les destructeurs de Delphi ne s'initialisent et ne se finalisent respectivement, ils n'allouent pas ou ne libèrent pas la mémoire, alors essayer de cacher les constructeurs était toujours un peu erroné.
à-dire qu'il est possible de permettre l'utilisation gratuite de tout et de tous les constructeurs aussi longtemps que vous NewInstance redéfini tel qu'il ne revenait jamais une référence à une seule allocation de mémoire pour la classe. Mais essayer de faire respecter un modèle d'utilisation/comportement dans une classe de base est une erreur imho. Tous les patterns ne sont pas ou ne nécessitent pas de classes spécifiques pour encapsuler le pattern. Dans de tels cas, vous finissez par créer quelque chose qui est inutilement compliqué, et la complication attire les erreurs dans mon expérience et l'objet de l'exercice devient alors de trouver des failles dans la mise en œuvre du modèle et d'essayer de mettre en place défauts, plutôt que de poursuivre le travail pratique que la classe singleton était censée accomplir.
Il est loin, plus simple et plus efficace de documenter l'utilisation de la classe.
Documentation comme une technique pour la mise en œuvre de ce modèle a fonctionné parfaitement pendant 15 ans pour l'application et écran objets dans la VCL, par exemple, sans parler d'innombrables autres singletons que j'ai créé dans les années.
Pour un singleton, vous pouvez remplacer la méthode NewInstance. Et utilisez une variable de classe. Vous créez la variable au premier appel et renvoyez le pointeur vers la classe l'un l'autre appel.
Vous avez juste besoin de trouver quelque chose pour le détruire à la fin (probablement en utilisant la finalisation).
Dans Delphi 2010 de loin, la meilleure et la plus sûre consiste à utiliser constructeurs de classe. Voir here - lire en particulier le paragraphe appelé Amélioration de l'encapsulation.
HTH.
Il est dommage qu'ils n'aient pas pris la peine de documenter cette fonctionnalité correctement, ou l'inclure dans le "Quoi de neuf" dans l'aide. – IanH
Je préfère utiliser des interfaces lorsque j'ai besoin de singletons et cacher l'implémentation de l'interface dans la section implémentation.
bénéficie
- destruction automatique lorsque le programme se termine.
- Impossible de créer accidentellement un TMySingleton.
inconvénients
- Quelqu'un pourrait décider de mettre en œuvre IMySingleton lui-même.
Note: Je crois que l'utilisation de Singletons devrait être maintenue à un minimum absolu. Dans l'ensemble, les singletons sont un peu plus que des variables globales glorifiées. Si et quand vous commencez à tester votre code, ils deviennent une nuisance.
unit uSingleton;
interface
type
ISingleton = interface
['{8A449E4B-DEF9-400E-9C21-93DFA2D5F662}']
end;
function Singleton: ISingleton;
implementation
uses
SyncObjs;
type
TSingleton = class(TInterfacedObject, ISingleton);
var
Lock: TCriticalSection;
function Singleton: ISingleton;
const
_singleton: ISingleton = nil;
begin
if not Assigned(_singleton) then
begin
Lock.Acquire;
try
if not Assigned(_singleton) then
_singleton := TSingleton.Create();
finally
Lock.Release;
end;
end;
Result := _singleton;
end;
initialization
Lock := TCriticalSection.Create;
finalization
Lock.Free;
end.
+1 pour la partie italique sur les singletons. – mghie
@mghie: vous n'êtes pas d'accord sur la mise en œuvre du modèle? –
Je suis d'accord avec vous que la plupart des utilisations du modèle Singleton ne sont pas un progrès par rapport aux variables globales. Mais si l'on veut les avoir (pour une raison quelconque), alors une solution générique sans danger pour les threads serait nécessaire. Le vôtre n'est certainement pas, de sorte que vous pouvez finir avec plus d'une instance. Au moins, il ne fuit pas la mémoire, quelque chose que j'ai vu dans les grandes bibliothèques commerciales où les objets sont utilisés pour l'implémentation singleton au lieu des interfaces. La solution de Moritz semble être sûre pour les threads, mais cela devrait être vérifié minutieusement. – mghie
Si vous avez juste besoin d'un singleton simple, la façon la plus simple est d'utiliser les constructeurs de classe et les méthodes de classe comme suggéré par plainth. Mais les génériques sont très utiles si vous avez besoin de singletons avec construction à la demande (c'est-à-dire en premier accès).
Le code suivant provient d'une de mes unités utilitaires; il fournit essentiellement une usine générique singleton pour Delphi 2009 et au-delà.
interface
type
{$HINTS OFF}
{ TSingletonInstance<> implements lazy creation, which is sometimes useful for avoiding
expensive initialization operations.
If you do not require lazy creation and you target only Delphi 2010 onwards, you should
use class constructors and class destructors instead to implement singletons. }
TSingletonInstance<T: class, constructor> = record
private
FGuard: IInterface;
FInstance: T;
function GetInstance: T;
function CreateInstance: TObject;
public
property Instance: T read GetInstance;
end;
{$HINTS ON}
TSingletonFactoryFunction = function: TObject of object;
{ Private symbols (which are in the interface section because of known limitations of generics) }
procedure _AllocateSingletonInstance (InstanceRecord: Pointer; Factory: TSingletonFactoryFunction);
implementation
{ TSingleton }
var
SingletonCriticalSection: TRTLCriticalSection;
type
TSingletonGuard = class (TInterfacedObject)
private
FSingletonInstance: TObject;
public
constructor Create (AInstance: TObject);
destructor Destroy; override;
end;
PUntypedSingletonInstance = ^TUntypedSingletonInstance;
TUntypedSingletonInstance = record
FGuard: IInterface;
FInstance: TObject;
end;
// TODO: is a lock required for multiple threads accessing a single interface variable?
procedure _AllocateSingletonInstance (InstanceRecord: Pointer; Factory: TSingletonFactoryFunction);
var
USI: PUntypedSingletonInstance;
begin
USI := PUntypedSingletonInstance (InstanceRecord);
EnterCriticalSection (SingletonCriticalSection);
if USI.FInstance = nil then
begin
USI.FInstance := Factory();
USI.FGuard := TSingletonGuard.Create (USI.FInstance);
end;
LeaveCriticalSection (SingletonCriticalSection);
end;
constructor TSingletonGuard.Create (AInstance: TObject);
begin
FSingletonInstance := AInstance;
end;
destructor TSingletonGuard.Destroy;
begin
FSingletonInstance.Free;
inherited;
end;
function TSingletonInstance<T>.GetInstance: T;
var
Factory: TSingletonFactoryFunction;
begin
if FInstance = nil then
begin
Factory := Self.CreateInstance; // TODO: associate QC report
_AllocateSingletonInstance (@Self, Factory);
end;
Result := FInstance;
end;
function TSingletonInstance<T>.CreateInstance: TObject;
begin
Result := T.Create;
end;
initialization
InitializeCriticalSection (SingletonCriticalSection);
finalization
DeleteCriticalSection (SingletonCriticalSection);
Utilisation comme suit:
type
TMySingleton = class
public
constructor Create;
class function Get: TMySingleton; static;
end;
var
MySingletonInstance: TSingletonInstance<TMySingleton>;
class function TMySingleton.Get: TMySingleton;
begin
Result := MySingletonInstance.Instance;
end;
Bon code Moritz. –
Merci. En fait, il serait plus joli si je pouvais supprimer les solutions de contournement pour D2009 :) –
Je préfère créer une classe singleton en utilisant un générateur de code. Le problème avec générique est que tout le code est généré en mémoire, pas dans le fichier source. Cela augmentera la difficulté du débogage.
Il existe un moyen de masquer le constructeur "Create" hérité de TObject. Bien qu'il ne soit pas possible de changer le niveau d'accès, il peut être caché avec une autre méthode publique sans paramètre avec le même nom: "Créer". Cela simplifie énormément l'implémentation de la classe Singleton. Voir la simplicité du code:
unit Singleton;
interface
type
TSingleton = class
private
class var _instance: TSingleton;
public
//Global point of access to the unique instance
class function Create: TSingleton;
destructor Destroy; override;
end;
implementation
{ TSingleton }
class function TSingleton.Create: TSingleton;
begin
if (_instance = nil) then
_instance:= inherited Create as Self;
result:= _instance;
end;
destructor TSingleton.Destroy;
begin
_instance:= nil;
inherited;
end;
end.
J'ai ajouté les détails à mon message original: http://www.yanniel.info/2010/10/singleton-pattern-delphi.html
- 1. Nouvelles fonctionnalités de WPF 4?
- 2. Gestion des nouvelles fonctionnalités de bibliothèque dans les anciennes applications
- 3. Composant de base de données pour Firebird utilisant D2010
- 4. Création d'une base de données en utilisant DBExpress dans Delphi?
- 5. Quelles seront les nouvelles fonctionnalités disponibles dans ASP.Net 4.0?
- 6. Problème de création Singleton C++
- 7. Résumé des nouvelles fonctionnalités de WCF 2.0?
- 8. API en développement: l'équilibre entre les nouvelles fonctionnalités et compatibilité arrière
- 9. Quelles sont les nouvelles fonctionnalités du navigateur disponibles aujourd'hui?
- 10. Création d'une fabrique singleton
- 11. Comment gérer les listes d'enfants et obtenir de nouvelles listes d'objets en C# en utilisant LINQ?
- 12. Tout organisme dispose d'une liste de nouvelles fonctionnalités flex 4?
- 13. Création d'un singleton de toute classe donnée en javascript
- 14. Création de produit basé sur les fonctionnalités d'Eclipse RCP
- 15. Création de nouvelles lignes modifiables dans une table
- 16. Conversion de projets qui utilisent des images Png en D2009
- 17. Comment Delphi 2009 convertit les projets Delphi 7 pour les configurations de construction
- 18. D2010 Beta: la façon parfaite de prendre en charge le multicœur
- 19. Modification du comportement de TStringStream.ReadString dans D2009?
- 20. Création de nouvelles commandes SQLC ou réutilisation de celles-ci
- 21. Comment télécharger une page de publication en utilisant des fonctionnalités?
- 22. "Les bases de Delphi" dans Delphi 2009
- 23. Quelles sont les nouvelles fonctionnalités du sql server 2005 de t-sql?
- 24. Ninject et Singleton
- 25. Création d'un descendant TCustomComboBox dans Delphi
- 26. Quel serait le meilleur workflow pour faire des refactorings mêlés à de nouvelles fonctionnalités dans Git?
- 27. PNG dans Delphi 2009 Imagelists et images
- 28. Verrouiller les fonctionnalités Word et PowerPoint
- 29. Singleton jetable en C#
- 30. Où puis-je soumettre une demande de nouvelles fonctionnalités dans la langue C#?
ou une destructor de classe D2010. –