Seit TwinCAT V3.1 Build 4024 können Funktionsblöcke, Methoden und Eigenschaften als abstract gekennzeichnet werden. Abstrakte FBs können nur als Basis-FB für die Vererbung genutzt werden. Ein direktes Instanziieren von abstrakten FBs ist nicht möglich. Somit haben abstrakte FBs eine gewisse Ähnlichkeit zu Schnittstellen. Es stellt sich nun die Frage, wann eine Schnittstelle und wann ein abstrakter FB zum Einsatz kommen sollte.
Eine sehr gute Beschreibung zu abstract liefert der Post The ABSTRACT keyword aus dem Blog PLCCoder.com oder das Beckhoff Information System. Deshalb soll das Wichtigste nur kurz wiederholt werden.
abstrakte Methoden
METHOD PUBLIC ABSTRACT DoSomething : LREAL
- bestehen ausschließlich aus der Deklaration und enthalten keine Implementierung. Der Methodenrumpf ist leer.
- können public, protected oder internal sein. Der Zugriffsmodifizierer private ist nicht erlaubt.
- können nicht zusätzlich als final deklariert werden.
abstrakte Eigenschaften
PROPERTY PUBLIC ABSTRACT nAnyValue : UINT
- können Getter oder Setter oder beides enthalten.
- Getter und Setter bestehen ausschließlich aus der Deklaration und enthalten keine Implementierung.
- können public, protected oder internal sein. Der Zugriffsmodifizierer private ist nicht erlaubt.
- können nicht zusätzlich als final deklariert werden.
abstrakte Funktionsblöcke
FUNCTION_BLOCK PUBLIC ABSTRACT FB_Foo
- Sobald eine Methode oder eine Eigenschaft mit abstract deklariert wurde, muss auch der Funktionsblock mit abstract deklariert werden.
- Von abstrakten FBs können keine Instanzen angelegt werden. Abstrakte FBs können nur bei der Vererbung als Basis-FB verwendet werden.
- Alle abstrakte Methoden und alle abstrakte Eigenschaften müssen überschrieben werden, damit ein konkreter FB entsteht. Aus der abstrakten Methode oder einer abstrakten Eigenschaft wird durch das Überschreiben eine konkrete Methode oder eine konkrete Eigenschaft.
- Abstrakte Funktionsblöcke können zusätzlich konkrete Methoden und/oder konkrete Eigenschaften enthalten.
- Werden bei der Vererbung nicht alle abstrakte Methoden oder nicht alle abstrakte Eigenschaften überschrieben, so kann der erbende FB auch wieder nur ein abstrakter FB sein (schrittweise Konkretisierung).
- Zeiger oder Referenzen von Typ eines abstrakten FBs sind erlaubt. Diese können aber auf konkrete FBs referenzieren und somit deren Methoden oder Eigenschaften aufrufen (Polymorphismus).
Unterschiede abstrakter FB und Schnittstelle
Besteht ein Funktionsblock ausschließlich aus abstrakten Methoden und abstrakten Eigenschaften, so enthält dieser Funktionsblock keinerlei Implementierungen und hat dadurch eine gewisse Ähnlichkeit mit Schnittstellen. Im Detail gibt es allerdings einige Besonderheiten zu beachten.
Schnittstelle | abstrakter FB | |
---|---|---|
unterstützt Mehrfachvererbung | + | – |
kann lokale Variablen enthalten | – | + |
kann konkrete Methoden enthalten | – | + |
kann konkrete Eigenschaften enthalten | – | + |
unterstützt neben public noch weitere Zugriffsmodifizierer | – | + |
Verwendung bei Arrays | + | nur über POINTER |
Durch die Tabelle kann der Eindruck entstehen, dass Schnittstellen nahezu komplett durch abstrakte FBs austauschbar sind. Allerdings bieten Schnittstellen eine größere Flexibilität durch die Möglichkeit, in unterschiedlichen Vererbungshierarchien verwendet zu werden. In dem Post IEC 61131-3: Objektkomposition mit Hilfe von Interfaces wird hierzu ein Beispiel gezeigt.
Als Entwickler stellt sich somit die Frage, wann eine Schnittstelle und wann ein abstrakter FB genutzt werden sollte. Die einfache Antwort lautet: am besten beides gleichzeitig. Hierdurch steht eine Standardimplementierung im abstrakten Basis-FB zur Verfügung, wodurch das Ableiten erleichtert wird. Jedem Entwickler bleibt aber die Freiheit erhalten, die Schnittstelle direkt zu implementieren.
Beispiel
Für die Datenverwaltung von Angestellten sind Funktionsblöcke zu entwerfen. Hierbei wird unterschieden zwischen Festangestellten (FB_FullTimeEmployee) und Vertragsmitarbeiter (FB_ContractEmployee). Jeder Mitarbeiter wird durch seinen Vornamen (sFirstName), Nachnamen (sLastName) und der Personalnummer (nPersonnelNumber) identifiziert. Hierzu werden entsprechende Eigenschaften bereitgestellt. Außerdem wird eine Methode benötigt, die den vollständigen Namen inklusive Personalnummer als formatierten String ausgibt (GetFullName()). Die Berechnung des Monatseinkommens erfolgt durch die Methode GetMonthlySalary().

