2009-03-30 8 views
4

Lors de la conception de bibliothèques, je finis souvent par recourir au modèle suivant, que je n'aime pas car il en résulte beaucoup de types.
Éviter la diffusion lors du passage d'objets via le code de la bibliothèque (En Delphi)

Le modèle de base est:
Le code en utilisant les mains bibliothèque d'un objet à la bibliothèque, la bibliothèque de mains, puis l'objet Retour au code d'appel. Le code appelant est forcé de lancer l'objet, car la bibliothèque restitue un type générique. (Exemple de code Stripped-dessous)

La bibliothèque définit les objets suivants et la fonction:

TThing = Class 
End; 

TThingProcessor = Class 
Public 
    Function CreateThing : TThing; Virtual; Abstract; 
    Procedure ProcessThing (Thing : TThing); Virtual; Abstract; 
End; 

Procedure DoEverything (Processor : TThingProcessor); 

Le code d'appel utilise alors la bibliothèque en remplaçant les objets et appeler DoEverything, comme suit: -

TMyThing = Class(TThing) 
Public 
    X : Integer; 
End; 

TMyThingProcessor = Class(TThingProcessor) 
Public 
    XSum : Integer; 

    Function CreateThing : TThing; Override; 
    Procedure ProcessThing (Thing : TThing); Override; 
End; 

Function TMyThingProcessor.CreateThing : TThing; 
Begin 
    Result := TMyThing.Create; 
End; 

Procedure TMyThingProcessor.ProcessThing (Thing : TThing); 
Begin 
    XSum := XSum + (Thing As TMyThing).X; 
    //Here is the problem, the caller is forced to cast to do anything 
End; 

La classe de processeur est également une usine TThing. La bibliothèque garantit qu'elle transmettra TThings uniquement au TThingProcessor correspondant qui les a créés, donc cela fonctionne, mais n'est pas sécurisé. Bien que le code ci-dessus soit un peu stupide en ce sens qu'il ne fait vraiment rien, il montre pourquoi ProcessThing ne peut pas simplement être déplacé vers TThing et être polymorphe - la variable XSum doit être mise à jour.

Comment puis-je restructurer le code afin que la distribution ne soit pas nécessaire? Je dois garder le code de la bibliothèque séparé mais être capable d'accepter n'importe quel type.

Edit: Changé le disque à un casting en raison de la suggestion de coulée de sorte qu'il exception au moins deux pas au lieu de l'accident dans le cas des types dépareillées

+0

Je pense que http://stackoverflow.com/questions/681522/casting-between-parent-and-child-classes-in-delphi "pourrait avoir une incidence sur ce que vous essayez de faire – RobS

+0

Merci pour le lien, c'était une lecture utile.Je ne pense pas que cela s'applique directement, mais penser à une restructuration de la même manière me donne des idées, même si je n'ai pas encore pu les identifier. – David

Répondre

3

Utilisez-vous Delphi 2009? C'est un excellent usage pour les génériques. Changez vos déclarations à:

TThingProcessor<T: TThing> = Class 
Public 
    Function CreateThing : T; Virtual; Abstract; 
    Procedure ProcessThing (Thing : T); Virtual; Abstract; 
End; 


TMyThingProcessor = Class(TThingProcessor<TMyThing>) 
Public 
    XSum : Integer; 

    Function CreateThing : TMyThing; Override; 
    Procedure ProcessThing (Thing : TMyThing); Override; 
End; 

Fini la coulée.

+0

Je n'ai pas D2009 pour l'essayer, mais j'aime le look de celui-ci. Cela signifie-t-il que la bibliothèque doit être distribuée en tant que code source (impossible de créer une DLL, par exemple)? Aussi, je suppose que DoEverything doit être générique aussi - le compilateur en créerait-il une nouvelle version pour chaque type? – David

+0

Vous n'avez pas besoin de distribuer le code source. Les DCU fonctionneront certainement. Les paquets pourraient (n'ont pas essayé). Les DLL simples ne seront pas (ne peut pas partager même des objets non génériques avec une DLL normale). Je ne peux pas dire à propos de DoEverything sans voir l'implémentation. –

+0

Je pensais que DoEverything aurait besoin d'être générique juste à partir de sa déclaration parce que s'il était déclaré comme acceptant TThingProcessor , alors il n'accepterait pas les classes dérivées de TThingProcessor . Cette pensée vient des modèles C++, les génériques Delphi fonctionnent-ils différemment? – David

3

Vous devriez restructurer votre code. Si vous faites beaucoup de création de 'TThing's, alors il devrait avoir une classe d'ancêtre avec la méthode de processus irs déclarée comme virtuelle, alors vous travaillez simplement avec l'ancêtre. Généralement, il sera toujours possible de définir une «classe de base» commune lorsque vous effectuez des appels similaires à des classes très différentes. Si vous ne pouvez pas créer une structure de classe comme celle-ci, utilisez une interface pour définir vos besoins de traitement et attacher cette interface à votre TThing.

Dans le dernier recours où vous ne pouvez pas éviter un plâtre, utilisez ce qui suit pour faciliter la lecture et le débogage ...

procedure Something(ASender : TObject); 
var 
    MyThing : TMyThing; 
begin 
    MyThing := ASender as TMyThing; 
    MyTHing.DoSomething; 
    MyThing.DoSomethingElse; 
end; 
+0

J'ai généralement besoin de tous les TThings pour accéder à des données communes stockées dans TThingProcessor. (dans ce cas XSum), donc je ne peux pas mettre la fonction de processus dans TThing. Plus généralement, tout ajout à TThing (comme la connexion d'une interface) rend la bibliothèque moins générique - ce que je souhaite éviter. – David

+0

Une autre façon d'y parvenir est d'utiliser "absolu" var MyThing: TMyThing absolute Sender; commencez si l'expéditeur est TMyThing alors .... –

1

Si TMyThingProcessor n'accepte que des objets et des objets TMyThing TThing de base ne fonctionnent pas, alors n'essayez pas de le faire de façon polymorphe. Déclarer ProcessThing avec un reintroduce; à la place d'une priorité ; directive.

En outre, si vous l'avez, les fonctionnalités Generics de Delphi 2009 aident beaucoup à réduire les falsifications de polices dans certaines situations.

+0

Si TMyThingProcessor.ProcessThing a été déclaré réintroduire alors DoEverything appellera toujours la base TThingProcessor.ProcessThing, ce qui n'est pas ce que je veux. Je n'ai malheureusement pas D2009, mais je suis ouvert aux solutions qui l'utilisent, car je m'intéresse plus à la structure du code qu'à la spécificité du langage. – David

+0

Oh, je vois ce que vous voulez dire. Dans ce cas, vous êtes coincé. Vous essayez de mélanger le polymorphisme (littéralement, "many-forms-ism") avec quelque chose qui fonctionne uniquement avec une forme * single *, et ils ne s'accordent pas avec vous de faire un peu de travail manuellement. –

Questions connexes