Delphi-PRAXiS

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 13:07

Delphi-Version: 5

Interface richtig umgesetzt?
 
Hallo, auf Grund eines anderen Threads, wurde mir vorgeschlagen, ich solle zur Umsetzung eines Vorhabens ein Interface entwickeln. Ich möchte mit MessageQueues arbeiten, mir aber offen halten, welchen Broker ich benutze bzw. in Zukunft benutzen will. Nun habe ich ein Interface entwickelt und soweit alles umgesetzt. Das Wechseln zwischen Brokern geht auch. Nun wollte ich dennoch wissen, ob ich alles richtig umgesetzt habe. Vielleicht mache ich mir dennoch zu viel Arbeit da man es auch einfacher Schreiben kann.

Meine Vorgehensweise:

1. Interface deklarieren
2. TInterfacedObject von Interface ableiten (für MSMQ) und Methoden erstellen
3. TInterfacedObject von Interface ableiten (für ActiveMQ) und Methoden erstellen
4. Eigenes Objekt erstellen, welches die gleichen Methoden hat, wie das Interface. So kann ich dieses Objekt programmweit benutzen. Intern ruft es die Methode des jeweils benötigten InterfaceObjects auf.

Vielleicht blöd erklärt, daher hier nochmal ganz knapp der Quelltext:

1. Eigenständige Unit für Interfaces & Co.

Delphi-Quellcode:
  //Das Interface
  IMy_MQ = interface
  ['{5C3F02B1-2E73-4500-9CD3-02EAFEDB332C}']
    function CreateQueue(path: String): Boolean;
  end;

  TMQBroker = (mqMS, mqActiveMQ);

  //Objekt für Microsoft MessageQueue
  TMy_MSMQ = class(TInterfacedObject, IMy_MQ)
    function CreateQueue(path: String): Boolean;
  end;

  //Objekt für Apache ActiveMQ
  TMy_ActiveMQ = class(TInterfacedObject, IMy_MQ)
    function CreateQueue(path: String): Boolean;
  end;

  //Objekt, welches programmweit benutzt wird
  TMy_MQ = class(TObject)
  private
    fBroker: TMQBroker;
    fMQ: IMy_MQ;
  public
    constructor Create(mqBroker: TMQBroker = mqMS);
    destructor Destroy;
    function CreateQueue(Path: String): Boolean;
    property Broker: TMQBroker read fBroker write fBroker;
  end;

//////////

function TMy_MSMQ.CreateQueue(path: string): Boolean;
begin
  ShowMessage('MSMQ '+path);
end;


function TMy_ActiveMQ.CreateQueue(path: string): Boolean;
begin
  ShowMessage('ActiveMQ '+path);
end;

constructor TMy_MQ.Create(mqBroker: TMQBroker = mqMS);
begin
  fBroker:=mqBroker;

  if (mqBroker = mqMS) then
  begin
    fMQ:=TMy_MSMQ.Create;
  end else if (mqBroker = mqActiveMQ) then
  begin
    fMQ:=TMy_ActiveMQ.Create;
  end;
end;
Programmiert wird dann nur noch mit Hilfe von TMy_MQ in folgender Form

Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
var
  sacom_mq: TSacom_MQ;
begin
  Sacom_MQ:=TSacom_MQ.Create(mqActiveMQ); //oder halt eben mqMS
  Sacom_MQ.CreateQueue('test');
  Sacom_MQ.Destroy;
end;
Soll der Broker gewechselt werden, einfach im Construkter den gewünschten eintragen.

Macht man das so, oder kann ich mir den Weg über TMy_MQ irgendwie ersparen?

Danke sehr!

alda 23. Dez 2014 13:15

AW: Interface richtig umgesetzt?
 
Also wenn das Dein Code ist, dann stimmt etwas vorne und hinten nicht (oder ich verstehe einfach nicht :D).
Warum implementiert eine MessageQueue Klasse das MessageQueue-Interface, die andere aber nicht? Der Enum im Create der MessageQueue ist auch unschön - hier müsstest mit einem neuen Broker jedes mal den Konstruktiv anfassen. Um Dir korrekt helfen zu können wäre es schön, alles erstmal korrekt zu benennen (den Broker erkennt man nämlich nicht am Namen ;-)).

Und zu den Begrifflichkeiten: Eine Klasse leitet oder erbt von anderen Klassen. Interfaces werden von einer Klasse implementiert (da ja nur die Schnittstelle vorgegeben wird) - sie leitet also nicht vom Interfaces ab.

mjustin 23. Dez 2014 13:29

AW: Interface richtig umgesetzt?
 
Mein Vorschlag (und meine Präferenz) wäre es, für jeden Broker eine konkrete Implementierungsklasse zu bauen die ein allgemeines Interfaces unterstützt.
Minimales Beispiel:
Delphi-Quellcode:
IMessageBrokerClient = interface
...
end;

TActiveMQClient = class(TInterfacedObject, IMessageBrokerClient)
...
end;

TMSMQClient = class(TInterfacedObject, IMessageBrokerClient)
...
end;
Irgendwann muss man sich dann für die konkrete Klasse entscheiden:
Delphi-Quellcode:
var
  MyClient: IMessageBrokerClient;
begin
  MyClient := TActiveMQClient.Create;
  ...