Die Unterschiede beider Funktionsblöcke bestehen in der Berechnung des Monatseinkommens. Während der Festangestellte ein Jahreseinkommen (nAnnualSalary) bezieht, ergibt sich das Monatseinkommen des Vertragsmitarbeiters aus dem Stundenlohn (nHourlyPay) und der Monatsarbeitszeit (nMonthlyHours). Somit besitzen die beiden Funktionsblöcke für die Berechnung des Monatseinkommens unterschiedliche Eigenschaften. Die Methode GetMonthlySalary() ist in beiden Funktionsblöcken enthalten, unterscheidet sich aber in der Implementierung.
1. Lösungsansatz: abstrakter FB
Da beide FBs etliche Gemeinsamkeiten haben, liegt es nahe einen Basis-FB (FB_Employee) zu erstellen. Dieser Basis-FB enthält alle Methoden und Eigenschaften, die in beiden FBs enthalten sind. Da sich aber die Methoden GetMonthlySalary() in der Implementierung unterscheiden, wird diese in FB_Employee als abstract gekennzeichnet. Dadurch müssen alle FBs, die von diesen Basis-FB erben, GetMonthlySalary() überschreiben.

Beispiel 1 (TwinCAT 3.1.4024) auf GitHub
Nachteile
Der Lösungsansatz sieht auf dem ersten Blick sehr solide aus. Wie aber weiter Oben schon erwähnt, kann der Einsatz von Vererbung auch Nachteile mit sich ziehen. Besonders dann, wenn FB_Employee Teil einer Vererbungskette ist. Alles was FB_Employee über diese Kette erbt, wird auch an FB_FullTimeEmployee und FB_ContractEmployee vererbt. Kommt FB_Employee in einem anderen Zusammenhang zum Einsatz, so kann eine umfangreiche Vererbungs-Hierarchie zu weiteren Problemen führen.
Auch gibt es Einschränkungen bei dem Versuch, alle Instanzen in einem Array als Referenzen abzulegen. Folgende Deklaration wird vom Compiler nicht zugelassen:
aEmployees : ARRAY [1..2] OF REFERENCE TO FB_Employee; // error
Statt Referenzen müssen Zeiger verwendet werden:
aEmployees : ARRAY [1..2] OF POINTER TO FB_Employee;
Allerdings ist bei der Verwendung von Zeigern einiges zu beachten (z.B. beim Online-Change). Aus diesem Grund versuche ich Zeiger so weit wie möglich zu vermeiden.
Vorteile
Es ist zwar nicht möglich, direkt eine Instanz eines abstrakten FB anzulegen, allerdings kann per Referenz auf die Methoden und Eigenschaften eines abstrakten FB zugegriffen werden.
VAR
fbFullTimeEmployee : FB_FullTimeEmployee;
refEmployee : EFERENCE TO FB_Employee;
sFullName : STRING;
END_VAR
refEmployee REF= fbFullTimeEmployee;
sFullName := refEmployee.GetFullName();
Auch kann es ein Vorteil sein, dass die Methode GetFullName() und die Eigenschaften sFirstName, sLastName und nPersonnelNumber im abstrakten Basis-FB schon vollständig implementiert und dort nicht als abstract deklariert wurden. Ein Überschreiben dieser Elemente in den abgeleiteten FBs ist nicht mehr notwendig. Soll z.B. die Formatierung für den Namen angepasst werden, so ist dieses nur an einer Stelle durchzuführen.
2. Lösungsansatz: Schnittstelle
Ein Ansatz mit Schnittstellen ähnelt sehr stark der vorherigen Variante. Die Schnittstelle enthält alle Methoden und Eigenschaften, die bei beiden FBs (FB_FullTimeEmployee und FB_ContractEmployee) gleich sind.

