Delphi-PRAXiS
Seite 2 von 4     12 34      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Interface richtig umgesetzt? (https://www.delphipraxis.net/183251-interface-richtig-umgesetzt.html)

TheMiller 23. Dez 2014 15:04

AW: Interface richtig umgesetzt?
 
Zitat:

Zitat von mjustin (Beitrag 1284558)
die konkrete Klasse wird im Programm nur an einer Stelle benötigt, um einen Interfacezeiger zu erhalten, ab diesem Punkt wird dann nur noch mit dem Interface gearbeitet.

Kannst du mir dafür ein konkretes Beispiel geben? Das ist genau das, was ich meine bzw. was ich nicht verstanden habe!

Danke

mjustin 23. Dez 2014 15:22

AW: Interface richtig umgesetzt?
 
Zitat:

Zitat von DJ-SPM (Beitrag 1284559)
Kannst du mir dafür ein konkretes Beispiel geben? Das ist genau das, was ich meine bzw. was ich nicht verstanden habe!

Angenommen in der Applikation ist eine MSMQBrokerClient Klasse als private Property "MyMSMQBrokerClient" erzeugt worden.

Diese eine Instanz wird dann in einer Factory Methode verwendet die eine Connection liefert:

Delphi-Quellcode:
function TMyApp.CreateConnection: IConnection;
begin
  Result := MyMSMQBrokerClient.CreateConnection;
end;
Und dann kann im Programm an beliebigen Stellen - die natürlich auf die MyApp Instanz zugreifen können - eine Connection erzeugt und geöffnet werden, zum Beispiel in einem Thread.

Delphi-Quellcode:
procedure TMyDoWorkThread.Execute;
var
  Conn: IConnection;
  Msg: string;
begin
  Conn := MyApp.CreateConnection;
  Conn.Start;

  while not Terminated do begin
    Msg := Conn.Receive(1000);
    // mach etwas mit der erhaltenen Nachricht...
  end;

  Conn.Stop;
end;
(Exceptionbehandlung für Verbindungsverlust etc. mal weggelassen).

TheMiller 23. Dez 2014 16:16

AW: Interface richtig umgesetzt?
 
Ok, verstanden.

Hab gerade etwas rumprobiert und gesehen, dass in deinem Beispiel MyMSMQBrokerClient.CreateConnection einen Zeiger auf IConnection zurückgibt (denke ich).

Doch ich bekomme den Inhalt für "CreateConnection" nicht hin. Was muss ich konrekt reinschreiben, um eine IConnection zurückzubekommen?

Danke für eure Geduld ;)

EDIT

Ich glaube, ich habe es!

Interfaces - Die Klasse TMyMQ wird gelöscht, da überflüssig (wie vermutet)

Delphi-Quellcode:
IMy_MQ = interface
...
...
end;

TMy_MSMQ = class(TInterfacedObject, IMy_MQ)
...
...
end;
und hier kommt in der programmweiten Verwendung der Knackpunkt. Es wird nicht TMy_MQ erstellt, sondern direkt eine Variable IMy_MQ, welche dann via TMy_MSMQ.Create mit dem Interfacezeiger gefüllt wird:

Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
var
  mq: IMy_MQ;
begin
  mq:=TMy_MSMQ.Create;
  mq.SendMessage;
end;
Richtig? Es funktioniert jedenfalls. Aber hatte meine erste Version ja auch ;)

alda 23. Dez 2014 17:59

AW: Interface richtig umgesetzt?
 
Sieht sauberer aus als am Anfang, es ist aber auch viel weniger Code :P

TheMiller 23. Dez 2014 18:09

AW: Interface richtig umgesetzt?
 
Das freut mich. Nun muss es auch nur richtig sein ;)

Es ergibt sich aber jetzt dadurch das Problem, dass ich dennoch viel eingeschränkter hinsichtlich Parameter bin.

Angenommen, mein Interface hat die Methode "Open". Mit dieser möchte ich entweder bei MS die Queue öffnen, oder eben bei ActiveMQ.

Nun fordert MSMQ bei dessen Open-Methode Parameter A, B, C, ActiveMQ fordert Parameter 1, 2.

Da ich nun durch

Delphi-Quellcode:
var
  mq: IMy_MQ;
begin
  mq:=TMy_MSMQ.Create;
nur Zugriff auf die Interface-Methoden habe, bleiben die Methoden/Felder von TMy_MSMQ verborgen. Nun stehe ich also vor dem Problem, dass ich nicht weiß, wie ich innerhalb meiner Open-Methode auf die verschiedenen Erfordernisse der jeweiligen Message-Queue Objekte eingehen kann.

EDIT:

Ich könnte natürlich auch einen Cast nach TMy_MSMQ durchführen, um auf die Methoden/Felder dieser Klasse zuzugreifen. So könnte ich Parameter setzen, die dann wieder in "Open" ausgelesen werden.