end;
Die Clientklassen können natürlich auch von einer abstrakten Basisklasse abgeleitet sind in der allgemeine Dinge und Properties untergebracht sind, die für alle Broker genutzt oder unterstützt werden:

Delphi-Quellcode:
TAbstractClient = class(TInterfacedObject, IMessageBrokerClient)
...
end;
 
TActiveMQClient = class(TAbstractClient, IMessageBrokerClient)
...
end;
Nachtrag: die verschiedenen Lösungen beim Einsatz von Interfaces unterscheiden sich oft auch darin, welche Delphi Units konkret einkompiliert werden. Wenn man eine Klasse pro Broker hat, wird nur diese dem Projekt hinzugefügt. Wenn man eine Klasse hat über deren Konstruktorargument man den Broker auswählen kann, dann muss in der Unit dieser Klasse Code für jeden unterstützten Broker enthalten sein. Dadurch würde man - ausser durch Verwendung von einigen IFDEFs - mehr in die Anwendung packen als man braucht.

TheMiller 23. Dez 2014 13:44

AW: Interface richtig umgesetzt?
 
Danke.

@mjustin: Mache ich mir da nicht die Flexibilität kaputt, die ich durch das Interface erreichen wollte?

In meinem Beispiel müsste ich bei einem Brokerwechsel nur eine Zeile für die gesamte Anwendung ändern. Bei deiner Lösung müsste ich ein komplette Objekte tauschen. Warum nutzt du in diesem Fall ein Interface?

Vielleicht übersehe ich auch was wegen meinem Schnupfkopf ;)

alda 23. Dez 2014 13:52

AW: Interface richtig umgesetzt?
 
Zitat:

Zitat von DJ-SPM (Beitrag 1284544)
@mjustin: Mache ich mir da nicht die Flexibilität kaputt, die ich durch das Interface erreichen wollte?

Das ist der Sinn eines Interfaces - ich kann beliebige Implementierungen (Klassen) austauschen. Dein Ansatz das innerhalb des Konstruktors
zu machen ist unschön und nimmt dir die Flexibilität, da Du diese Klasse nun doch an die Implementierung (TActiveMQ.Create) koppelst, obwohl Du ein Interfaces dafür hast (IMy_MQ). Der korrekte Ansatz wäre hier, wenn überhaupt, eine fertige MessageQueue mit in den Konstruktiv zu übergeben (IMY_MQ) und somit diese Klasse von der Implementierung von IMy_MQ zu entkoppeln.

Zitat:

Zitat von DJ-SPM (Beitrag 1284544)
In meinem Beispiel müsste ich bei einem Brokerwechsel nur eine Zeile für die gesamte Anwendung ändern. Bei deiner Lösung müsste ich ein komplette Objekte tauschen. Warum nutzt du in diesem Fall ein Interface?

In seinem Beispiel wäre es genau so, nur dass die "Zeile" von der Du redest nicht innerhalb einer MQ-Klasse ist, sondern außerhalb von einer Factory oder sonstwem übernommen wird.

