2015-03-10 6 views
8

Kürzlich hatte ich ein Interview und mir wurde die folgende Frage gestellt. Angesichts der folgenden Klasse/Interface-Struktur:Wie kann eine untergeordnete Schnittstelle die Implementierungen ihrer Eltern wiederverwenden?

enter image description here

Frage:

Wie eine Schnittstelle implementieren kann EmployedStudent Code von StudentImpl und EmployeeImpl wiederzuverwenden.

Ich schlug vor, Mitarbeiter und Student in meine Implementierung zu komponieren.

Basierend auf der Reaktion des Interviewers glaube ich nicht, dass sie die beste Lösung gefunden haben. Ich habe viel darüber nachgedacht, aber ich kann mir keine andere Lösung ausdenken.

+0

Komposition klingt wie der beste Weg zu mir, in Bezug auf die Wiederverwendung von Code. Ohne Mehrfachvererbung weiß ich nicht, wie Sie den Code von * both * StudentImpl und EmployeeImpl auf andere Weise wiederverwenden würden. – christopher

+1

Ich hätte die Komposition auch gesagt. Sie könnten * EmployeedStudent von einer der Implementierungen ableiten und dann nur die Komposition für die andere verwenden, nehme ich an. Aber ich würde nicht gesagt hat, dass als gerade Zusammensetzung wirklich besser war, es wäre denn eine der Schnittstellen ist mehr „primäre“ als die andere (zum Beispiel, dass die primäre Facette war, dass es ein Mitarbeiter war, mit den Schülern etwas ziemlich zweitrangig). –

Antwort

1

Da Java keine Mehrfachvererbung unterstützen, Sie könnte/sollte

  • entweder ein Feld für jede der gewünschten Super
  • oder leiten sich von einer übergeordneten Klasse und haben ein Feld für die anderen.

Die Felder werden dann durch "unsere" Implementierung der jeweiligen Methoden bezeichnet.

Dies ist einer der design patterns from the GoF, ich denke, es ist die Proxy pattern.

5

Erstellen Sie eine Klasse, die sowohl Employee als auch Student implementiert. Erstellen Sie in Ihrer Klasse eine Instanz von EmployeeImpl und StudentImpl. Lassen Sie Ihre Klasse dann alle Methodenaufrufe an eines der Objekte delegieren.

public class EmployedStudent implements Employee, Student { 

    private EmployeeImpl employee = new EmployeeImpl(); 
    private StudentImpl student = new StudentImpl(); 

    public int getSalary() { 
    return this.employee.getSalary(); 
    } 

    public float getAverageGrade() { 
    return this.student.getAverageGrade(); 
    } 

} 
+1

Das ist die Verwendung von Schnittstellen, ich verstand die Frage mehr wie die Verwendung tatsächlich vorhandenen CODE in den Mitarbeiter- und Studenten-Klassen, nicht nur die Schnittstellen. –

+2

@meister_reineke: Durch die Delegation, die Sie tatsächlich * werden * unter Verwendung von vorhandenen Code der '' '' EmployeeImpl'' und StudentImpl'' Klasse. – user1438038

1

Mit Java 8 können Sie Code in die Schnittstelle selbst verschieben. Das löst das Problem der "Mehrfachvererbung".

1

Während die Schnittstelle Segregations Prinzip manchmal hilfreich sein kann, und einige Leute spotten über die Idee von Schnittstellen, die nicht versprechen, dass alle Implementierungen alle Mitglieder unterstützen, würde ich vorschlagen, dass es hilfreich sein kann Student und Employee Schnittstellen haben welche beinhalten isStudent und isEmployee Mitglieder, und dann haben Person implementieren beide Schnittstellen; einige Methoden wie giveRaise() sollten nicht auf jemanden funktionieren, der kein Angestellter ist, aber andere wie getOutstandingPay() sollten gut funktionieren (wenn jemand kein Geld verdient hat, sollte die Methode einfach Null zurückgeben).

Obwohl es hässlich erscheinen mag, alle Person Objekte mit Methoden zu komplizieren, die für viele von ihnen nicht anwendbar sind, vermeidet ein solches Design Schwierigkeiten für den Fall, dass ein Student eingestellt wird oder ein Mitarbeiter anfängt Klassen zu nehmen. Getrennte Klassen für Student, Employee und StudentEmployee, auch wenn man so leicht tun könnte, würde erfordern, dass ein Student, die einen Job mit einer neuen Objektinstanz ersetzt werden um ein StudentEmployee zu werden. Im Gegensatz dazu kann man, wenn man eine Person Klasse hat, deren Instanzen möglicherweise mit Methoden wie giveRaise umgehen können, mit Situationen umgehen, in denen sich die Fähigkeiten von Objekten während ihrer Lebensdauer ändern.

3

Sie könnten also implements Employee, Student intern sowohl an EmployeeImpl als auch an StudentImpl delegieren, aber auch von einer Implementierungsklasse erben.

Nun ist die Klasse Name EmployedStudent (StudyingEmployee oder BothEmployeeAndStudent nicht) schlägt vor:

class EmployedStudent extends StudentImpl implements Employee { 
    Employee asEmployee = new EmployeeImpl(); 

Ja, ein kleines bisschen effizienter als nur auf eine andere Klasse zu delegieren.

Die Use-Case ist jedoch weit davon entfernt realistisch: alle Arten von Kombinationen denkbar mehrere Klassen sind. In diesem Fall ist Ihre Lösung universeller. Wirklich universell, ist ein Lookup-Mechanismus:

public class Person { 
    public <T> T as(Class<T> klazz) { ... } 
} 

Person person = ...; 
Student asStudent = person.as(Student.class); 
if (asStudent != null) { 
    ... 
} 
Employee asEmployee = person.as(Employee.class); 
if (asEmployee != null) { 
    asEmployee.quitJob(); 
    asEmployee = person.as(Employee.class); // null 
} 

Das ist ein Lookup-Funktionen ist. Es in der Regel eine umständliche Vererbungshierarchie ersetzen kann, sagen von Fahrzeug, RoadVehicle, SwimmingVehicle (Boot), FlyingVehicle (Flugzeug), WaterAirplane (?), AmphibianTank (?) Von Fähigkeiten Schwimmen mit, Fliegen, Fahren.

Der Unterschied ist die gesamte Entkopplung.

+0

Dies ist ein super cooler Workaround! Ich bin mir nicht sicher, ob ich etwas Ähnliches für die Komposition wählen würde, aber es zeigt definitiv, dass man diese Funktionalität simulieren kann. – christopher

+0

Ich dachte nicht über Ihre Methode nach. Was ist die Klasse Person? – gstackoverflow

+0

Beabsichtigt zu zeigen, dass eine Antwort mehr als zwei Zeilen liefern kann, indem ein anderer Ansatz gezeigt wird. Aber erwähne definitiv that_: "In ähnlichen Anwendungsfällen darf man auch ...", erweiterte die Antwort. –