4 Stimmen

Kompilieren einer Java-Klasse gegen eine Schnittstelle, auch wenn keine angegeben wurde

Ich unterrichte einen Einführungskurs in die Programmierung und wir verwenden Java. Ich möchte den Studenten helfen zu lernen, wie man eine schriftliche Klassenspezifikation in ein funktionierendes Programm umsetzt. Ich werde die schriftliche Spezifikation zur Verfügung stellen. Sie gibt den Namen der Klasse und das Verhalten in Form von Methodensignaturen an. Ich möchte, dass die Schüler diese in eine funktionierende Java-Klasse übersetzen.

Ich könnte ihnen eine Schnittstelle zur Verfügung stellen und sie die Schnittstelle implementieren lassen, aber das verfehlt einen Teil des Zwecks: das Lesen und Interpretieren eines schriftlichen Pflichtenhefts. Ich möchte, dass sie die Klasse von Grund auf neu schreiben. Dann möchte ich ihre Arbeit benoten.

Meine Idee zur Überprüfung ihrer Arbeit ist folgende: Kompilieren Sie ihre Java-Klassendatei gegen meine eigene Schnittstelle. Wenn sie kompiliert, weiß ich zumindest, dass sie alle Methodenverträge befolgt haben, und ich kann mit dem Testen der Funktionalität beginnen. Lässt sie sich nicht kompilieren, erhalte ich eine Fehlermeldung, die mir mitteilt, welche Methoden nicht korrekt implementiert wurden.

Wie kann ich erzwingen, dass eine Java-Klassendatei gegen eine Schnittstelle kompiliert wird, auch wenn im Quellcode ursprünglich keine angegeben war?

Mit anderen Worten: Ich habe die folgenden zwei Dateien:

FooInterface.java

public interface FooInterface
{
    ...
}

Foo.java

public class Foo
{
    ...
}

Ich möchte Foo so kompilieren, als ob es FooInterface explizit implementiert. Aber ich möchte nicht einen Haufen von Quellcode-Dateien manuell bearbeiten müssen, um dies zu tun. Wie kann ich das machen?

Editar

Um die Frage nach dem Wert einer schriftlichen Spezifikation im Vergleich zur Bereitstellung der Schnittstelle zu beantworten, soll hier ein hypothetisches Spezifikationsdokument dargestellt werden:

Schreiben Sie eine Klasse namens Foo mit den folgenden Methoden:

älteste : ages (int[]) -> int
Gibt in einem Array von Altersangaben die höchste zurück.

anyAdults : ages (int[]) -> boolean
Gibt ein Array von Altersangaben zurück, ob einer von ihnen 18 oder älter ist.

IMO hat dies einen großen pädagogischen Nutzen. Die Schüler müssen kritisch beurteilen, ob ihr Programm die Spezifikationen einhält. Wenn ich die Schnittstellendatei zur Verfügung stellen würde, könnten sie ihr Gehirn ausschalten und sich einfach vom Compiler sagen lassen, ob sie die Spezifikation befolgt haben oder nicht. Die Verwendung des Compilers als kognitive Krücke ist die gleiche Technik, die die schlechteren Schüler derzeit (erfolglos) anwenden, um ihre Klammern auszugleichen.

3 Stimmen

+1 Guter Kurs. Sie sind ein guter Lehrer.

3 Stimmen

Wenn Sie Ihren Schülern genau Methodensignaturen als Teil der Spezifikation zu verwenden, wie unterscheidet sich das davon, ihnen eine echte Schnittstelle zu geben? Und wenn Sie nur die Klasse beschreiben, wird Ihr Ansatz wahrscheinlich in vielen Fällen scheitern, weil die tatsächlichen Signaturen, die Sie und Ihre Schüler entwickeln, sich geringfügig unterscheiden können.

0 Stimmen

@ChssPly76 - Da bin ich anderer Meinung. Wenn der Auftraggeber sorgfältig vorgeht, kann die Signatur der Methode genau beschrieben werden, und die Schüler sollten in der Lage sein, sie leicht auszuführen, vorausgesetzt, sie sind sorgfältig. Es gibt noch einen weiteren Aspekt: Ein Teil des Lernens beinhaltet das Lesen und Interpretieren der Dokumentation, selbst angesichts einer unvollständigen Spezifikation oder eines Fehlers des Lehrers. Etwas selbst angesichts solcher Herausforderungen zu bauen, sollte imho gefördert werden.

11voto

Murali VP Punkte 5788

Sie könnten Foo immer noch FooInterface implementieren lassen, aber geben Sie Ihren Schülern einfach ein leeres FooInterface. Wenn Sie das Foo der Schüler kompilieren, verwenden Sie Ihr tatsächliches FooInterface.