Beispiel 2 (TwinCAT 3.1.4024) auf GitHub
Nachteile
Dadurch das FB_FullTimeEmployee und FB_ContractEmployee die Schnittstelle I_Employee implementieren, muss jeder FB alle Methoden und alle Eigenschaften aus der Schnittstelle auch enthalten. Das betrifft auch die Methode GetFullName(), die in beiden Fällen die gleiche Berechnung durchführt.
Wurde ein Schnittstelle veröffentlich (z.B. durch eine Bibliothek) und in verschiedenen Projekten eingesetzt, so sind Änderungen an dieser Schnittstelle nicht mehr möglich. Wird eine Methode oder eine Eigenschaft hinzugefügt, so müssen auch alle Funktionsblöcke angepasst werden, die diese Schnittstelle implementieren. Bei der Vererbung von FBs ist dieses nicht notwendig. Wird ein Basis-FB erweitert, so müssen alle FBs die davon erben nicht verändert werden. Außer, die neuen Methoden oder Eigenschaften sind abstrakt.
Tipp: Kommt es doch vor, dass man eine Schnittstelle später anpassen muss, so kann man eine neue Schnittstelle anlegen. Diese erbt von der ursprünglichen Schnittstelle und wird um die notwendigen Elemente erweitert.
Vorteile
Funktionsblöcke können mehrere Schnittstellen implementieren. Schnittstellen sind dadurch in vielen Fällen flexibler einsetzbar.
Bei einem Funktionsblock kann zur Laufzeit per __QUERYINTERFACE() eine bestimmte Schnittstelle abgefragt werden. Wurde dieses implementiert, so ist über diese Schnittstelle ein Zugriff auf den FB möglich. Dieses macht den Einsatz von Schnittstellen sehr flexibel.
Ist die Implementierung einer bestimmten Schnittstelle bekannt, so kann der Zugriff über die Schnittstelle auch direkt erfolgen.
VAR
fbFullTimeEmployee : FB_FullTimeEmployee;
ipEmployee : I_Employee;
sFullName : STRING;
END_VAR
ipEmployee := fbFullTimeEmployee;
sFullName := ipEmployee.GetFullName();
Auch können Schnittstellen als Datentyp für ein Array verwendet werden. Alle FBs, welche die Schnittstelle I_Employee implementieren, können zu dem folgenden Array hinzugefügt werden.
aEmployees : ARRAY [1..2] OF I_Employee;
3. Lösungsansatz: Kombination abstrakter FB und Schnittstelle
Warum nicht beide Ansätze miteinander kombinieren und somit von den Vorteilen beider Varianten profitieren?

Beispiel 3 (TwinCAT 3.1.4024) auf GitHub
Bei der Kombination der beiden Ansätze wird zunächst die Schnittstelle zur Verfügung gestellt. Anschließend wird die Verwendung der Schnittstelle durch den abstrakten Funktionsblock FB_Employee vereinfacht. Gleiche Implementierungen von gemeinsamen Methoden können in dem abstrakten FB bereitgestellt werden. Eine mehrfache Implementierung ist nicht notwendig. Kommen neue FBs hinzu, können diese auch direkt die Schnittstelle I_Employee nutzen.
Der Aufwand für die Umsetzung ist erstmal etwas höher als bei den beiden vorherigen Varianten. Aber gerade bei Bibliotheken, die von mehreren Programmierern eingesetzt und über Jahre weiterentwickelt werden, kann sich dieser Mehraufwand lohnen.
- Wenn der Anwender keine eigene Instanz des FBs anlegen soll (weil dieses nicht sinnvoll erscheint), dann sind abstrakte FBs oder Schnittstellen hilfreich.
- Wenn man die Möglichkeit haben will, in mehr als einen Basistyp zu verallgemeinern, dann sollte eine Schnittstelle zum Einsatz kommen.
- Wenn ein FB ohne die Implementierung der Methoden oder Eigenschaften vereinbart werden kann, dann sollte man eine Schnittstelle dem abstrakten FB vorziehen.
Halo Stefan,
I follow your blog but I do not read German.
Will you publish this article in English?
Thank you.
Yes, I’ll translate the article into English as soon as possible.
Stefan
Hello Stefan,
I am really looking forward reading this article in English.
It would be a perfect Xmas present. 🙂
Best Regards,
András Mózes
Hi András,
The post is now in english online too.
Regards
Stefan
Hi Stefan,
Thanks for your article. Would be nice if you could post it in english as well. I understand example 1 and 2, but for me it’s still hard to see the advantage of example 3 compared to 1. What’s the extra advantage of the interface in example 3? Do you also have some notes on how to deal (workarounds) with multiple inherentance, eventhough it’s not supported in Twincat 3.
Hi Mark,
Sample 3 combines the advantages of Sample 1 and Sample 2.
Stefan