Um das Beispiel von mjustin zu vollenden, hättest Du z.B. noch einen TMessageQueueProvider (oder wie auch immer man das nennen möchte:
Delphi-Quellcode:
IMessageQueueProvider = interface
....
   function CreateMessageQueue(const AMessageBrokerType: TMessageBrokerType): IMessageBrokerClient;
end;

TMessageQueueProvider = class(TInterfacedObject, IMessageQueueProvider)
public
   function CreateMessageQueue(const AMessageBrokerType: TMessageBrokerType): IMessageBrokerClient;
end;

....

implementation

function TMessageQueueProvider .CreateMessageQueue(const AMessageBrokerType: TMessageBrokerType): IMessageBrokerClient;
begin
   case AMessageBrokerType:
      mqMS:
         Result := TMy_MSMQ.Create;
      msAtiveMQ:
         Result := TActiveMQ.Create;
      else
         raise EMessageQueueBrokerNotSupportedError.Create('');
end;
....

mjustin 23. Dez 2014 14:19

AW: Interface richtig umgesetzt?
 
Zitat:

Zitat von alda (Beitrag 1284545)
Delphi-Quellcode:
   case AMessageBrokerType:
      mqMS:
         Result := TMy_MSMQ.Create;
      msAtiveMQ:
         Result := TActiveMQ.Create;
      else
         raise EMessageQueueBrokerNotSupportedError.Create('');

In einem Edit meines Beitrags habe ich angemerkt, dass diese Flexibilität auch einen Preis, nämlich eine entsprechend größere Anwendungsdatei hat, da auch die Implementierungs-Klassen für alle unbenutzten Message Broker eingebunden werden.
(Man könnte aber durch Einsatz von IFDEF verschiedene Versionen der Anwendung erstellen, die je nach Bedarf nur einen Message Broker oder eine Auswahl (zwei bis N) unterstützen.)

Dejan Vu 23. Dez 2014 14:31

AW: Interface richtig umgesetzt?
 
Wie willst Du mit diesem Interface eigentlich einen TCP-Client umsetzen?

Du musst anders herum anfangen: Beschreibe die Funktionalität und die Methoden, die Du für die Kommunikation benötigst, ohne die Begriffe TCP, UDP, MSMQ, Schnur, RS-232 und Buschtrommel zu verwenden.

Also: Mein Client soll sich immer nur mit einem konkreten Server unterhalten. Dafür brauche ich eine 'Connect' (muss die wirklich Connect heißen) Routine. Oder besser (und allgemeiner) : StartConnection.

Dann will ich dem eine Nachricht als String schicken können (SendMessage).
Dann will ich noch eine Anfrage schicken und auf das Ergebnis (auch ein String) warten. Aber nur maximal X Sekunden. (QueryResponse)

Und zu guter Letzt möchte ich noch, das der Client wieder offen für einen anderen Server ist (StopConnection)

Fertig ist das allgemeine Client-Interface
Delphi-Quellcode:
Type
  IClientConnection = Interface
    procedure StartConnection (server : IServer);
    procedure SendMessage (message : String);
    procedure QueryResponse (query : String; var response : String; timeout : Integer);
    procedure StopConnection;
  end;
Fertig ist das Interface. Für die Nachrichten könntest Du nun auch noch ein allgemeines Interface bauen, aber so geht es erst einmal. Natürlich fehlt noch z.B. die Stream-Funktion, aber das kannst Du ja später machen.

Schreibe nun eine konkrete TMSMQClientConnection-Klasse, die das IClientConnection-Interface implementiert.
Danach schreibst Du noch ein TTCPClientConnection-Klasse, die auch dieses Interface implementiert.
Dann noch eine RS-232-Klasse
Und eine Consolen-Klasse: SendMEssage => WriteLn und QueryResponse = 'WriteLn' und 'ReadLn'... Sehr schön zum testen.
Und eine Buschtrommel-Klasse mit Mikrofon, Lautsprecher und Samples.
Und eine LTE-Klasse
Und.
Und.
Und.

Deine Anwendungen, die dieses Interface verwenden, werden auch mit einer LTE-4G-Astromedial-Klasse funktionieren. Und, besser noch: Sie müssen noch nicht einmal neu kompiliert werden (wenn man die Klasse per Plugin aus einer DLL lädt, z.B.)

Zitat:

Zitat von mjustin (Beitrag 1284549)
In einem Edit meines Beitrags habe ich angemerkt, dass diese Flexibilität auch einen Preis, nämlich eine entsprechend größere Anwendungsdatei hat, da auch die Implementierungs-Klassen für alle unbenutzten Message Broker eingebunden werden.
(Man könnte aber durch Einsatz von IFDEF verschiedene Versionen der Anwendung erstellen, die je nach Bedarf nur einen Message Broker oder eine Auswahl (zwei bis N) unterstützen.)

Dann doch lieber Plug-Ins.

mjustin 23. Dez 2014 14:51

AW: Interface richtig umgesetzt?
 
Zitat:

Zitat von Dejan Vu (Beitrag 1284551)
Fertig ist das allgemeine Client-Interface
Delphi-Quellcode:
Type
  IClientConnection = Interface
    procedure StartConnection (server : IServer);
    procedure SendMessage (message : String);
    procedure QueryResponse (query : String; var response : String; timeout : Integer);
    procedure StopConnection;
  end;

Oder auch so:
Delphi-Quellcode:
type
  IConnection = interface
    procedure Start;
    procedure Send(AMessage: string);
    function Receive(ATimeout: Integer): string;
    procedure Stop;
  end;

  IMessageBrokerClient = interface
    function CreateConnection: IConnection;
    ...
  end;
Im Interface fehlt noch die Angabe des Queuenamens (oder allgemeiner: Destinationnamens, da es neben Queues auch Topics oder Channel gibt, die Begriffe sind nicht einheitlich über alle Broker). Man könnte den Destinationnamen zum Beispiel als Argument beim Erzeugen der Connection übergeben.

TheMiller 23. Dez 2014 14:53

AW: Interface richtig umgesetzt?
 
Hi,

Dejan Vu, das habe ich soweit verstanden. So habe ich es auch umgesetzt (nur, dass bei mir erstmal alles auf Broker zugeschnitten ist, da ich TCP verworfen habe. Aber okay, lassen wir es mal bei deiner Skizze).

Ich dachte, dass das Interface mir ermöglicht, dass ich meinen Quelltext nicht ändern muss, wenn ich den Broker tausche (in meinem Fall). Also, dass ich im Programm selbst nur gegen TAllgemeineKlasse programmiere, welche dann im Hintergrund die geforderten Broker benutzt.

In deinem Beispiel muss ich ja dann jeden Aufruf von TTCPClientConnection in TMSMQClientConnection ändern. Und ich dachte, dass mir das Interfaces ersparen. Daher habe ich meine Klasse TMy_MQ erstellt, gegen die ich programmiere.

Es kanna aber auch wirklich sein, dass ich das mit den Interfaces noch missverstehe.

mjustin 23. Dez 2014 15:01

AW: Interface richtig umgesetzt?
 
Zitat:

Zitat von DJ-SPM (Beitrag 1284556)
In deinem Beispiel muss ich ja dann jeden Aufruf von TTCPClientConnection in TMSMQClientConnection ändern. Und ich dachte, dass mir das Interfaces ersparen.

Das ist sicher ein Missverständnis: 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.

Falls im Programm an verschiedenen Stellen eine neue Instanz der Klasse benötigt wird, kann man die Erzeugung in eine zentrale Factorymethode auslagern. Auch dann wird nur ein Mal die konkrete Klasse eingebunden.

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.

Sir Rufo 25. Dez 2014 14:46

AW: Interface richtig umgesetzt?
 
Es ist doch unsinnig die Open Methode im Interface zu deklarieren. Wozu soll die gut sein?

Ich will eine Nachricht senden. Ob dafür bei MSMQ erst die Queue erst mit welchen Parametern auch immer geöffnet werden muss oder bei TrallalaMQ erst ein Purzelbaum geschlagen werden muss, das interssiert bei der konkreten Implementierung aber nicht das Interface. Das ermöglicht mir das Senden einer Nachricht. Das Wie regelt die Implementierung.

Das Empfangen geht genauso: Was alles gemacht werden und erfolgen muss, darum kümmert sich die Implementierung. Das Interface hat z.B. 2 Methoden HasMessage und GetMessage.

Wenn man das Interface zu konkret aufbaut, dann arbeitet man am Interface-Gedanken vorbei und kann sich das auch gleich sparen.

TheMiller 25. Dez 2014 19:44

AW: Interface richtig umgesetzt?
 
Hi,

erstmal vorab: Ich wünsche euch allen eine frohe Weihnachtszeit!

Ich nutze gerade mal die Gelegenheit zwischen den Essen und Familienfeiern, auf die Nachricht hier zu antworten:

Das Problem habe ich verstanden. Nur da ich ja als Rückgabeparameter das Interface erhalte, sehe ich auch nur die Mehtoden, die es hat, nicht aber die der TMy_MSMQ-Klasse. Würde ich jetzt aber im ganzen Programm mit der TMY_MSMQ-Klasse arbeiten, so wäre ich wieder an dieses Objekt gebunden und der gewünschte Vorteil des Interfaces wäre nicht mehr gegeben. Ebenso, wenn ich das den Interface-Zeiger nach TMy_MSMQ caste. Ich muss da noch irgendwas falsch verstehen...

Ich wüsste also derzeit nicht, wo ich die Open-Methode deklarieren könnte, damit ich mit ihr arbeiten kann. Entweder darf ich mir kein Interface-Zeiger zurückgeben lassen, dann könnte ich eben mit der My_MSMQ.Open arbeiten, bin dann aber programmweit an sie gebunden. Oder ich nutze das Interface und habe eben diese Parameter-Probleme. So ist derzeit meine Sicht - ich weiß, da ist was falsch, aber ich komme nicht drauf was.

Oder gibt es für mein konkretes Vorhaben nicht diese Lösung, dass ich bei einem Provider-Wechsel nur eine Quelltextzeile austauschen muss?

Nochmal der Vollständigkeit halber - Dies ist mein letzter "Entwicklungs"-Stand:

Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
var
  mq: IMy_MQ; //Interface
begin
  mq:=TMy_MSMQ.Create; //Objekt vom Typ "TMy_MSMQ"

  //Verbinden
  mq.SetParam(IntToStr(MQ_RECEIVE_ACCESS)); //Notlösung
  mq.SetParam(IntToStr(MQ_DENY_NONE)); //Hier auch ;)
  if (mq.QueueOpen('DIRECT=OS:.\Private$\Test')) then
    ShowMessage('Queue opened');
end;
Danke und weiterhin noch schöne Feiertage!

Sir Rufo 25. Dez 2014 20:32

AW: Interface richtig umgesetzt?
 
Der grundlegende Denk-Fehler ist, dass du der Meinung bist, in der Anwendung alles Mögliche über das Interface geben zu müssen. Aber eben genau diese Implementations-spezifischen Informationen habe dort nichts verloren.

Nehmen wir mal den ganz simplen Fall, dass wir einfach nur einen Text versenden wollen. Dann sähe das interface so aus
Delphi-Quellcode:
IMy_MQ = interface
  procedure Send( const AText : string );
end;
So, jetzt wollen wir diesen Text wie in deinem Beispiel über MSMQ versenden:
Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
var
  mq: IMy_MQ; //Interface
begin
  mq := TMy_MSMQ.Create( 'DIRECT=OS:.\Private$\Test', MQ_RECEIVE_ACCESS, MQ_DENY_NONE ); //Objekt vom Typ "TMy_MSMQ"
  mq.Send( 'Mal was senden' );
end;
In der Implementierung von
Delphi-Quellcode:
TMy_MSMQ
kann ich doch nun ganz gemütlich überprüfen, ob die Queue schon geöffnet wurde, wenn nicht, die Queue öffnen und einfach den Text versenden. Genauso eben auch Empfangen. Der Anwendung kann es wie gesagt völlig egal sein, was alles passieren muss, damit ein Text versendet werden kann. Das ist Aufgabe der Implementierung dieses entsprechend zu gewährleisten.

(Das das Beispiel jetzt nicht ganz passt, weil du hier eine Empfangs-Queue öffnen willst und ich etwas senden möchte weiß ich, es sollte aber den Ansatz hinreichend erklären).

Denk immer daran, wie das im wahren Leben funktioniert. Egal ob du mit UPS, DHL, Hermes, etc. ein Paket verschicken möchtest, bei allen ist folgendes gleich:
  • Paket mit Inhalt
  • Adresse
  • kostet Geld
Die Unterschiede sind die Paketaufkleber und die Abgabestellen. Dein Interface ist jetzt der Mitarbeiter in der hauseigenen Poststelle. Dem gibst du das Paket und der kümmert sich um den Versand. Ob dann heute mit UPS, morgen mit DHL, übermorgen mit Hermes oder in ferner Zukunft per berittenem Boten das Paket transportiert wird, für die Mitarbeiter im Haus ändert sich nichts, denn die geben das Paket wie eh und je beim Mitarbeiter der Poststelle ab.

Das ist dann ein Interface :)