11voto

jonnii Punkte 27657

Du könntest vielleicht so etwas machen wie:

public class Bar extends Foo implements FooInterface{}

Wenn FooInterface von Foo vollständig erfüllt wird, wird Bar keine Fehler aufweisen.

4voto

Bill the Lizard Punkte 384619

Zusätzlich zur Bereitstellung einer vollständigen Schnittstelle bei der Kompilierung des Codes Ihrer Schüler, wie von Murali vorgeschlagen, schlage ich vor, dass Sie auch eine Unit-Test-Suite implementieren, damit Ihre Benotung viel reibungsloser verläuft. Zusätzlich zum Testen, dass die Methodenverträge aus der schriftlichen Spezifikation alle erfüllt sind, könnten Sie auch testen, dass jede Methode richtig funktioniert.

3voto

OscarRyz Punkte 189898

Wie in Ihrer Bearbeitung:

Sie können sie nicht an eine bestehende Schnittstelle anpassen. Sie müssen dies von Hand tun, indem Sie die Methodennamen mit Reflexion .

Anhand dieses Codes sollten Sie erkennen können, ob die Klasse die Spezifikation implementiert:

 String studentClassName = args[0];
 Class class = Class.forName( studentClassName );

 Methods [] methods = class.getMethods();

 for( Method m : methods ) {
    if( matchTheSpecs( method ) ){
       markAsAccepted( method.getName() );
    }
 }

El matchTheSpecs Methode würde überprüfen, ob

 method.getName()
 method.getParameterTypes()
 method.getReturnType()

Also, das:

oldest : ages (int[]) -> int
anyAdults : ages (int[]) -> boolean

Sollte sein

public boolean anyAdults( int [] ages );
public int oldest( int [] ages );

Das können Sie nicht.

~~

Zumindest nicht so, wie du es beschreibst.

Stellen Sie sich die folgende Situation vor. Eine Aussage der Spezifikation könnte lauten:

"... the system saves the user name and ... "

Die möglichen Methodensignaturen sind einfach zu viel.

 public void setUserName( String name );
 public void setName( String user );
 public void userName( String user );
 public String saveUserName( String s );
 public void save( String userName );
 ... another 10 more here ... 

Und so weiter. Sie sind alle richtig, denn sie werden diese Funktion in den Code aufnehmen.

Was Sie tun können ist die Hinzufügung von Anweisungen für die Methodengenerierung wie.

Hinweis: alle Methodennamen sollten dem folgenden Muster folgen (beschreiben Sie das Muster hier)

Dann können Sie die Klasse kompilieren und mit Hilfe von Reflection die Namen der Methoden ermitteln und auswerten, ob sie Ihrer Spezifikation entsprechen.

 String studentClassName = args[0];
 Class class = Class.forName( studentClassName );
 Methods [] methods = class.getMethods();
 for( Method m : methods ) {
    if( matchThePattern( method.getName() ) ){
       markAsAccepted( method.getName() );
    }
 }
 if( allTheMethodsAreWritten() ) {
      grade("A");
 }

Um zu beurteilen, ob sie die Kriterien erfüllen, müssen Sie überprüfen, ob die Klasse etwas enthält:

1) die Anzahl der Methoden, die in der Spezifikation angegeben sind (es könnten auch mehr sein).
2) Der Name der Methode stimmt mit der Spezifikation überein (die Methode spiegelt die Absicht wider).

Für mein erstes Beispiel wären beispielsweise alle folgenden Einträge gültig:

  [setUserName,setName,userName,saveUserName,save]

Ihr Muster könnte in etwa so aussehen:

keyAction + Betreff(e) + Sonstige.

~~

Oder etwas, das Sinn macht.

3voto

weiji Punkte 1916

Ich weiß nicht, ob dies der eleganteste Weg ist, aber Ihr Benotungsprogramm könnte mit Hilfe von Reflexion die Schülerklasse laden und nach Methoden mit einem bestimmten Namen suchen - wenn es eine solche Methode nicht gibt, können Sie die gewünschten Punkte abziehen. Wenn die Methode existiert, können Sie sie ebenfalls mit Reflection aufrufen und beliebige Parameter übergeben.

Sun hat hier einen ziemlich guten Lehrpfad:

http://java.sun.com/docs/books/tutorial/reflect/member/index.html

Siehe den Abschnitt über den Erhalt von Methodeninformationen und den Aufruf von Methoden.

Markierte Gemeinschaft - andere können gerne Korrekturen usw. vornehmen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X