Einzelnen Beitrag anzeigen

Der_Unwissende

Registriert seit: 13. Dez 2003
Ort: Berlin
1.756 Beiträge
 
#2

Re: Patterns: Wie programmiert man auf eine Schnittstelle us

  Alt 23. Nov 2006, 15:32
Zitat von EccoBravo:
Wie programmiert man in Delphi auf eine Schnittstelle?
(Wie sieht es aus, wenn man es nicht machen würde - auf Implementierung programmieren, feste Bindung..)
Hi,
Javacode bzw. die Entsprechenden Schlüsselwort kannst du (für mich) immer gerne nennen, macht vieles leichter. Java können einige im Forum, die dann auch noch leichter helfen können den Code dort zu erklären.
Eine Schnittstelle wird in Delphi (wie in Java) mit dem Schlüsselwort interface angezeigt. Es gelten auch ganz ähnliche Dinge, so sind nur Methoden in der Schnittstelle erlaubt (in Delphi auch Properties, die aber nur über Methoden gelesen oder geschrieben werden können). Die Methoden sind immer public (wobei es in Delphi nicht dazu geschrieben werden darf).
Eine Klasse kann wiederum beliebig viele Interfaces implementieren. Das besondere in Delphi ist dann wiederum, dass sich bei den Interfaces um COM Interfaces handelt, diese beinhalten damit aut. 3 Methoden (_addRef, _release und queryInterface), diese 3 Methoden müssen immer implementiert werden. Die Klasse TInterfacedObject stellt eine Standardimplementierung bereit. Genaueres findest du in der OH zum Thema Interface oder halt auch im Forum (gibt glaube ich auch Tut. dazu).

Kurzes Beispiel:
Delphi-Quellcode:
type
  IFoo = interface
    procedure doFoo;
  end;

  IBar = interface
    procedure doBar;
  end;
  
  TFooBar(TInterfacedObjekt, IFoo, IBar)
    public
      procedure doFoo;
      procedure doBar;
  end;
Wobei du überall dort, wo eine Variable vom Typ IFoo oder IBar erwartet wird auch ein TFooBar übergeben kannst.


Zitat von EccoBravo:
Was ist eine lockere Bindung?
Die lockere/lose Bindung ist eine der wichtigsten Techniken um guten Code zu schaffen. Lockere Bindung heißt einfach, dass du wenig Abhängigkeiten schaffst. Am leichtesten lässt sich das anhand von einem Beispiel erklären. Es gibt so gewisse Datenstrukturen, die man ganz gerne verwendet um eine unbekannte Menge von Daten zu speichern. Typischerweise würde man hier von einer Liste sprechen. Dabei kann ein Liste auf ganz unterschiedliche Art und Weise implementiert werden. Du kannst eine doppelt verkettete Liste, eine einfach verkette Liste, ein Array dessen Größe angepasst wird uvm. verwenden. Wie die Liste genau funktioniert ist aber egal, man möchte schließlich nur beliebig viele Datentypen anhängen und wieder rausnehmen können.
Eine Lose Bindung gibt also nicht vor, welche konkrete Implementierung hier zum Einsatz kommen muss, sondern nur einen abstrakten Datentypen (eine abstrakte Klasse oder ein Interface) und somit kann hier jede Implementierung, die Nachfahre der abstrakten Klasse ist bzw. das Interface implementiert verwendet werden.

In Delphi ist ein Standardbeispiel der Datentyp TStrings. Hierbei handelt es sich um eine abstrakte Klasse, die zwei VLC Nachfahren hat: TStringList und THashedStringList. Möchte man einfach ein Menge von Strings speichern, so kann man hier eine Variable vom Typ TStrings verlangen, ob dahinter nun eine TStringList, eine THashedStringList oder ein eigener Nachfahre steht bleibt für einen verborgen (an der Stelle im Programm).
Der Vorteil ist, dass man die konkrete Implementierung austauschen kann (weil es etwas besseres gibt oder Fehler behoben wurden) ohne dass die Logik an dieser Stelle angepasst werden muss. So kann ein und das selbe Programm (dass z.B. mit einem TStrings Objekt arbeitet) auf einem normalen PC eine THashedStringList verwenden, da man hier schnell suchen kann, auf einem anderen Rechner mit sehr beschränkten Ressourcen hingegen zur eigenen Implementierung greifen, die keinen Platz verschenkt, aber etwas länger beim Suchen benötigt.
Vorallem aber auch bei größeren Projekten mit mehr als einem Entwickler ist lose Bindung wichtig. Hier legt man nur die Interfaces der einzelnen Teilprobleme fest. Sagen wir es gibt Problem A und B, wobei A eine Funktion von B benötigt (typisch wäre, dass B beliebige Daten speichert). Nun ist A aber um einiges leichter als B, also auch früher fertig. Dummerweise muss jedoch auf die Lösung von B gewartet werden, da A ja eine dieser Funktionen braucht. Kennt man aber das (verbindliche) Interface von B, so weiß man, dass B (egal wie es aussieht) dieses Interface implementieren und damit die Funktionen der Schnittstelle zur Verfügung stellen wird. Also kann man hier einfach eine Dummy-Implementierung erstellen, A mit dieser Testen und wenn B irgendwann fertig ist, muss nur B statt der Dummy-Implementierung übergeben werden, am korrekten Verhalten von A sollte dies nichts ändern.
Kapseltst du z.B. das Speichern in einer Datenbank durch eine solche Schnittstelle und die konkrete Implementierung, kann hier für jedes DBMS eine eigene Implementierung erstellt werden, ohne dass es etwas am restlichen Programm ändert. Wie Daten dabei in einem bestimmten DBMS abegelegt werden bleibt hinter dieser Schnittstelle verborgen.

Gruß Der Unwissende
  Mit Zitat antworten Zitat