TheMiller 25. Dez 2014 21:04

AW: Interface richtig umgesetzt?
 
Super Erklärung!

Aber dann habe ich wohl technisch was falsch umgesetzt. Denn ich kann nicht auf die Methoden von TMy_MSMQ zugreifen. Ich würde nur die Methode "Send" sehen, welche vom Interface kommt. Denn "mq" ist vom Typ IMy_MQ. Ich hatte es ja schon probiert, der Klasse TMy_MSMQ neben den Methoden des Interfaces noch weitere Methoden und Felder hinzuzufügen. Aber die waren mir in OI immer verborgen. Ich kann nicht darauf zugreifen. Was müsste denn richtigerweise .Create von TMy_MSMQ zurückgeben?

Oder ist das der Grund, warum du die Parameter im Constructor übergibst?

Sir Rufo 25. Dez 2014 22:31

AW: Interface richtig umgesetzt?
 
Wieso denn jetzt auf einmal OI? Möchtest du eine Komponente bauen?

Ja, kann man auch machen, würde ich allerdings nicht, denn warum sollte ich auf eine Form sowas draufklatschen, das gehört für mich ein paar Ebenen tiefer in die Anwendung.

Anyway, bauen wir uns also so eine Sende-Komponente:
Delphi-Quellcode:
// Basis Komponente für alle Send Queues
TCustomSendQueue = class( TComponent )
protected
  procedure DoSend( const AText : string ); virtual; abstract;
public
  procedure Send( const AText : string ); // ruft OnSend und DoSend auf
published
  // Ein paar Events?
  property OnSend : TNotifyEvent;
end;

// Konkrete Komponenten für MSMQ
TMSMQConnection = class( TComponent )
published
  property Server : string;
  property ...
end;

// Konkrete Ableitung für MSMQ SendQueue
TMSMQSendQueue = class( TCustomSendQueue )
protected
  procedure DoSend( const AText : string ); override;
published
  property Connection : TMSMQConnection;
  property Queue : string;
  property ...
end;

TheMiller 26. Dez 2014 09:30

AW: Interface richtig umgesetzt?
 
Guten Morgen.

Es tut mir schrecklich leid - ich habe mich vertran. Ich schiebe es mal auf meine Erkältung. Ja, ich schrieb OI, meinte aber lediglich die Codevervollständigung. Dort werden mir die weiteren Methoden nicht angezeigt. Eine Komponente macht da wirklich keinen Sinn.

Das Interface hat z.B. nur GetMessage, SendMessage. TMy_MSMQ hat darüber hinaus noch SetEncryption, IsActiveDirectory, etc.pp (alles fiktiv). Bei meinem Codebeispiel von oben sehe ich wie gesagt nur die Methoden des Interfaces Get/SendMessage. Ich weiß also nicht - außer mit einem TypeCast - wie ich auf die Methoden der KLasse zugreifen kann. Daher denke ich, noch irgenetwas falsch zu machen.

Dass ein Interface minimalistisch und allgemein gehalten werden soll, habe ich jetzt verstanden. Weiß daher jetzt auch, warum es nur Get/SendMessage zu haben braucht. Deine Erklärung mit Post etc. war echt gut. Es wird. Langsam, aber es wird ;)

Danke (auch für die Geduld)

Sir Rufo 26. Dez 2014 10:54

AW: Interface richtig umgesetzt?
 
Aber warum willst du auf die Methoden der implementierenden Klasse zurückgreifen?

Wenn du eine Nachricht verschicken willst, dann benutze das Interface und verschicke die Nachricht. Es ist Aufgabe der implementierenden Klasse sich um den gesamten konkreten Versandablauf zu kümmern, denn nur die weiß, wie man z.B. mit MSMQ sprechen muss. Ich als Versender muss doch gar nicht wissen, was da intern alles passiert, es muss nur passieren.

Am sinnvollsten ist es dem Konstruktor der implementierenden Klasse alles mitzugeben, damit ich ein funktionierendes Interface bekomme.

Generell würde ich allerdings auch eine Trennung zwischen Sende- und Empfangs-Queue vornehmen (also 2 Interfaces). Die Möglichkeit besteht ja, dass ich x Sende-Queues und y Empfangs-Queues benötige. Und die Sende-Queue ist idR nicht die Empfangs-Queue (warum soll ich mir selber Nachrichten schicken).

TheMiller 26. Dez 2014 13:48

AW: Interface richtig umgesetzt?
 
Okay, habe ich jetzt so alles gemacht, wie du es gesagt hast. Leuchtet alles ein, funktioniert auch soweit.

Nur irgendwie werde ich den Verdacht nicht los, dass ich mich - zumindest bei den MessageQueues - mit den Interfaces einschränke. Ich habe nun zwei Interfaces, eines für das Senden, eines für das Empfangen. Habe jetzt auch zwei implementierende Klassen (obwohl ich die zusammenführen könnte - mach ich noch).

Wenn eine Nachricht gesendet wird, prüft die implementierende Klasse, ob es die Queue schon gibt. Wenn nicht, wird sie erstellt, dann geöffnet und die Nachricht eingetragen. Soweit okay. Der Versender kümmert sich also um nichts - wie ich bei der Post.

Beim Erstellen einer Queue kann man aber so viele Optionen angeben, wie übermittle ich die? zB Journal, Transaktionen (IsTransactional), Zugriff (IsWorldReadable), Verschlüsselung, ACL, etc. Und das sind nur die Optionen für das Erstellen einer Queue. Ich kann ja nicht alle Parameter, die ich irgendwann mal gebrauchen könnte, in dem Constructor definieren. Verstehst du mein Problem?

Allerdings habe ich auch verstanden, dass sich die Methode "SendMessage" um alles kümmern soll. Trotzdem habe ich noch obiges Problem. Würde ich nur eine Klasse erstellen, würde ich zwar die Vorteile des Interfaces verlieren, aber da hätte ich diese Probleme nicht. Habe ich noch ein altes Klassendenken? :lol:

Ach unabhängig davon: Wenn die Queue bei MSMQ nicht existiert, muss man um sie zu erstellen, einen Pfad benutzen, um sie zu öffnen/lesen/löschen einen Formatnamen. Also gibt es da auch wieder unterschiede, auf die ich nicht zu reagieren weiß.

Sir Rufo 26. Dez 2014 14:21

AW: Interface richtig umgesetzt?
 
Anders herum gefragt:

Du hast da eine Poststelle wo alle ihre Post hinbringen, auf dass diese versendet wird.

Wer legt das konkrete Verhalten der Poststelle fest? Die Mitarbeiter, die ihre Post versendet haben wollen? Oder eventuell eher doch die Geschäftsleitung?

Und nochmal: Was interessiert es dich wie die Queue funktioniert (mit Transaktion oder ohne, mit Schleifchen oder Bändern, in rot oder grün) wenn du nur etwas versenden willst und du bekommst etwas geliefert, womit du versenden kannst.

Ich denke du springst zu sehr zwischen den Ebenen in deiner Anwendung hin und her.

Wenn die Anwendung z.B. ein Konstrukt aus Sende- und Empfangs-Queue benötigt, dann gib ihr das so
Delphi-Quellcode:
TFoo = class
  constructor Create( SendQueue: ISendQueue; ReceiveQueue: IReceiveQueue );
  // entweder Methoden
  procedure Send(...);
  function GetMessage(..):...;
  // oder als Eigenschaften
  property SendQueue : ISendQueue;
  property ReceiveQueue : IReceiveQueue;
end;
Und schon gehören die zusammen. Das ist wie Lego. Man hat kleine Steine und steckt sich da größere, spezialisierte Dinge zusammen.

Sir Rufo 26. Dez 2014 14:29

AW: Interface richtig umgesetzt?
 
Um nochmals die Poststelle aufzugreifen:

Die Poststelle kann ja durchaus mehrere Versandkanäle benutzen, die z.B. abhängig von Größe, Gewicht und Volumen genutzt werden. Die Mitarbeiter liefern trotzdem ihre Sachen an die Poststelle und die kümmert sich intern um die Bestimmung in welche Queu jetzt das geschoben wird.

Und der Empfang von ausserhalb geht auch durch die Poststelle und kommt aus unterschiedlichen Queues. Die Mitarbeiter bekommen davon nichts mit, denn die kennen nur ... die Poststelle.
Delphi-Quellcode:
TPostStelle = class( TInterfacedObject, ISendQueue, IReceiveQueue )
private
  FSendQueues : // Liste mit den möglichen Queues und dem Regelwerk, wann welche genutzt wird
  FReceiveQueues : // Liste aller möglichen Empfangs-Queues
public
  // Erzeugen mit allen benötigten Informationen
  constructor Create( SendQueueRuleSet: ...; ReceiveQueues : ... );
  procedure Send(...); // ISendQueue
  function GetMessage(...):...; // IReceiveQueue
end;

alda 26. Dez 2014 15:37

AW: Interface richtig umgesetzt?
 
Zitat:

Zitat von DJ-SPM (Beitrag 1284718)
Allerdings habe ich auch verstanden, dass sich die Methode "SendMessage" um alles kümmern soll. Trotzdem habe ich noch obiges Problem. Würde ich nur eine Klasse erstellen, würde ich zwar die Vorteile des Interfaces verlieren, aber da hätte ich diese Probleme nicht. Habe ich noch ein altes Klassendenken? :lol:

Ja, ich würde sagen das ist eine Mischung aus "altem Klassendenken" und vor allem alten Methodiken.

Ich denke Sir Rufo bringt es hier auf den Punkt:
Zitat:

Zitat von Sir Rufo (Beitrag 1284721)
Und schon gehören die zusammen. Das ist wie Lego. Man hat kleine Steine und steckt sich da größere, spezialisierte Dinge zusammen.

Das schwierigste hierbei ist die einzelnen Aufgaben und vor allem die verschiedenen Rollen (Post-Mitarbeiter, Kunde etc. ) korrekt zu identifizieren und zu implementieren (was hier ja sehr gut anhand des Post-Beispiels erläutert wurde). Was hierbei auch hilft ist einfach jede Implementierung zu testen - kannst Du eine Implementierung mal nicht richtig testen, ist Deine Klasse zu groß und Du solltest Dir was andres überlegen um den entsprechenden Code testbar zu machen. Wo man "früher" Funktionalität durch unzählige Vererbungshierarchien verfügbar (und untestbar) gemacht hat, liegt heute der Schwerpunkt auf der Aggregation von Funktionalitäten (den flexiblen Lego-Bausteinen).

TheMiller 26. Dez 2014 16:46

AW: Interface richtig umgesetzt?
 
Danke für die Antworten. Ich habe das alles jetzt mal umgesetzt und die Parameter in den Konstruktor gepackt. Das kann so nicht richtig sein.

Nochmal ganz konkret: Ja, ich rufe nur noch SendMessage() auf und die ganze Magie passiert in der implementierenden Klasse, weil mich das nicht interessiert was da vor sich geht. Okay. Doch wenn die Queue nicht existiertm, muss sie erst angelegt werden. Dann muss ich doch sagen können, wie sie angelegt werden soll.

Parameterübergabe entweder im Konstruktor oder garnicht. Richtig? Im Interface bei Send/Receive kann ich sie auch nicht übergeben, da die Send-Parameter bei MSMQ andere sind, also bei anderen Queues oder via TCP. Also müssten sie alle im Konstruktor übergeben werden - auch die, die vllt. gerade garnicht benötigt werden.

Oder muss ich mich dann auf Standard-Werte festlegen. Wenn eine Queue also nicht existiert, wird sie halt immer mit den von mir in der implementierenden Klasse festgelegten Standardwerten festgelegt. Also zB immer nicht transaktionell und nie weltlesbar.

Nun aber zur Umsetzung: Dies wären die möglichen Parameter:

MSMQ Create: Pathname, IsTransactional, IsWorldReadable
MSMQ Open: Formatname, Access (MQ_READ_ACCESS etc..), ShareMode
MSMQ Send: Subj, Body, DestinationQueue, Transaction
MSMQ Receive: Transaction, WantDestinationQueue, WantBody, ReceiveTimeout, WantConnectorType

Delphi-Quellcode:
  IMy_MQReceiver = interface
  ['{EECD3A9D-D9BC-4644-9D69-DADE10E65ED2}']
    function ReceiveMessage(): String;
  end;

  IMy_MQSender = interface
  ['{5C3F02B1-2E73-4500-9CD3-02EAFEDB332C}']
    function SendMessage(subject, msg: string): Boolean;
  end;

  TMQBroker = (mqMS, mqActiveMQ);

  TMy_MSMQ = class(TInterfacedObject, IMy_MQSender, IMy_MQReceiver)
  private

  public
    constructor Create(createPathName: String; createTransactional, createWorldReadable: Boolean; openAccess, openShareMode: LongInt; openFormatName: String; readTransaction: LongInt; readWantBody: Boolean; readTimeout; sendDestQueue: IMSMQQueue3; sendTransaction: LongInt);
    destructor Destroy();
    function ReceiveMessage(): String;
    function SendMessage(Subject, msg: String): Boolean;
  end;

// Und so könnte dann das Erstellen des Objektes aussehen:
procedure TForm1.Button4Click(Sender: TObject);
var
  mq: TMy_MSMQ;
begin
  mq:=TMy_MSMQ.Create('meineNeueQueue', false, false, MQ_READ_ACCESS, MQ_DENY_SHARE, '.\Private\meineNeueQueue', 0, true, INFITE, nil, 0);
end;
Ich habe verstanden, dass sich das implementierende Objekt um alles kümmern soll. Aber ich muss doch irgendwie Einfluss darauf haben, wie es die Queue im Notfall erstellen soll.

Habe mich gerade länger damit beschäftigen können und verschiedene Methoden ausprobiert. Bin jetzt gerade etwas verwirrt ;)

Sir Rufo 26. Dez 2014 17:26

AW: Interface richtig umgesetzt?
 
Bis auf die Tatsache, dass du eine Schreib-Lese-Queue erzeugen möchtest ist das doch alles richtig. Ich wüsste keinen Fall, wo ich in ein und derselben Queue lesen und schreiben möchte. Queues sind One-To-One Verbindungen. Eine Nachricht in einer Queue wird von einem Empfänger empfangen. Will ich an mehrere Empfänger die gleiche Nachricht senden, dann muss die Nachricht auch in mehrere Queues.

Warum sollte es falsch sein, der implementierenden Klasse alles benötigte an die Hand zu geben um zu funktionieren?

Mal abgesehen, dass es für dich ungewohnt ist anscheinend nicht alles in der Hand zu haben, wo siehst du in deiner Anwendung das konkrete Problem?

Zeig doch mal etwas halbwegs konkretes, wo du beim Benutzen des Interfaces den Wunsch verspüren würdest, z.B. den Namen der Queue zu ändern, oder die Queue auf Transaktion umzustellen.

BTW

Das passt aber nicht
Delphi-Quellcode:
  IMy_MQReceiver = interface
  ['{EECD3A9D-D9BC-4644-9D69-DADE10E65ED2}']
    function ReceiveMessage(): String;
  end;

  IMy_MQSender = interface
  ['{5C3F02B1-2E73-4500-9CD3-02EAFEDB332C}']
    function SendMessage(subject, msg: string): Boolean;
  end;
Bei SendMessage hast du Subject und Message aber bei ReceiveMessage gibt es nur einen String zurück. Was kommt denn da? Subject, Message oder beides zusammengewürfelt?

TheMiller 26. Dez 2014 22:16

AW: Interface richtig umgesetzt?
 
Zitat:

Zitat von Sir Rufo (Beitrag 1284744)
ist das doch alles richtig.

Ahhhhh. Das ist also der springende Punkt. Doch alle Parameter in den constructor. Das kannte ich so wirklich nicht. Kenne es eigentlich immer nur so, dass man so wenige Parameter wie möglich hat und dann auch nur die, die man wirklich braucht. Hier habe ich ja jetzt alle die dabei, die ich vielleicht mal irgendwie gebrauchen könnte. Auch die Anzahl war mir nicht geheuer. Aber da das nun bestätigt ist, kann ich damit gut leben!

Zitat:

Zitat von Sir Rufo (Beitrag 1284744)
Ich wüsste keinen Fall, wo ich in ein und derselben Queue lesen und schreiben möchte. Queues sind One-To-One Verbindungen. Eine Nachricht in einer Queue wird von einem Empfänger empfangen. Will ich an mehrere Empfänger die gleiche Nachricht senden, dann muss die Nachricht auch in mehrere Queues.

Ja, das hat sich jetzt durch die Tests und Verdeutlichungen ergeben. Das weiß ich, hatte ich eigentlich auch nicht vor.

Zitat:

Zitat von Sir Rufo (Beitrag 1284744)
Bei SendMessage hast du Subject und Message aber bei ReceiveMessage gibt es nur einen String zurück. Was kommt denn da? Subject, Message oder beides zusammengewürfelt?

Ja, auch das hat sich durch Tests und so weiter ergeben. Ist natürlich auch nicht korrekt so.

Aber danke für diese Hinweise - hätte ja wirklich ein echter Fehler sein können. Die Interfaces etc werden dann später auch nicht mehr "TMY..." lauten ;)

---

Aber wenn wir schon dabei sind: Kannst du mir nochmal folgendes erklären

Zitat:

Zitat von Sir Rufo (Beitrag 1284723)
Delphi-Quellcode:
TPostStelle = class( TInterfacedObject, ISendQueue, IReceiveQueue )
private
  FSendQueues : // Liste mit den möglichen Queues und dem Regelwerk, wann welche genutzt wird
  FReceiveQueues : // Liste aller möglichen Empfangs-Queues
public
  // Erzeugen mit allen benötigten Informationen
  constructor Create( SendQueueRuleSet: ...; ReceiveQueues : ... );
  procedure Send(...); // ISendQueue
  function GetMessage(...):...; // IReceiveQueue
end;

Es geht um die "Liste mit den möglichen Queues und dem Regelwerk, wann welche genutzt wird". Kannst du mir mal ganz konkret erklären, wie sowas aussehen könnte? Also sowohl die Liste, als auch das Regelwerk? Und dann noch sagen, von welchem Datentyp das sein müsste und wie die Umsetzung ist. Das habe ich leider garnicht verstanden. Das wäre jetzt nur der Vollständigkeit halber!

Vielen Dank an alle!

Sir Rufo 26. Dez 2014 22:31

AW: Interface richtig umgesetzt?
 
Zum einen muss man ja auch nicht nur einen Konstruktor haben und zum anderen kann man auch eine Factory nehmen.

Für eine minimalistische Queue brauchst du z.B. nur 2 Parameter, dann erstell dir einen Konstruktor mit diesen 2 Parametern und für die superduper Gedöns-Queue benötigst du 42 Parameter, jo, dann eben noch so einen, und evtl. noch ein paar weitere.

Eine Factory hilft immer dann, wenn man sehr viele Instanzen benötigt mit sehr vielen gleichen und wenigen variablen Parametern. Dann erzeuge ich die Factory mit den gleichbleibenden Parametern und hole mir die Instanzen über die Angabe der noch fehlenden, aber eben variablen Parameter. Schon bleibt das hübsch übersichtlich.
Delphi-Quellcode:
TMSMQQueueFactory = class
  constructor Create( Server, { ganz viele Parameter } );
  function CreateSendQueue( const QueueName : string ) : ISendQueue;
  function CreateReceiveQueue( const QueueName : string ) : IReceiveQueue;
end;
Sieht ja schon einfacher aus :)

Das Beispiel mit dem Postamt wird bei dir jetzt nicht so richtig passen, es ging mehr um die Möglichkeiten. Ein Regelwerk könnte ja darin bestehen, dass z.B. Chat-Nachrichten in eine andere Queue kommen, als andere Nachrichten. Dann muss dein Postamt z.B. den Nachrichtentypen erkennen und sucht sich die passende Queue für die Chat-Nachrichten heraus und versendet darüber.

Ursa 5. Jan 2015 06:48

AW: Interface richtig umgesetzt?
 
Du diese Klasse nun doch an die Implementierung koppelst.




samsung galaxy A5 schutzhülle


Alle Zeitangaben in WEZ +1. Es ist jetzt 15:50 Uhr.

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