alda 23. Dez 2014 18:28

AW: Interface richtig umgesetzt?
 
Gut, wenn sich die Parameter so unterscheiden, dann hast Du zusätzlich zu der "allgemeinen" Schnittstelle auch noch speziellere Schnittstellen, mit entsprechenden Methoden.
Dann kannst Du das aber nicht sauber mit einem allgemeinen Typ abbilden (auch mit Klassen nicht). Du musst quasi schon VOR dem Verwenden wissen, ob es sich um eine MQ mit 2 oder mit 3 Parametern handelt.
Die Optionen die ich hier sehe sind also:
- Du versucht eine allgemeines "Open" zu definieren und die eine Klasse verwendet alle 3 Parameter, die anderen nur 2 (das ist aber nicht gerade die feine englische Art)
- zwei separate "Basis" Interfaces, einmal für das Open mit 3 Parametern, einmal mit 2 Parametern
- ein Basis-Interface mit zwei Spezialisierungen (abgeleitete Interfaces, jeweils mit der eigenen Open-Definition)

Allerdings kenne ich auch hier nicht Deinen kompletten Anwendungsfall. Des Weiteren stell ich mal die Frage: gibt es nicht bereits vorhandene Implementierungen im Netz ?

TheMiller 23. Dez 2014 18:47

AW: Interface richtig umgesetzt?
 
Vielen Dank für die Antworten.

Ausgangspunkt war folgender: Ich wusste nicht, welche MessageQueue-Lösung ich final im Produkt verwenden möchte. Für den Anfang reicht MSMQ. Vielleicht kommt irgendwann mal ein MAC-Client o.ä. dazu. Dann hab ich mit MSMQ schlechte Karten und müsste auf ActiveMQ (oder einen anderen) umsteigen. Daher riet mir Sir Rufo, ein Interface zu implementieren und später beim Umstieg nur einen winzigen Teil im Programm ändern müsste.

Daher ein Interface mit z.B. "Open" (welches die MessageQueue öffnet). MSMQ verlangt zwei Parameter. ActiveMQ mit Sicherheit ganz andere. Und darauf weiß ich gerade nicht zu reagieren, außer einen Typecast zum TMy_MSMQ-Objekt oder einem Array im Interface namens "Params", die vor dem Open (oder anderen Methoden) entsprechend gefüllt und in der Methode ausgelesen und wieder gelert, quasi als Container.

Wenn wir die Interface-Geschichte mal außen vor lassen: Mit der MSMQ komme ich nun in den Grundzügen klar, kann erstellen, lesen, schreiben etc.

Sir Rufo 23. Dez 2014 19:11

AW: Interface richtig umgesetzt?
 
Also wenn ich einen Brief versenden will, dann muss ich trotzdem nicht das Postamt öffnen, oder den Briefkasten aufschließen. Ich gehe einfach zu meinem Interface
Delphi-Quellcode:
IBriefPost
und sende den Brief ab mit
Delphi-Quellcode:
IBriefPost.Sende( Brief);
. Soll sich das Interface doch selber darum kümmern. ;)

TheMiller 23. Dez 2014 19:36

AW: Interface richtig umgesetzt?
 
Ich verstehe, was du damit sagen willst. Bleiben wir dabei: Es gibt die Post, TransoFlex und UPS. Bei allen kann ich noch Post fragen. Dafür habe ich IMessages.GetMessage.

Nun kann ich bei der Post auswählen, ob nur das Vorliegen neuer Post erfragt werden soll und wer das machen darf (Open MQ_RECEIVE_ACCESS, MQ_DENY_NONE), erfragen und gleich mitnehmen <bei Post löschen> aber nur spezielle Leute das machen dürfen (Open MQ_PEEK_ACCESS, MQ_DENY_RECEIVE_SHARE) etc.

Bei TransoFlex kann ich genau das gleiche machen, nur nicht festlegen, wer diese Aktionen durchführen darf.

UPS will garnicht genau wissen, was ich vorhabe, sondern ich kann mich selbst dort bedienen.

Wie soll ich darauf mit IMessage.GetMessage reagieren?

Ich habe als Zwischenlösung (nur damit ich weiter testen kann) ein Array mit Parametern angelegt:

Delphi-Quellcode:
procedure TMy_MSMQ.SetParam(Value: string);
begin
  SetLength(fParams, Succ(Length(fParams)));
  fParams[High(fParams)]:=Value;
end;

