Verfahren zur automatischen Erkennung von Software- Mustern im Überblick



Ähnliche Dokumente
1 topologisches Sortieren

Lineargleichungssysteme: Additions-/ Subtraktionsverfahren

Software Engineering Klassendiagramme Assoziationen

Fachdidaktik der Informatik Jörg Depner, Kathrin Gaißer

Daniel Warneke Ein Vortrag im Rahmen des Proseminars Software Pioneers

Professionelle Seminare im Bereich MS-Office

Prinzipien Objektorientierter Programmierung

Primzahlen und RSA-Verschlüsselung

Handbuch ECDL 2003 Basic Modul 5: Datenbank Grundlagen von relationalen Datenbanken

Suche schlecht beschriftete Bilder mit Eigenen Abfragen

Stellen Sie bitte den Cursor in die Spalte B2 und rufen die Funktion Sverweis auf. Es öffnet sich folgendes Dialogfenster

Objektorientierte Programmierung für Anfänger am Beispiel PHP

Verhindert, dass eine Methode überschrieben wird. public final int holekontostand() {...} public final class Girokonto extends Konto {...

1 Mathematische Grundlagen

4. Jeder Knoten hat höchstens zwei Kinder, ein linkes und ein rechtes.

Informationsblatt Induktionsbeweis

Objektorientierte Programmierung. Kapitel 12: Interfaces

Binäre Bäume. 1. Allgemeines. 2. Funktionsweise. 2.1 Eintragen

Die Gleichung A x = a hat für A 0 die eindeutig bestimmte Lösung. Für A=0 und a 0 existiert keine Lösung.

Objektorientierte Programmierung

Datensicherung. Beschreibung der Datensicherung

Grundlagen der Theoretischen Informatik, SoSe 2008

Objektorientierter Software-Entwurf Grundlagen 1 1. Analyse Design Implementierung. Frühe Phasen durch Informationssystemanalyse abgedeckt

Einführung in. Logische Schaltungen

Einführung und Motivation

ARCO Software - Anleitung zur Umstellung der MWSt

Fachbericht zum Thema: Anforderungen an ein Datenbanksystem

Handbuch Fischertechnik-Einzelteiltabelle V3.7.3

Fassade. Objektbasiertes Strukturmuster. C. Restorff & M. Rohlfing

Kapiteltests zum Leitprogramm Binäre Suchbäume

Konzepte der Informatik

50. Mathematik-Olympiade 2. Stufe (Regionalrunde) Klasse Lösung 10 Punkte

Musterlösung zur Vorlesung Modellbasierte Softwareentwicklung Wintersemester 2014/2015 Übungsblatt 9

Projektmanagement in der Spieleentwicklung

SEP 114. Design by Contract

Grundbegriffe der Informatik

Komponententest. Testen von Software Systemen. Übung 02 SS 2009 Version:

infach Geld FBV Ihr Weg zum finanzellen Erfolg Florian Mock

Folge 19 - Bäume Binärbäume - Allgemeines. Grundlagen: Ulrich Helmich: Informatik 2 mit BlueJ - Ein Kurs für die Stufe 12

M. Graefenhan Übungen zu C. Blatt 3. Musterlösung

Zeichen bei Zahlen entschlüsseln

Diese Ansicht erhalten Sie nach der erfolgreichen Anmeldung bei Wordpress.

Prozessbewertung und -verbesserung nach ITIL im Kontext des betrieblichen Informationsmanagements. von Stephanie Wilke am

geben. Die Wahrscheinlichkeit von 100% ist hier demnach nur der Gehen wir einmal davon aus, dass die von uns angenommenen

Einführung in die Programmierung

Software Engineering. Sommersemester 2012, Dr. Andreas Metzger

1. Man schreibe die folgenden Aussagen jeweils in einen normalen Satz um. Zum Beispiel kann man die Aussage:

Einführung in die Java- Programmierung

In diesem Thema lernen wir die Grundlagen der Datenbanken kennen und werden diese lernen einzusetzen. Access. Die Grundlagen der Datenbanken.

Objektorientierte Programmierung OOP

Zahlen auf einen Blick

SDD System Design Document

Dokumentation von Ük Modul 302

4 Aufzählungen und Listen erstellen

Kurzanleitung zu. von Daniel Jettka

EinfÅhrung in die objektorientiere Programmierung (OOP) unter Delphi 6.0. EDV Kurs 13/2

Objektorientierte Programmierung

Übungen zur Softwaretechnik

StuPro-Seminar Dokumentation in der Software-Wartung. StuPro-Seminar Probleme und Schwierigkeiten in der Software-Wartung.

SANDBOXIE konfigurieren

Agile Vorgehensmodelle in der Softwareentwicklung: Scrum

Klassenentwurf. Wie schreiben wir Klassen, die leicht zu verstehen, wartbar und wiederverwendbar sind? Objektorientierte Programmierung mit Java

Kostenstellen verwalten. Tipps & Tricks

News & RSS. Einleitung: Nachrichten er-(veröffentlichen) und bereitstellen Nachrichten erstellen und bereitstellen

PHP - Projekt Personalverwaltung. Erstellt von James Schüpbach

Übung 6: Feinentwurf. Prof. Dr. Dr. h.c. Manfred Broy Dr. Herbert Ehler, Martin Feilkas 6. Juli 2006 Bernd Spanfelner, Sebastian Winter

Robot Karol für Delphi

Access [basics] Rechnen in Berichten. Beispieldatenbank. Datensatzweise berechnen. Berechnung im Textfeld. Reporting in Berichten Rechnen in Berichten

Programmierkurs Java

Speicher in der Cloud

Mediator 9 - Lernprogramm

Bilder Schärfen und Rauschen entfernen

Vgl. Kapitel 5 aus Systematisches Requirements Engineering, Christoph Ebert

Klausur zur Einführung in die objektorientierte Programmierung mit Java

Inhalt. 1 Einleitung AUTOMATISCHE DATENSICHERUNG AUF EINEN CLOUDSPEICHER

Der Aufruf von DM_in_Euro 1.40 sollte die Ausgabe 1.40 DM = Euro ergeben.

etutor Benutzerhandbuch XQuery Benutzerhandbuch Georg Nitsche

Persönliche Zukunftsplanung mit Menschen, denen nicht zugetraut wird, dass sie für sich selbst sprechen können Von Susanne Göbel und Josef Ströbl

Kapitel 4 Die Datenbank Kuchenbestellung Seite 1

impact ordering Info Produktkonfigurator

sondern alle Werte gleich behandelt. Wir dürfen aber nicht vergessen, dass Ergebnisse, je länger sie in der Vergangenheit

Zwischenablage (Bilder, Texte,...)

Urlaubsregel in David

Novell Client. Anleitung. zur Verfügung gestellt durch: ZID Dezentrale Systeme. Februar ZID Dezentrale Systeme

Summenbildung in Bauteiltabellen mit If Then Abfrage

Software Engineering Interaktionsdiagramme

Zahlenwinkel: Forscherkarte 1. alleine. Zahlenwinkel: Forschertipp 1

Welche Unterschiede gibt es zwischen einem CAPAund einem Audiometrie- Test?

Windows XP Jugendschutz einrichten. Monika Pross Molberger PC-Kurse

Microsoft Update Windows Update

FIS: Projektdaten auf den Internetseiten ausgeben

4. BEZIEHUNGEN ZWISCHEN TABELLEN

Some Software Engineering Principles

Eine Logikschaltung zur Addition zweier Zahlen

Kapitel 3 Frames Seite 1

Lernerfolge sichern - Ein wichtiger Beitrag zu mehr Motivation

Lineare Differentialgleichungen erster Ordnung erkennen

LinguLab GmbH. Bedienungsanleitung Allgemeine Definition

2.1 Erstellung einer Gutschrift über den vollen Rechnungsbetrag

Softwaretests in Visual Studio 2010 Ultimate Vergleich mit Java-Testwerkzeugen. Alexander Schunk Marcel Teuber Henry Trobisch

Transkript:

Verfahren zur automatischen Erkennung von Software- Mustern im Überblick Seminararbeit zum Hauptseminar Software Engineering in Software Intensive Systems 08.03.2009 Christian Messinger

Inhaltsverzeichnis 1 Einleitung... 3 2 Reverse Engineering... 3 3 Software-Muster... 4 4 Automatische Mustererkennung... 6 4.1 Aufbau von Mustererkennungsverfahren... 7 4.2 Graph Matching & Graph-Reduktion... 9 4.3 Graph Matching & Dekomposition... 13 4.4 Graph Matching & Maschinelles Lernen... 16 4.5 Metriken & Logische Formeln... 20 5 Zusammenfassung und Fazit... 22 6 Literaturverzeichnis... 23 2

1 Einleitung Das Erstellen einer Software, indem man von Grund auf alles neu entwickelt und dabei auf einen ausgereiften Prozess zurückgreift, ist nicht die einzige Herausforderung denen Entwickler gegenüberstehen. Eine typische Aufgabe für einen neu eingestellten Mitarbeiter kann auch so lauten: Der Kollege K. hat bisher unsere Buchhaltungssoftware betreut, sie haben nun seine Stelle. Wir benötigen da aber dringend ein paar Änderungen Dabei kommt oft heraus, dass der nun nicht mehr zur Verfügung stehende Mitarbeiter der einzige war, der sich wirklich mit der Software, Quelltexten sowie Architektur auskannte. Eine Dokumentation ist wenn vorhanden oft unvollständig oder nicht mehr aktuell. Der Programmierstil ist vielleicht auch nicht festgelegt gewesen und Kommentare in den Quellen wurden unter Umständen nur nichtssagend verfasst, oder gleich weggelassen. Der erste Schritt, um Änderungen an der Software machen zu können muss also das Verständnis der Bestehenden sein. Dabei steht nur der Quelltext des zu untersuchenden Systems zur Verfügung. Da diese Aufgabe nicht neu ist wurden schon verschiedene Ansätze entwickelt, um diesen Prozess zu unterstützen. Eine Möglichkeit, um mehr Wissen zu erlangen ist es, bekannte Muster zu suche und zu bewerten. Dabei ist mit dieser Mustersuche etwas anderes gemeint als der gleiche Begriff etwa in der Stimmenerkennung oder der Mustersuche in Bildern, um etwa Beispiel Gesichter zu finden. Die Mustersuche in der Software soll eine bekannte Struktur, wie beispielsweise ein Design Pattern von Gamma et. Al. in [7] beschrieben, finden. In dieser Seminararbeit wird eine Einführung in die automatische Erkennung von Software- Mustern gegeben. Dabei wird der Begriff des Reverse-Engineering erklärt, Software-Muster dargestellt und allgemein auf die automatische Erkennung der Muster eingegangen. In dem größten Teil dieser Arbeit werden schließlich vier Verfahren näher beleuchtet, mit Beispielen erklärt und anhand ihrer Eigenschaften bewertet. 2 Reverse Engineering Große Software-Systeme zu verstehen ist keine triviale Aufgabe. Schon kleine Programme mit nur 1000 Zeilen Quellcode können komplex und schwer durchschaubar sein, auch wenn die Objektorientierung viel zur Übersichtlichkeit beiträgt. Ein großes System hat alleine aufgrund von Investitionsschutz eine lange erwartete Lebensdauer in der es jedoch aktuell auf Anforderungen und Fehlerbehebungen angepasst werden muss, dies wurde schon 1985 erkannt[11]. Der Prozess des Veränderns einer bestehenden Software, um neue Anforderungen abzudecken, nennt sich Reengineering[4]. Das Verändern der internen Struktur einer Software, um die interne Qualität [12] zu erhöhen, ohne jedoch die Funktionalität zu erweitern oder anderweitig das Verhalten der Software zu ändern, nennt sich Refactoring. Sowohl Reengineering als auch Refactoring benötigen ein mehr oder weniger genaues Verständnis der zu ändernden Software. Um dieses zu erhalten gibt es das Reverse Engineering. Dabei wird wissen über das Design, in Einzelfällen bis hin zu den Anforderungen aus der Implementierung abgeleitet. Im Allgemeinen wird dabei davon ausgegangen, dass der Quelltext vorliegt, diese Annahme liegt auch den später vorgestellten Verfahren zu Grunde. Es gibt mehrere Ziele für den Einsatz des Reverse Engineerings. Zwei der Hauptziele sind dabei: 3

Wiedergewinnung von verlorenen Informationen. Der Verlust von Informationen über Fehlerbehebungen, Erweiterungen usw. kann durch geringe Entwicklungsressourcen (Zeit) entstehen, da Dokumentation oft als erstes vernachlässigt wird. Entwicklung einer abstrakteren Sicht auf das Softwaresystem. Ein weiterer klassischer Fall, in dem das genaue Verständnis einer Software gefragt ist, ist das Portieren auf eine neue Technologie und einer damit verbundenen Portierung in eine neue Programmiersprache (Migration). So werden immer mehr ehemalige C++ Projekte in Java oder C# neu erstellt, um neuen Anforderungen gerecht zu werden, die in C++ nur mit sehr hohem Aufwand möglich sind. Besonderer Druck in diese Richtung entsteht, wenn ein Konzern die Unterstützung für eine Technologie einstellt, wie es jüngst mit Visual Basic von der Seite von Microsoft geschehen ist. Es gibt einige Verfahren, die schon seit einiger Zeit den Prozess des Reverse Engineerings vereinfachen. Um die Schrittweite eines solchen Verfahrens von der fertigen Software zurück zum Design oder sogar zu den Anforderungen darzustellen bietet sich ein eindimensionaler Strahl an (siehe Abbildung 1). Dieser stellt dabei die Abstraktion der Implementierung dar. Je mehr von Details abstrahiert wird, desto einfacher ist es einen Überblick über die Zusammenhänge der Software zu bekommen. Nur mit Sicht auf abstrakte Übersichten und konkrete Details ist ein umfassendes Verständnis möglich. Mustererkennung (Decompiler) Architektur Software Muster Modell Quellcode (Binärcode) UML- Diagramme ableiten Abstraktion Abbildung 1: Abstraktions-Ebenen 3 Software-Muster In dieser Seminararbeit werden zwei Arten von Software-Mustern vorgestellt. Zum einen gibt es Muster, die bekannte Lösungen für wiederkehrende Probleme beschreiben. Zum anderen Muster, die immer wieder auftretende Probleme beschreiben. Muster die im Forward-Engineering, also in der Design- oder der Implementierungsphase eines Entwicklungsprozesses genutzt werden, sind geplante Muster. Ursprünglich gehen diese auf eine Idee aus dem Bereich der (Gebäude-) Architektur zurück. Dort wurden zuerst von Christopher Alexander 1977 [1] verschiedene Lösungen für typische Probleme oder Anforderungen bei dem Bau eines Hauses zusammengetragen. Dieser Ansatz wurde daraufhin in verschiedene Bereiche übertragen, darunter auch in die Softwareentwicklung. 4

Der bekannteste und einflussreichste Muster-Katalog für die Softwareentwicklung ist unbestreitbar die Sammlung von Entwurfsmustern (Design Patterns) aus [7]. In ihm werden Lösungen zu immer wieder auftretenden Problemen und Aufgaben in der objektorientierten Programmierung aus drei Bereichen Vorgestellt: Struktur-, Erstellungs- und Verhaltensmuster. Die Beschreibung eines Musters besteht dabei aus einigen Teilen, unter anderem: Ein Name, der genutzt werden kann, um auf das beschriebene Muster zu verweisen. Eine Intention, die das zu lösende Problem skizziert. Die Struktur der Lösung, als UML Diagramm oder in ähnlicher Form. Die Beschreibung der Konsequenzen, die aus der Benutzung des Musters entstehen. Durch solch einen Muster-Katalog ist es möglich 1. das Verständnis der Software zu vereinfachen. Die Intention hinter der Anwendung des Musters ist bekannt und muss nicht mehr erläutert werden. 2. die Qualität der Software zu erhöhen. Die Muster stellen bewährte und vielfach eingesetzte Lösungen dar. Als Beispiel soll das Strategie-Muster dienen. Name Intention Struktur der Lösung Kontext +KontextSchnittstelle() Strategie Ein Algorithmus soll austauschbar sein ohne die Klasse, auf der er operieren soll, zu ändern. 1 -aktuellestrategie 1 Strategie +führeaus() StrategieA +führeaus() StrategieB +führeaus() Abbildung 2: Strategie-Muster (UML) Eine Kontext-Klasse enthält die Daten, auf denen der Algorithmus (die Strategie) arbeiten soll, sowie eine Schnittstelle für diese Daten. Der eigentliche Algorithmus wird durch implementieren einer Schnittstelle realisiert, wobei die Kontext-Klasse nur diese Schnittstelle kennt. Konsequenzen Der Kontext und der Algorithmus sind sauber getrennt. Der Algorithmus kann ausgetauscht werden und ist somit je nach Einsatzzweck leicht anpassbar. Außer den Entwurfsmustern gibt es aber auch Architektur-Muster oder kleinere wiederkehrende Quelltext-Strukturen, manchmal auch Cliché genannt. Neben diesen bewährten Mustern gibt es auch Muster die durch ihr Auftreten Probleme aufzeigen. So gibt es analog zu den Design-Pattern auch sogenannte Anti Patterns die ebenfalls Lösungsansätze zu wiederkehrenden Problemen darstellen, wobei die Nachteile der gewählten Lö- 5

sung jedoch gegenüber den Vorteilen überwiegen. Ein solches Muster ist beispielsweise das Swiss Army Knife. Es beschreibt den Versuch verschiedenste Funktionalität für unterschiedlichste Aufgaben in einer Klasse zu vereinen. Dies kann zum Verlust der Übersichtlichkeit führen und dafür sorgen, dass eine Änderung dieser Klasse, durch die vielen Einsatzorte (=Wechselwirkungen) andere Funktionen beeinträchtigt. Neben den Anti Patterns, die konkrete Probleme darstellen, gibt es auch die sogenannten Bad Smells[6], die eher Indikatoren für Probleme sind. Ein solcher Bad Smell kann, muss aber nicht, auf unsaubere und nicht konsequent objektorientierte Programmierweise hinweisen. Diese werden in der Literatur im allgemeinen nicht als Muster beschrieben. Der nun als Beispiel dargestellte Bad Smell Feature Envy wird später in dieser Arbeit erneut benötigt. Name Beschreibung Problem Feature Envy Eine Methode benutzt mehr Attribute einer anderen Klasse als die aus der eigenen. Hinweis auf schlechte Kapselung class X { public int mya; public int myb; public int myc; } class Y { public X myx; void dosomething()//feature-envy { int a = myx.mya; int b = myx.myb; myx.myc = a + b; } } Abbildung 3: Bad Smell - Feature Envy Allgemein kann man sagen, dass das Erkennen eines Musters einen Erkenntnisgewinn bringt; entweder über die Intention des Entwicklers oder über mögliche Probleme. 4 Automatische Mustererkennung Das Ziel der automatischen Erkennung von Software-Mustern ist es den Entwickler im Reverse- Engineering-Prozess zu unterstützen, sodass er schnell bekannte Teile erkennen und richtig einordnen kann. Dies ist vor allem wenn die zu untersuchenden Systeme groß sind ansonsten sehr aufwändig. Dabei spielt sowohl die reine Menge an Quelltext als auch die Komplexität der Zusammenhänge eine Rolle. Natürlich kann ein Entwickler selber erkennen, dass es beispielsweise von einer Klasse nur eine einzige, statisch zugängliche, Instanz gibt. Wenn jedoch in einem UML-Diagramm die Klasse direkt per Annotation als Ausprägung des Singleton Musters ausgezeichnet wird, so spart dies viel Zeit und Aufwand. Daher gibt es einige Ansätze, welche die Mustersuche automatisieren. Es gibt viele verschiedene Ansätze zur automatischen Software-Mustererkennung, die anhand einiger Kriterien verglichen werden können. Dabei werden zuerst die Ausgaben der Mustererkennung Kategorisiert: 6

True Positive: Ein richtig gefundenes Muster. False Positive: Ein Fund, der in Wirklichkeit nicht das angegebene Muster enthält. True Negative: Eine Stelle, die richtigerweise als kein Fund bezeichnet wird. False Negative: Ein nicht erkanntes Vorkommen eines Musters. Diese Funde oder Nicht-Funde lassen sich quantitativ nach einem Suchlauf bestimmen. Dadurch werden zwei weitere, abgeleitete, Metriken bestimmt, die die Güte des verwendeten Suchalgorithmus, anhand eines Testlaufes mit einem bekannten Quelltext, angeben. Der Recall gibt dabei an, wie groß die Erkennungsrate des Algorithmus ist. Dies wird als Verhältnis von den korrekt gefundenen Mustern zu den vorhandenen Mustern ausgedrückt. Ein Recall von 1 gibt an, dass alle Muster erkannt wurden; 0 bedeutet, dass es keinen korrekten Fund gab. Recall = TruePositives TruePositives + FalseNegatives Als zweite Metrik gibt es die Precision, die die Glaubwürdigkeit eines Fundes angibt. Dabei wird das Verhältnis aller korrekten Funde zu allen Funden, also auch inkorrekten, dargestellt. Eine Präzision von 1 stellt dabei das Ideal dar, bei dem alle Funde auch wirkliche Muster in dem untersuchten Quelltext darstellen. Eine Präzision von 0 tritt nur auf, wenn alle Funde keine wirklichen Muster sind. Präzision = TruePositives TruePositives + FalsePositives Ein weiteres wichtiges Kriterium für die Bewertung einer Mustererkennung ist ihre Effizienz. Dabei ist vor allem die Laufzeiteffizienz im Bezug auf die Größe des zu untersuchenden Systems wichtig. Aber auch die Laufzeit in Bezug auf die Größe des Musterkataloges ist von Bedeutung, da meist nicht nach nur einem isolierten Muster, sondern nach mehreren Mustern und Mustervarianten gesucht wird. Ein viertes Kriterium, das sich zum Vergleich der Verfahren heranziehen lässt, ist die Art und die Mächtigkeit der Spezifikation von Mustern. Dazu gehören auch der Umgang mit Varianten eines Musters und die Frage, ob diese alle einzeln spezifiziert werden müssen. 4.1 Aufbau von Mustererkennungsverfahren Alle Verfahren zum Erkennen von Software-Mustern haben einen ähnlichen Aufbau, zu sehen in Abbildung 4. Als Eingabe fungiert dabei meist direkt der Quelltext der zu untersuchenden Software, manchmal aber auch ein Modell, wie zum Beispiel UML-Diagramme. Eine Vorverarbeitung erstellt nun aus der Eingabe ein Modell, auf dem der Suchalgorithmus arbeiten wird. Dies kann eine Graphdarstellung, im Speziellen die abstrakte Syntax eines UML-Modelles, aber auch eine Menge von Metriken oder (prädikaten-) logischen Aussagen sein. Die Musterspezifikation dient als weitere Eingabe in den Suchalgorithmus, wobei sie oft vom Anwender nicht mehr neu erstellt wird. Für Programme wie Reclipse[21] gibt es zum Beispiel schon Kataloge mit allen 23 Mustern aus [7]. Meist ist wird die Musterspezifikation in einer ähnlichen Syntax wie die Zwischendarstellung erstellt, auf der letztendlich gesucht wird. Manche nicht sehr flexible Ansätze haben keine Spezifikationen als solches, diese sind fest in den Algorithmen enthalten. So ist die Suche 7

zwar beliebig mächtig aber schlecht von einem Benutzer anpassbar. Nach dem Durchführen des Suchalgorithmus werden die Funde ausgegeben, was in Form von Annotationen in UML- Klassendiagrammen, durch einen Report mit Zeilennummern oder ähnlichem geschehen kann. Zwischendarstellung Muster- Spezifikation Algorithmus Muster-Fund Quelltext oder Modell Vorverarbeitung Abbildung 4 Automatische Mustererkennung (Ablauf) Die Ansätze zur Mustererkennung lassen sich nach verschiedenen Kriterien kategorisieren. Ein Kriterium ist die Art bzw. der Inhalt des internen Modells, auf dem der Algorithmus arbeitet. Dabei lassen sich sowohl das Modell der Zwischendarstellung wie auch das in der Musterspezifikation gewählte Modell zur Unterscheidung heranziehen. Im Folgenden werden einige der genutzten Arten aufgezählt. Die Liste ist jedoch nicht als vollständig zu betrachten. Struktur Eine Darstellung der Klassen-Struktur, oft mit Kanten die Beziehungen wie Aufrufe oder Benutzung als Rückgabetyp darstellen. Verhalten Die Software wird auf ihr Laufzeitverhalten hin analysiert. Dabei wird sie oft Instrumentalisiert und daraufhin ausgeführt. Die dabei gewonnenen Daten fließen in die Zwischendarstellung mit ein. Metriken Es werden verschiedene Metriken wie Lines of Code oder auch die Anzahl an Methoden einer Klasse sowie viele weitere ausgewertet. Logische Aussagen Es werden Aussagen, zum Beispiel über Beziehungen von Klassen, in Boolescher Logik oder auch Prädikatenlogik formuliert, die die Software beschreiben. Diese werden daraufhin untersucht. Eine andere Eigenschaft der Ansätze sind verschiedene Methoden, die genutzt werden, um die Musterinstanzen aufzuspüren. Dabei sind mit Methoden Suchverfahren und andere Algorithmen aus der Informatik gemeint. Beispielhaft seien hier drei Methoden genannt: Graph-Matching In einem Wirtsgraphen wird ein anderer Graph gesucht. Ein Fund muss dabei ein Subgraph des Wirtsgraphen sein, der isomorph zu dem anderen Graphen ist. Dabei sind die Knoten und Kanten oft typisiert, um etwa Aufruf- und Vererbungs-Kanten auseinander halten zu können. Maschinelles Lernen Constraint- Solver Manchmal ist es sinnvoll, Methoden der Künstlichen-Intelligenz zu benutzen, um die Mustersuche zu unterstützen. Dadurch kann etwa die Klassifizierung von Funden durch gelerntes Wissen verbessert werden. Ein Constraint Solver sucht in einem meist sehr großen Zustandsraum nach Lösungen für ein Problem. Eine Lösung muss dabei vorher zu spezifizierende Bedingungen erfüllen, die sogenannten Contraints. Oft basiert solch ein Solver auf Meta-Heuristiken wie genetischen-algorithmen[9] oder Tabu Search[8]. 8

In den folgenden vier Unterkapiteln werden konkrete Ansätze zur Mustererkennung kurz vorgestellt und genauer beleuchtet. Zu jedem werden dabei kurz Vor- und Nachteile anhand der oben genannten Kriterien erläutert. Der Schwerpunkt in der Darstellung der Verfahren wird dabei jeweils auf die Eigenschaft gelegt, die das Verfahren auszeichnet. Drei der untersuchten Verfahren basieren auf einer Graphdarstellung mit darauf angewandtem Graph Matching. Das in [14] vorgestellte Verfahren konzentriert sich auf die Verkleinerung und Planarisierung des Wirtsgraphen. Der[20] vorgestellte Ansatz ist sehr komplex, daher liegt hier der Fokus auf der Fähigkeit der Dekomposition von Mustern. Zur Verbesserung der Präzision wird in[5] Maschinelles Lernen eingesetzt. Der einzige Ansatz ohne Graph Matching wird in [19] beschrieben und versucht anhand von Software-Metriken Entwurfsprobleme aufzudecken. 4.2 Graph Matching & Graph-Reduktion Der, der in [14] vorgestellt wird kommt aus Schweden. Es ist bei den Verfahren einzuordnen, welche die Struktur betrachten, und ein Graph Matching darauf durchführen. Hinzu kommt ein Verfahren, um die Komplexität der Suche zu verringern. Als Eingabe werden Java Quelltexte unterstützt. Dieses Verfahren konzentriert sich vor allem darauf; in großen zu untersuchenden Systemen Laufzeiteffizient zu sein. Im Prinzip entspricht ein Graph Matching einer Suche nach einem isomorphen Subgraphen. Dieses Problem ist jedoch NP-hart und somit bei großen Graphen nicht in vertretbarer Zeit zu lösen. Ist der Graph jedoch planar, so existieren sogar Algorithmen mit linearer Laufzeit. Dieses Wissen versucht das Verfahren auszunutzen. Die Idee ist es den Wirtsgraphen, der die Struktur des Quelltextes darstellt, so zu reduzieren, dass ein planarer Graph übrig bleibt. Dies ist nicht immer möglich, zum einen, da es auch nicht planare Muster geben könnte, und zum anderen, da man keine für das Muster signifikanten Bestandteile entfernen darf. Es wird jedoch argumentiert, dass auch dann eine Reduktion des Graphen einen Geschwindigkeitsvorteil bringt. Da ein Graph, der die Struktur darstellt, auch Kanten enthält, die für das gesuchte Muster nicht von Bedeutung sind ist es möglich diese zu entfernen, ohne die Suche nach dem Muster zu gefährden. Als Eingabe wird in dem Verfahren der Quelltext genommen, der zuerst in eine Graph- Repräsentation überführt wird. Diese wird daraufhin anhand der Musterspezifikation eines Musters gefiltert. Kanten, die nicht in der Musterspezifikation vorkommen sind danach auch nicht mehr in dem Wirtsgraphen vorhanden. Der entstandene verkleinerte Graph dient daraufhin als Grundlage für die eigentliche Mustersuche per Graph Matching. Der Ablauf wird in Abbildung 5 dargestellt. Muster Graph Quelltext Muster Fund Filtern Matching Vorverarbeitung Graph- Repräsentation verkleinerter Graph Abbildung 5: Matching & Graph-Reduktion (Ablauf) 9

4.2.1 Vorgehen Die Graph-Repräsentation besteht aus Knoten und Kanten. Die Knoten stellen dabei Klassen und Methoden, die Kanten Beziehungen zwischen diesen dar. Beispiele für Kanten sind dabei die Containment Kanten, die von einer Klasse zu einer über ein Feld referenzierten Klasse führen, oder auch Returns Kanten, die von einer Methode auf ihren Rückgabetyp zeigen. Dazu gibt es einige Kanten, die auf simple Typen wie Integer oder Strings zeigen, sowie extra Kanten für Container -Klassen wie etwa ArrayList aus der Java-Runtime. Hinzu kommen Eigenschaften der Knoten wie IsPublic oder IsAbstract. Die Kanten werden recht feingranular definiert, um diese bei dem Filterungsprozess auseinander halten zu können. Weitere Kanten werden in dem folgenden Beispiel dargestellt. Zur Verdeutlichung der Verfahren soll das folgende Beispiel (Abbildung 6) dienen. Es stellt eine Karteikarten-Verwaltung mit einem per Strategie-Muster abgekoppelten Sortier-Algorithmus dar. Dieses Muster gilt es für die Mustersuche zu erkennen. class RecordCardManager{ List cards; ISorter currentsorter; } public void swapcards(int a,int b){ /*cards[i]<->cards[j]*/} public Card drawcard(int i){...} public void putcard(int i,card c){..} public int getnumberofcards(){...} public void addcard(card c){putcard(0,c);currentsorter.sort(this);} /*...*/ public interface ISorter { void sort(recordcardmanager m); } public class InsertionSort implements ISorter { void sort(recordcardmanager m){ /*...*/ } } public class QuickSort implements ISorter { void sort(recordcardmanager m){ /*...*/ } } public class MergeSort implements ISorter { void sort(recordcardmanager m){ /*...*/ } } Abbildung 6: Beispielquelltext, Karteikartensystem Diesen Quelltext-Ausschnitt kann man nun in die Graphdarstellung des Ansatzes überführen. Dazu werden alle Methoden, Klassen und einfache Datentypen in Knoten gewandelt, was der Übersicht halber in Abbildung 7 als Zwischenschritt dargestellt wird. 10

List RecordCard Manager sort ISorter swapcards drawcard putcard getnumber OfCards addcard InsertionSort QuickSort MergeSort int Card Abbildung 7: Graphdarstellung, Zwischenschritt nur mit Knoten Durch Hinzufügen der Beziehungen als Kanten und der Eigenschaften entsteht die Graphdarstellung des Quelltextes (Abbildung 8). Containment IsPublic ContainsPrimitiveParam Container IsPublic ContainsMethod ContainsMethod ContainsMethod ContainsMethod ContainsMethod ContainsPrimitiveParam IsPublic ContainsPrimitiveParam Calls IsPublic returnsprimitive ContainsParam IsPublic ContainsParam Containment Calls ContainsMethod Overriding Overriding IsInterface Implements Implements Overriding Implements ContainsParam ContainsPrimitiveParam returns int Abbildung 8: Graphdarstellung des Beispiels Um nun ein Muster zu, suchen wird das Muster, in diesem Fall das Strategie-Muster, vergl. Abbildung 2, in der gleichen Art als Graph spezifiziert und beschreibt so eine Ausprägung des Musters. Zu beachten ist dabei, dass aufgrund des Filtervorganges in einem Schritt nur eine Variante des Musters gesucht werden kann. Das Beachten von verschiedenen Implementationsvarianten wäre nur durch Weglassen von Klassen, Methoden oder Beziehungen, in denen sich die Varianten unterscheiden, möglich. ContainsMethod Containment IsInterface Overriding Overriding Implements Implements Abbildung 9: Musterspezifikation des Strategie-Musters 11

Um den Wirtsgraphen zu vereinfachen und ihn eventuell sogar planar zu machen werden nun alle Kanten entfernt, die in der Musterspezifikation nicht vorkommen. Je kleiner das Muster ist beziehungsweise je weniger verschiedene Merkmale es enthält, desto besser lässt sich der Wirtsgraph verkleinern. Die Musterspezifikation des Strategie-Musters enthält nur die Kanten Overriding, Implements, Containment, sowie ContainsMethod. Alle anderen können nun entfernt werden, was in dem Graphen in Abbildung 10 resultiert. Containment IsPublic IsPublic IsPublic IsPublic IsPublic Containment ContainsMethod Overriding Overriding IsInterface Implements Implements Overriding Implements ContainsParam ContainsParam int Abbildung 10: Gefilterter Wirtsgraph Der Wirtsgraph ist nun sogar planar, womit der schnelle Matching-Algorithmus anwendbar ist. Der Fund wird der Vollständigkeit halber in Abbildung 11 dargestellt Contains ContainsMethod IsInterface Overriding Overriding Overriding Implements Implements Implements Abbildung 11: Musterfund 4.2.2 Bewertung des Verfahrens Die Stärke des Verfahrens liegt ganz klar in dem Umgang mit großen Systemen. Die daraus resultierenden Graphen sind für ein ungefiltertes Graph Matching oft zu groß und die Laufzeit explodiert aufgrund der Komplexität. In der Arbeit, die dieses Verfahren vorstellt, wurde in 14 von 36 Fällen durch das Herausfiltern von unbedeutenden Kanten Planarität erreicht. Mithilfe eines linearen Matching-Algorithmus werden dann nur drei Schritte mit einer Komplexität von O(n), mit n als Anzahl der Kanten und Knoten des Wirtskraphen, benötigt: Filtern, Planaritäts-Test, Planares-Matching. Ist der Planaritäts-Test negativ, so kann kein optimiertes Verfahren zur Suche angewendet werden. Doch der Vorteil des kleineren Graphen bleibt laut den Autoren des Verfahrens als Vorteil erhalten. Hier stellt sich die Frage, ob der Algorithmus für das Graph Matching denn alle Kanten, auch solche die nicht von Bedeutung sind mit betrachtet. Ein gezielter Zugriff auf die benötigten Kan- 12

ten stellt keinen Mehraufwand dar, es sei denn die Verifizierung eines Musters geschieht erst anhand eines gefundenen Matchings. Dies könnte daraus resultieren, dass der lineare Matching- Algorithmus und der Planaritäts-Test in einer generischen Implementierung verwendet wurde. Ein Nachteil, der aufgrund des Änderns des Wirtsgraphen durch das Filtern entsteht ist jedoch, dass nur ein Muster beziehungsweise eine Variante eines Musters nach einem Filter-Durchgang gesucht werden kann. Es stellt sich die Frage, ob bei dem Suchen nach mehreren Mustern, etwa eines Kataloges, die Unmöglichkeit der parallelen Ausführung und die Zusatzkosten des Filterns durch die Geschwindigkeitsgewinne aufgrund der verkleinerten Graphen übertroffen werden. 4.3 Graph Matching & Dekomposition Das Problem der verschiedenen Varianten geht der nächste Ansatz an [20]. Es handelt sich wiederum um ein Graph Matching auf der Struktur der Software. Die Idee, auf die hier am meisten eingegangen werden soll, ist es, die Möglichkeit zu schaffen, einzelne Teile eines größeren Musters in verschiedenen Varianten zu spezifizieren. Eingesetzt wird das Verfahren beziehungsweise eine geänderte und erweiterte Version in der Fujaba-Reverse-Engineering-Umgebung. Die Eingabe für dieses Verfahren ist wiederum Quelltext, der daraufhin in eine Zwischendarstellung, einen abstrakten Syntaxgraphen (ASG), gewandelt wird. Auf diesem Graphen wird daraufhin ein Matching mit einem ganzen Katalog von Mustern durchgeführt. Die Ergebnisse der Mustersuche werden als Annotationen in einem UML-Klassen-Diagramm und als Ergebnislisten mit beteiligten Methoden, Klassen und anderen Elementen, dargestellt. Vorverarbeitung Muster- Katalog Muster-Fund Quelltext Matching Abbildung 12: Graph Matching & Dekomposition (Ablauf) 4.3.1 Vorgehen Die Spezifikation eines Musters beruht zum einen auf Elementen, die genau so auch in dem ASG vorkommen; sowie andere Elemente, die in [18], im Detail vorgestellt werden. So gibt es logische Ausdrücke und Negationen ebenso wie die Nutzung von Funden anderer Muster, die als Unter- Muster auftreten. Auf diese werden wir uns hier konzentrieren. Dazu betrachten wir in Abbildung 13 wieder das Beispiel des Strategie-Musters das schon in (Abbildung 2) dargestellt wird. Zu den Objekten der Klassen UMLClass, UMLMethod und UMLAttr werden bei dem Graph Matching Instanzen im ASG gesucht, die dort die vorhandenen Klassen, Methoden und Attribute des Quelltextes darstellen. Die Kante mit der Beschriftung attrs besteht zwischen einer Klasse und ihren Attributen, attrtype verbindet ein Attribut mit seinem Typ und methods eine Klasse mit vorhandenen Methoden. Die Namen der Objekte sind beliebig und haben keinen Einfluss. 13

führeaus : UMLMethod abstract : bool = true methods kontext : UMLClass attrs aktuellestrategie : UMLAttr attrtype strategie : UMLClass <<create>> interface interface context <<create>> :Strategy 0,6/0,5 <<create>> strategy :InterfaceImpl :InterfaceImpl implementation implementation konkretestrategie : UMLClass konkretestrategie2 : UMLClass Abbildung 13: Musterspezifikation Strategie-Muster (Ansatz 2) Solch eine Musterspezifikation ist im Grunde eine Graphtransformations-Regel. Sie gibt auf der einen Seite an, was gesucht wird, und beschreibt welche Elemente hinzugefügt werden. Dazu wird jeweils ein Annotations-Knoten, wie der mit :Strategy beschriftete Knoten, und benannte Kanten zu Teilnehmern des Musters mit dem Stereotyp <<create>> gekennzeichnet. Teil des zu suchenden Teilgraphen können auch wiederum Annotationen sein, die somit für ein anderes Muster stehen. In dem Beispiel sind dies die Knoten vom Typ InterfaceImpl. Sie sollen dabei das Konzept der Spezifikation[17] in der objektorientierten Programmierung darstellen, bei der durch Vererbung eine Schnittstelle erfüllt werden muss. Dies ist zum Beispiel in Java auf verschiedene Arten implementierbar: durch das Implementieren eines Interfaces oder durch das Ableiten von einer abstrakten Klasse. Welches davon genutzt wird, ist für das Strategie-Muster nicht von Bedeutung. Diese beiden Varianten werden nun einzeln spezifiziert: UseInterface extends InterfaceImpl interface <<create>> :UseInterface 0,9 <<create>> <<create>> implementation : UMLClass stereotype = interface superclass : UMLGeneralization subclass : UMLClass abstract : bool = false Abbildung 14: Spezifikation implementiert mit einem Interface 14

UseAbstractClass extends InterfaceImpl <<create>> :UseAbstractClass 0,6 <<create>> interface <<create>> implementation : UMLClass abstract : bool = true superclass : UMLGeneralization subclass : UMLClass abstract : bool = false Abbildung 15: Spezifikation implementiert durch das Ableiten von einer abstrakten Klasse Die beiden Untermuster des Beispiels erweitern jeweils das gleiche Untermuster und müssen daher eine kompatible Annotation erstellen. Das heißt die beiden erstellten Annotationskanten müssen mit interface und implementation beschriftet sein und die annotierten Objekte müssen jeweils vom gleichen Typ sein, also UMLClass. Einige von diesen Untermustern stehen deutlicher für das Konzept des Musters, von dem sie erben, andere sind nicht so eindeutig. So deutet die Implementierung eines Java-Interfaces ganz klar auf eine Spezifikation hin, während das Benutzen einer abstrakten Oberklasse auch beispielsweise eine Spezialisierung sein kann[17]. Daher bekommen alle Muster einen sogenannten fuzzy-belief Wert. Dieser gibt an, wie eindeutig die Musterspezifikation für das Konzept des Musters steht, von dem geerbt wird. Der Wert liegt dabei zwischen 0 und 1 und könnte als Wahrscheinlichkeit aufgefasst werden, dass ein Fund des Musters auch wirklich ein True Positive für das Konzept des geerbten Musters ist. Nun ist es möglich ein Muster M mit genutzten Untermustern zu bewerten, indem das Minimum der fuzzy-belief Werte aller genutzten Musterspezifikationen, inklusive des Wertes für M genommen wird. Besteht eine Strategie in unserem Beispiel aus einem Interface, das die konkreten Strategien implementieren, so haben die Untermuster einen fuzzy-belief von 0,9 das Strategie Muster ist selber jedoch nur mit 0,6 bewertet 1. Daher wird der Musterfund mit 0,6 bewertet. Um die Komplexität einzudämmen, die durch zu viele Annotationen entstehen kann, und die Übersicht durch unverlässliche Musterfunde nicht zu gefährden ist es zusätzlich möglich eine untere Schranke anzugeben, die dafür Sorgt, dass ein Musterfund nicht mehr als ein solcher angesehen wird, wenn die Bewertung eines Untermusters kleiner als eben diese Schranke ist. In der (Abbildung 13) ist die Schranke 0,5. Auch die Nutzung des schwächeren Untermusters mit seiner Bewertung von 0,6 würde also nicht zum Ignorieren eines Fundes führen. 4.3.2 Bewertung des Verfahrens Das Verfahren ist sehr umfassend und wurde hier nur zu einem kleinen Teil erklärt. Es ist sehr flexibel und gerade mit den in [18] genannten Erweiterungen sehr vielen Mustern gewachsen. Dazu gehören auch Spezifikationsregeln, die auf die Abläufe in einem Methodenrumpf eingehen. Der hier vorgestellte Ansatz des Umgangs mit Varianten kann sehr gut mit verschiedenen Arten von Implementierungen einzelner Teile von Mustern umgehen. Die fuzzy-belief Werte müssen dabei jedoch aufgrund von Erfahrung geschätzt oder durch spätere Evaluationsreihen abgeschätzt werden. Inwiefern die Regel, dass das Minimum der Bewertungen von Untermustern un- 1 Dies gibt der erste Wert unter dem Label :Strategy in der Abbildung 13 an. 15

ter Umständen stellvertretend für das ganze Muster steht, verbessert werden kann wird wiederum in [18] diskutiert. Die Möglichkeit Untermuster mehrfach zu benutzen, ist alleine schon eine große Hilfe bei der Spezifikation der gesuchten Muster. Dadurch können etwa Referenzen und Assoziationen implementierungsunabhängig definiert werden. Solch eine Dekomposition ist auch mit dem SPQR- Verfahren [16] möglich. Die Musterspezifikationen dort sind jedoch aufgrund ihres Formalismus längst nicht so zugänglich wie die hier vorgestellte, an UML-angelehnte Spezifikationssprache. Der Umgang mit großen ASGs mit der Optimierung der Laufzeiteffizienz ist in [20] nicht explizit behandelt worden, wie es in (4.2) getan wird. Es wird dagegen auf die Verbesserung der Laufzeit durch das verallgemeinern von Mustervarianten eingegangen. Dadurch müssen weniger Muster gesucht werden, was jedoch den Suchraum nicht einschränkt. Die Komplexität aufgrund des großen ASGs wird jedoch in der Dissertation [13] angegangen, womit die Laufzeit laut dieser Arbeit im Durchschnitt nur polynomiell ist. Durch die sehr Umfangreiche und genaue Musterspezifikationssprache ist es möglich False Positives und False Negatives durch inkrementelles Verbessern der Spezifikationen in den Griff zu bekommen. Dies ist ein ganz anderes Vorgehen als es in dem folgenden Verfahren vorgestellt wird. 4.4 Graph Matching & Maschinelles Lernen Trotz schon sehr genauer Formalismen und Spezifikationssprachen kommt es beim Graph Matching immer noch zu False Positives. Diesem Problem will sich der dritte vorgestellte Ansatz [5] explizit annehmen. Die Idee dabei ist es, verschiedene Eigenschaften nicht direkt auszuwerten, sondern eine Künstliche-Intelligenz (KI) darauf zu trainieren, False Positives zu erkennen. Dadurch ist es möglich die Musterspezifikation weniger genau zu gestalten, so dass viele Mustervarianten damit gefunden werden, und das Risiko der zusätzlichen False Positives durch die KI lösen zu lassen. Der Ablauf ist wieder ähnlich wie bei den anderen Graph-Matching orientierten Verfahren und wird in Abbildung 16 dargestellt. Graphen- Repräsentation Muster- Graph Gelerntes (Muster) Wissen Musterkandidat, Metriken Sicherer Muster- Fund Matching Filtern Quelltext Vorverarbeitung Abbildung 16: Graph Matching & Maschinelles Lernen (Ablauf) Der Quelltext wird in eine im Papier [5] nicht näher beschriebene, Graphdarstellung überführt. Darauf wird ein Matching mit einer Musterspezifikation durchgeführt, die Klassen, Methoden und Attribute sowie Beziehungen zwischen diesen beschreibt. Eine Analyse des Kontrollflusses 16

ist nicht möglich; es wird auf die reine Struktur geschaut. Zusätzlich zu dem gefundenen Musterkandidaten werden einige Metriken extrahiert, die der KI vorgelegt werden. Diese Kategorisiert nun in False Positive und True Positive. Die True-Positives ergeben die Menge der von diesem Verfahren gefundenen Muster. 4.4.1 Vorgehen Um den Ansatz mit der nachgeschalteten Filterung erfolgreich einsetzen zu können, benötigt man einen großzügigen Graph-Matching-Algorithmus beziehungsweise eine Musterspezifikation, die eher zu allgemein ist als dass sie False Negatives produziert. Nur so kann die künstliche Intelligenz dabei helfen, neben dem Vermeiden von False Positives, auch möglichst viele True- Positives zu erhalten. Ist ein False Negative in der Graph-Matching-Phase aufgetreten, so ist dies nicht mehr reparabel. In dem zugrunde liegenden Papier [5] wird diese Eigenschaft des Suchalgorithmus nicht explizit erwähnt, es wird stattdessen nur auf das verhindern von weiteren False Positives eingegangen. Die Präzision des genutzten Graph-Matching Ansatzes wird von den Autoren selber als verbesserungswürdig dargestellt und als Motivation für den Einsatz der KI genannt. Die Eigenschaften der Muster, die als Eingabe in die KI genutzt werden ( Predictors genannt), sind verschiedene Software-Metriken, die zu jedem gesuchten Muster ausgewählt werden. Sie stellen dabei Eigenschaften eines Teilnehmers, wie etwa Kontext und Strategie in Abbildung 2, oder Beziehungen zwischen den Teilnehmern in Zahlen dar. So sind Methodenanzahl, Anzahl von Unterklassen aber auch komplexere und auf das gesuchte Muster stark zugeschnittene Eigenschaften Gegenstand der Bewertung durch die KI. Ein paar Predictors für das Strategie-Muster sind in Tabelle 1 aufgelistet. Dabei sind die ersten beiden nicht aus dem Papier entnommen, sind aber Beispiele, die durchaus Sinn machen könnten. Inwiefern die Aussage zu der Anzahl der konkreten Strategien (Predictor 5) einen Einfluss darauf hat, ob ein Fund ein echtes Muster ist, ist nicht direkt ersichtlich. 1. NumberOfCallsFromContextToStrategy Aufruf einer Strategie Methode von der Kontext- Klasse aus. Vermutlich hoch bei State- Pattern. Also niedrig beim Strategie-Muster. 2. NumberOfNonAbstractStrategyMethods Wie der Name schon sagt, die Anzahl der Implementierten Methoden in einer Strategie. 3. InheritanceContext Anzahl der Unterklassen die von der Kontext- Klasse abgeleitet wurden. Vermutlich hoch bei einem Bridge-Pattern. Also niedrig bei Strategy. 4. AlgorithmComplexity Ein Wert, der die Anzahl von Schleifen und Rekursionen in den Methoden der konkreten Strategien darstellt. Sollte hoch sein, da eine Strategie ja einen Algorithmus darstellt. 5. ConcreteStrategy Anzahl an konkreten Strategien. Wahrscheinlich relativ hoch bei einem echten Strategy- Pattern. Tabelle 1: Predictors für das Strategie-Muster Zu jeder dieser Eigenschaften wird in der Arbeit direkt angegeben, wie sie die Erkennung des Musters beeinflussen soll. Dies untergräbt ein wenig die Eigenständigkeit der KI und degradiert sie zu einer einfachen Suche nach den richtigen Schwellwerten. Man hätte auch weniger zielgerichtet generische Metriken, zu den Teilnehmern des Musterfundes und den Beziehungen zwi- 17

schen diesen, definieren können. Dies wäre zwar zu Lasten der Lerndauer gegangen, hätte jedoch den Schritt der Spezifizierung der Predictors gespart und der KI mehr Freiheiten bei der Bildung der Hypothesen gegeben. Die Auswahl der Predictors wäre dadurch der KI mit übertragen worden. Schwierig ist natürlich, dass dadurch auch die Trainingsmenge größer werden müsste, man bräuchte also sehr viele geprüfte Implementierungen der Muster. 4.4.1.1 Maschinelles Lernen Die Verfahren zum Maschinellen Lernen bilden, sind sie erst einmal trainiert, Merkmalsvektoren auf Kategorien ab. Die Kategorien sind in diesem Fall K = {FalsePositive, TruePositive}. Die Merkmale sind Software-Metriken beschrieben durch reelle Zahlen, ein Merkmalsvektor ist also aus der Menge R n wobei n die Anzahl der Merkmale (Predictors) ist. Ziel ist es eine Abbildung R n {FalsePositive, TruePositive} zu erhalten. Dazu werden dem Verfahren Trainingsbeispiele mit einer richtigen Zuordnung zu einer Kategorie gegeben. In dem Papier werden zwei Verfahren des Maschinellen Lernens verwendet. Zum einen ein Entscheidungsbaum mit dem Lernverfahren C4.5 [15], welches eine Erweiterung des bekannten ID3 Algorithmus ist, und zum anderen ein Neuronales Netz. Für weitere Details des Entscheidungsbaum basierenden Verfahren wird hier nur auf die Quelle [15] verwiesen. Neuronale-Netze werden nun ein wenig näher vorgestellt. Die Idee von Neuronalen Netzen ist durch die Gehirnforschung motiviert und basiert auf dem Aufbau von Synapsen. Diese bekommen Impulse und feuern selber Impulse, sobald ihre Reizschwelle überschritten ist. Ganz ähnlich sieht auch ein künstliches Neuron aus, siehe Abbildung 17. x 1 x 2 w 1 w 2 w n f s Ausgabe a x n Abbildung 17 Ein künstliches Neuron Es besteht aus n Eingängen x 1 x n = x, in unserem Fall die Werte der Predictors, und einem Ausgang a, wiederum eine reelle Zahl. Die einzelnen Eingänge sind individuell gewichtet mit den Gewichten w 1 w n = w R n und ergeben als derart gewichtete Summe die Eingabe für eine Schwellwertfunktion, wie beispielsweise die in Abbildung 18 dargestellte. 1,5 1 0,5 0-9 -7-5 -3-1 1 3 5 7 9 Abbildung 18: Verlauf der Fermifunktion, eine typische Schwellwertfunktion Beim Lernen werden die Gewichte w angepasst, wenn das Beispiel falsch klassifiziert wurde. Dazu existieren verschiedene Verfahren, auf die hier nicht näher eingegangen wird. Eine gute 18

Übersicht dazu bietet [10]. Mehrere Neuronen werden in einem Netz miteinander verbunden. Dabei gibt es drei Arten von Schichten, wobei die Neuronen der benachbarten Schichten jeweils paarweise verbunden sind: Eine Eingabeschicht, beliebig viele versteckte Schichten und eine Ausgabeschicht. Bei der Entscheidung zwischen True Positive und False Positive wird nur ein Ausgabe-Neuron benötig. Liegt der Ausgabewert nahe der 1, so gilt dies als True Positive. Eingabe Versteckt Ausgabe P r e d i c t o r - W e r t e Ausgabe True Positive / False Positive Abbildung 19 Neuronales Netz mit 2 versteckten Schichten 4.4.2 Bewertung des Verfahrens Das Verfahren hat den Vorteil, dass es Eigenschaften ausnützen könnte, auf die man nicht gekommen wäre, die somit auch nicht in einem Algorithmus abgebildet worden wären. Durch die eingeschränkte und sehr zielgerichtete Auswahl der Metriken wurde dies jedoch zu Gunsten eines schnelleren Lernerfolges sein gelassen. Dies sieht man daran, dass die Predictors in dem Papier alle mit einer Begründung warum sie gewählt wurden und wie diese gewertet werden sollten versehen wurden. Das Maschinelle Lernen ersetzt somit nur noch empirische Untersuchungen über die Schwellwerte der gewählten Predictors. Der Ansatz des Maschinellen Lernens als Ersatz des Experten, der die False Positives herausfiltern muss, ist vielversprechend und verringert etwa in dem Evaluationsteil des Papiers die Anzahl der False Positives um 94%. Das Problem ist nur, dass bei einem neuen Muster drei Schritte nötig sind: Spezifizierung des Musters mit einem Mustergraph Auswahl der Predictors und Implementierung der Metrikberechnungen Auswahl von Trainingsbeispielen mit Klassifizierung und Training der KI Dadurch ist das Verfahren zu inflexibel, um an neue, vielleicht sehr domänenspezifische Muster angepasst zu werden. Außerdem können die Erkennungsraten in einem anderen Umfeld auch ganz anders aussehen. So kann ein Teil der Muster durch Programmier-Stil-Vorgaben vorgegeben sein. Ein neuer Trainingslauf wäre nötig, damit sich das Maschinelle Lernen auf die neue Situation mit anderen Metrikbewertungen einstellen kann. Der vorgestellte Ansatz erreicht jedoch eine sehr gute Präzision. Über die Qualität im Sinne des Recall-Wertes wird keine Angabe gemacht. Da jedoch in dem Papier in der Evaluierung 42 Funde im Graph Matching auftreten, bei nur 7 True Positives am Ende kann man von einer sehr unscharfen Suche ausgehen, was einen guten Recall begünstigt. 19

Über die Laufzeit in Abhängigkeit der Größe des Wirtsgraphen für das Graph Matching wird keine Aussage in dem Papier [5] getroffen. In [3] wird der Graph-Matching-Algorithmus genauer beschrieben und mit der Beschreibung von Optimierungen ergänzt. Diese sind nicht so weitgreifend wie die in Kapitel 4.2 vorgestellten, werden aber an dieser Stelle auch nicht weiter behandelt. 4.5 Metriken & Logische Formeln Der als nächstes behandelte Ansatz aus dem Papier [19] zur Erkennung von Software-Mustern konzentriert sich ganz auf die Muster wie Anti-Pattern und Bad-Smells, die zur Erkennung von Problemen in der Software eingesetzt werden können. Das dahinterliegende Konzept mit Software-Metriken wird jedoch in ähnlicher Form auch für die Erkennung der Forward- Engineering -Muster genutzt [2]. In diesem Ansatz wird kein Graph-Matching eingesetzt, um die Muster zu identifizieren. Stattdessen werden Metriken aus dem Quelltext abgeleitet. Die gesuchten Muster werden in Bezug auf diese Metriken beschrieben, wobei obere oder untere Schranken für die Werte der Metriken, sowie logische Verknüpfungen von Aussagen über die Metriken genutzt werden. Es werden nur noch Kandidaten gesucht, deren Metriken einer solchen Spezifikation entsprechen. Schranken, logische Ausdrücke Muster-Fund Suchen Quelltext Metriken x Vorverarbeitung Abbildung 20 Metriken & Logische Formeln (Ablauf) 4.5.1 Vorgehen Der vorgestellte Ansatz konzentriert sich nicht nur darauf, Muster zu erkennen, sondern bildet zwei Stufen: Einmal Bad Smells, Anti Pattern und ähnliche Auffälligkeiten, diese nennen wir Indikator-Muster, als Symptome. Zum anderen Entwurfs-Probleme, im Sinne von unüberlegten Entscheidungen zur Entwurfszeit, als Kernpunkte und Auslöser dieser Symptome auftreten. Sind die Indikator-Muster identifiziert, so werden Entwurfsprobleme mit den Funden in Verbindung gebracht, indem auf notwendige und optionale Indikator-Muster geprüft wird. Als Beispiel soll das Entwurfsproblem Zu viel zentralisierte Kontrolle, im Original Abusive centralization of control genannt, dienen. Es bedeutet, dass eine Klasse das komplette Geschehen unter Kontrolle hat. Somit ist keine richtige Objekt-Orientierung, stattdessen ein eher strukturierter Ansatz gewählt worden. Zeichen dafür sind in Tabelle 2 dargestellt. Dabei ist zu jedem Indikator-Muster vorgegeben, ob es verpflichtend für das Erkennen des übergeordneten Entwurfsproblem ist. Notwendig? Indikator-Muster Beschreibung Ja Feature Envy Siehe Unten Optional God Class Eine Klasse die für alles zuständig ist. 20

Optional Data Classes Klassen, die nur Daten halten, und keine weitere Funktionalität haben Optional Intensive Coupling Eine Methode, die stark mit Methoden von wenigen anderen Klassen arbeitet. Optional (Verletzung von ) Demeter s Law Demeters Gesetz besagt, dass nur auf nahestehende Methoden zugegriffen wird. Tabelle 2: Indikatoren für Zu viel zentralisierte Kontrolle Die Metriken, auf dessen Grundlage nach Indikator-Mustern gesucht wird beziehen sich jeweils auf ein Subjekt. Dies kann eine Methode oder auch eine Klasse sein. Ein typischer Indikator ist beispielsweise NOAM(Number Of Accessor Methods), die Anzahl aller Getter und Setter Methoden der betrachteten Klasse. Um nun ein Indikator-Muster zu beschreiben werden solche Metriken genutzt. Als Beispiel soll der Bad Smell Feature Envy dienen, der schon in Abbildung 3 beispielhaft vorgestellt wurde. Es besagt, dass eine Methode sehr viele Daten von (einer oder wenigen) fremden Klassen benutzt. Es ist ein Zeichen dafür, dass die Methode gar nicht wirklich zu ihrer aktuellen Klasse gehört. Um das Indikator-Muster nun mit Hilfe der Metriken für die Suche zu spezifizieren wird eine Konjunktion von drei Eigenschaften verwendet, dies wird in Abbildung 21 mithilfe einer logischen Schaltung dargestellt. ATFD>Few LAA< 0.33 Und Feature Envy FDP<=Few Abbildung 21: Spezifikation des Indikator-Musters Feature Envy Die benutzten Metriken beziehen sich jeweils auf dieselbe Methode, im Einzelnen: ATFD (Access To Foreign Data)gibt die Anzahl aller verwendeten Attribute von anderen Klassen in einer Methode an. LAA (Locality of Attribute Accesses)ist der Anteil von Klasseneigenen Attributen an allen verwendeten Variablen in einer Methode(im Bereich von 0 bis 1). FDP (Foreign Data Providers) Anzahl der Klassen, auf deren Attribute zugegriffen wird. Die Konstante Few ist dabei auf einen Wert von 2 festgelegt. Sinnvoller wäre hier eine Kontextabhängige Größe, die etwa von der Größe der zu untersuchenden Software und der Metrik beeinflusst wird. Die Berechnung der Metrik-Liste ist ebenso wie die Suche nach einem Indikator-Muster linear in der Anzahl der Klassen, Methoden und Anweisungen. Dies liegt daran, dass der Metriken- Katalog nur Metriken enthält, die durch ein oder zweimaliges Parsen des Quelltextes berechnet werden können. Probleme mit der Komplexität wie in den graphen-orientierten Verfahren treten daher nicht auf. 4.5.2 Bewertung des Verfahrens Der Ansatz stellt eine Möglichkeit dar, einen Eindruck über die Qualität von Quelltexten zu erhalten. Durch das Hinzufügen einer weiteren Ebene über den Indikator-Mustern, also der einfachen Bad-Smell und Anti-Pattern Suche, gibt es eine Möglichkeit einzelne kleine Symptome in Zu- 21

sammenhang zu bringen. Gibt es kein Entwurfs-Problem, zu dem diese Indikatoren gehören, so werden diese auch nicht bemängelt. Dies ist wichtig, da es durchaus begründete Einzelfälle gibt, in denen Bad-Smells akzeptabel sind. Die Suche ist schnell bezüglich der Laufzeit, da sie heuristisch anhand der Metriken vorgeht. Eine Eigenschaft hat sie dabei, die über die in den Kapiteln 4.2 und 4.4 vorgestellten Graphen- Matching Verfahren hinausgeht: Das Beachten von Verhältnissen. Es ist damit möglich, eine Aussage zu formulieren, die sagt, dass es mehr public Attribute als Getter und Setter Methoden gibt. In dem in Kapitel 4.3 vorgestellten Verfahren sind, seit der Erweiterung die in [18] dargestellt wird, auch Metriken vorhanden, die zur Musterspezifikation eingesetzt werden können. Eingeschränkt ist in [19] jedoch die Auswertung von Metriken zusammenhängender Klassen oder Methoden, und somit auch ein wenig die Flexibilität. Es ist nicht machbar, eine Aussage wie Eine Klasse mit vielen Methoden referenziert eine Klasse ohne Methoden zu formulieren. Dazu müsste eine neue Metrik definiert werden, die jedoch keine lineare Laufzeit mehr haben kann. Da Bad Smells und Anti Pattern deutlich subjektiver gefasst werden als zum Beispiel Entwurfsmuster kann der Begriff der Präzision und des Recall hier schlecht mit den anderen Ansätzen verglichen werden. 5 Zusammenfassung und Fazit In dieser Seminararbeit wurde der Begriff der automatischen Software-Mustererkennung vorgestellt. Außerdem wurden vier verschiedene Verfahren eingehender untersucht und anhand von Beispielen verdeutlicht. Keines der Verfahren ist vollkommen und kann alle Anforderungen besser erfüllen als die Konkurrenten. Der als in Kapitel 4.3 vorgestellte Ansatz mit der Muster-Dekomposition ist, wenn man andere Arbeiten hinzuzieht, wahrscheinlich der flexibelste und mächtigste. Trotzdem ist die Idee des Filterns (4.2), und des darauf aufbauenden planaritäts-unterstützten Matchings, durchaus interessant und könnte bei sehr großen Systemen noch mehr an Bedeutung gewinnen. Maschinelles-Lernen einzusetzen, um den menschlichen Experten bei dem Verwerfen von False Positives einzusetzen (4.4), ist eine interessante Idee, sie erscheint jedoch noch etwas unflexibel und nutzt die Möglichkeiten von künstlicher-intelligenz wahrscheinlich noch längst nicht aus. Das Problem des Lernaufwandes pro Muster wird jedoch auf jeden Fall bleiben. Der letzte Ansatz (4.5), der anhand von Metriken nach internen Qualitäts-Mängeln sucht ist schnell und gerade in der Qualitätskontrolle als Teil eines Review-Prozesses bestimmt gut geeignet. Zum Suchen genau spezifizierter Strukturen ist er nicht einsetzbar, was jedoch auch nicht seine Bestimmung ist. Es bleibt zu sagen, dass die automatische Mustererkennung durchaus schon gewinnbringend einsetzbar ist. Inwiefern diese Möglichkeiten in Reverse-Engineering-Aufgaben der Industrie zum Einsatz kommen, wird sich zeigen. Die Erkennung von Problemen hat dabei meiner Erfahrung nach mehr Chancen, da eine Qualitätskontrolle als normaler Bestandteil des Entwicklungsprozesses schon vorhanden ist. Ein solcher Report mit Qualitäts-Mängeln kann sehr einfach in einen Review-Prozess mit einfließen und in die daraus resultierenden Dokumente aufgenommen werden. 22