AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Interface richtig umgesetzt?

Ein Thema von TheMiller · begonnen am 23. Dez 2014 · letzter Beitrag vom 5. Jan 2015
Antwort Antwort
Seite 1 von 4  1 23     Letzte »    
Benutzerbild von TheMiller
TheMiller

Registriert seit: 19. Mai 2003
Ort: Gründau
2.480 Beiträge
 
Delphi XE7 Architect
 
#1

Interface richtig umgesetzt?

  Alt 23. Dez 2014, 13:07
Delphi-Version: 5
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!
  Mit Zitat antworten Zitat
alda

Registriert seit: 24. Mär 2014
Ort: Karlsruhe
93 Beiträge
 
Delphi XE6 Architect
 
#2

AW: Interface richtig umgesetzt?

  Alt 23. Dez 2014, 13:15
Also wenn das Dein Code ist, dann stimmt etwas vorne und hinten nicht (oder ich verstehe einfach nicht ).
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.
  Mit Zitat antworten Zitat
mjustin

Registriert seit: 14. Apr 2008
3.004 Beiträge
 
Delphi 2009 Professional
 
#3

AW: Interface richtig umgesetzt?

  Alt 23. Dez 2014, 13:29
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.
Michael Justin

Geändert von mjustin (23. Dez 2014 um 14:01 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von TheMiller
TheMiller

Registriert seit: 19. Mai 2003
Ort: Gründau
2.480 Beiträge
 
Delphi XE7 Architect
 
#4

AW: Interface richtig umgesetzt?

  Alt 23. Dez 2014, 13:44
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
  Mit Zitat antworten Zitat
alda

Registriert seit: 24. Mär 2014
Ort: Karlsruhe
93 Beiträge
 
Delphi XE6 Architect
 
#5

AW: Interface richtig umgesetzt?

  Alt 23. Dez 2014, 13:52
@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.

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;
....

Geändert von alda (23. Dez 2014 um 14:02 Uhr)
  Mit Zitat antworten Zitat
mjustin

Registriert seit: 14. Apr 2008
3.004 Beiträge
 
Delphi 2009 Professional
 
#6

AW: Interface richtig umgesetzt?

  Alt 23. Dez 2014, 14:19
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.)
Michael Justin
  Mit Zitat antworten Zitat
Dejan Vu
(Gast)

n/a Beiträge
 
#7

AW: Interface richtig umgesetzt?

  Alt 23. Dez 2014, 14:31
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.)

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.
  Mit Zitat antworten Zitat
mjustin

Registriert seit: 14. Apr 2008
3.004 Beiträge
 
Delphi 2009 Professional
 
#8

AW: Interface richtig umgesetzt?

  Alt 23. Dez 2014, 14:51
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.
Michael Justin
  Mit Zitat antworten Zitat
Benutzerbild von TheMiller
TheMiller

Registriert seit: 19. Mai 2003
Ort: Gründau
2.480 Beiträge
 
Delphi XE7 Architect
 
#9

AW: Interface richtig umgesetzt?

  Alt 23. Dez 2014, 14:53
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.
  Mit Zitat antworten Zitat
mjustin

Registriert seit: 14. Apr 2008
3.004 Beiträge
 
Delphi 2009 Professional
 
#10

AW: Interface richtig umgesetzt?

  Alt 23. Dez 2014, 15:01
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.
Michael Justin
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 4  1 23     Letzte »    


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:06 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