...
procedure TMy_MSMQ.QueueOpen
begin
  fQueue:=fQueueInfo.Open(StrToInt(fParams[0]), StrToInt(fParams[1])); //MQ_SEND_ACCESS / MQ_RECEIVE_ACCESS etc.
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  mq: IMy_MQ;
begin
  mq:=TMy_MSMQ.Create;

  //Verbinden
  mq.SetParam(IntToStr(MQ_RECEIVE_ACCESS));
  mq.SetParam(IntToStr(MQ_DENY_NONE));
  if (mq.QueueOpen('DIRECT=OS:.\Private$\Test')) then
    ShowMessage('Queue opened');

end;
Funktioniert, ist aber nicht optimal...

alda 25. Dez 2014 13:11

AW: Interface richtig umgesetzt?
 
Zitat:

Zitat von DJ-SPM (Beitrag 1284597)
Ich habe als Zwischenlösung (nur damit ich weiter testen kann) ein Array mit Parametern angelegt:

Delphi-Quellcode:
procedure TMy_MSMQ.SetParam(Value: string);
begin
  SetLength(fParams, Succ(Length(fParams)));
  fParams[High(fParams)]:=Value;
end;

...
procedure TMy_MSMQ.QueueOpen
begin
  fQueue:=fQueueInfo.Open(StrToInt(fParams[0]), StrToInt(fParams[1])); //MQ_SEND_ACCESS / MQ_RECEIVE_ACCESS etc.
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  mq: IMy_MQ;
begin
  mq:=TMy_MSMQ.Create;

  //Verbinden
  mq.SetParam(IntToStr(MQ_RECEIVE_ACCESS));
  mq.SetParam(IntToStr(MQ_DENY_NONE));
  if (mq.QueueOpen('DIRECT=OS:.\Private$\Test')) then
    ShowMessage('Queue opened');

end;
Funktioniert, ist aber nicht optimal...

Ja das ist wohl wahr, bin mir auch nicht sicher was hier der eleganteste Weg ist. Ich gehe jetzt einfach mal davon aus, dass es maximal 0-N Setup-Parameter für jede MessageQueue gibt, gemäß Deiner Beschreibung aus:
Zitat:

Zitat von DJ-SPM (Beitrag 1284597)
Nun kann ich bei der Post auswählen, ob nur das Vorliegen neuer Post erfragt werden soll und wer das machen darf (Open MQ_RECEIVE_ACCESS, MQ_DENY_NONE), erfragen und gleich mitnehmen <bei Post löschen> aber nur spezielle Leute das machen dürfen (Open MQ_PEEK_ACCESS, MQ_DENY_RECEIVE_SHARE) etc..

Erstmal zu Deinem Beispiel:
Das ist insofern unschön, da Du einen undefiniertem Wert übergibst den Du innerhalb der Open-Implementierung ja wieder auswerten musst- man sieht innerhalb der Open-Implementierung also gar nicht auf den ersten Blick was gemeint ist.

Alternative mit Parametern zu Deinem Beispiel:
Sofern Du dabei bleiben möchtest, ein MQ-Interface bereitzustellen mit einem allgemeingültigen Open-Aufruf, dann solltest Du die Parameter sogt wie möglich typisieren. Du hast ja überall definierte Zustände, das bedeutet alle "Rechte" kannst Du als Enums darstellen (wie Du diese letztendlich gestaltest überlass ich mal Dir), die man dann entsprechend auch einfach und leserlich auswerten kann. Wenn möglich könntest Du sogar ein großes Enum deklarieren und in der Open-Methode einfach ein Enum-Set verlangen, z.B:
Delphi-Quellcode:
TMQSetupType = (mqsReceiveAccess, mqsDenyNone, mqsPeekAccess, mqsDenyReceiveShare);
TMQSetupTypes = set of TMQSetupType;

IMyMQ= interface
...
    procedure Open(const ASetup: TMQSetupTypes);
end;

.....

procedure TMy_MSMQ.QueueOpen
begin
  fQueue:=fQueueInfo.Open([mqsSendAccess, mqsReceiveAccess]);
end;
Jede Implementierung entscheidet dann selbst, ob und wie die übergebenen Parameter verwertet werden. Der Vorteil ist, Du musst beim austauschen der MQ-Implementierung nicht den Open-Aufruf anpassen und siehst gleich am Namen des Enums um was für ein Recht es sich handelt und kannst entsprechend einfach darauf prüfen und es auswerten. Nachteil ist weiterhin , dass man hier keine Rückmeldung erhält, sofern gesetzte (Rechte)Parameter ignoriert werden.

Alternativen:
Du solltest Dir einfach mal die MQ-Implementierungen anderer Sprachen anschauen und herausfinden wie das dort gelöst wurde (der Anwendungsfall sollte ja exakt der gleiche sein) - vielleicht findet sich da ja eine elegante Lösung, oder jemand anderem im Forum fällt bis dahin was schickes ein.


Alle Zeitangaben in WEZ +1. Es ist jetzt 15:57 Uhr.
Seite 2 von 4     12 34      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz