Viele Leute haben bereits geantwortet. Ich dachte, ich würde meine persönliche Perspektive geben.
Ich habe einmal an einer App gearbeitet (und mache es immer noch), mit der Musik erstellt wird.
Die App hatte eine abstrakte Scale
Klasse mit mehreren Unterklassen: CMajor
, DMinor
etc. Scale
sah so etwas wie so:
public abstract class Scale {
protected Note[] notes;
public Scale() {
loadNotes();
}
// .. some other stuff ommited
protected abstract void loadNotes(); /* subclasses put notes in the array
in this method. */
}
Die Musikgeneratoren arbeiteten mit einer bestimmten Scale
Instanz, um Musik zu generieren. Der Benutzer würde eine Skala aus einer Liste auswählen, aus der Musik erzeugt werden soll.
Eines Tages kam mir eine coole Idee in den Sinn: Warum nicht dem Benutzer erlauben, seine eigenen Skalen zu erstellen? Der Benutzer wählt Notizen aus einer Liste aus, drückt eine Taste, und der Liste der verfügbaren Skalen wird eine neue Skala hinzugefügt.
Aber das konnte ich nicht. Das lag daran, dass alle Maßstäbe bereits zur Kompilierungszeit festgelegt sind - da sie als Klassen ausgedrückt werden. Dann traf es mich:
Es ist oft intuitiv, in Über- und Unterklassen zu denken. Fast alles kann durch dieses System ausgedrückt werden: Ober- Person
und Unterklassen John
und Mary
; Ober- Car
und Unterklassen Volvo
und Mazda
; Superklasse Missile
und Unterklassen SpeedRocked
, LandMine
und TrippleExplodingThingy
.
Es ist sehr natürlich, auf diese Weise zu denken, besonders für die Person, die für OO relativ neu ist.
Wir sollten uns jedoch immer daran erinnern, dass Klassen Vorlagen sind und dass Objekte in diese Vorlagen eingegossen werden . Sie können beliebige Inhalte in die Vorlage einfügen und so unzählige Möglichkeiten schaffen.
Es ist nicht die Aufgabe der Unterklasse, die Vorlage auszufüllen. Es ist die Aufgabe des Objekts. Die Aufgabe der Unterklasse besteht darin, die eigentliche Funktionalität hinzuzufügen oder die Vorlage zu erweitern .
Aus diesem Grund hätte ich eine konkrete Scale
Klasse mit einem Note[]
Feld erstellen und Objekte diese Vorlage ausfüllen lassen sollen . möglicherweise durch den Konstruktor oder so. Und irgendwann tat ich es auch.
Denken Sie jedes Mal, wenn Sie eine Vorlage in einer Klasse entwerfen (z. B. ein leeres Note[]
Element, das ausgefüllt werden muss, oder ein String name
Feld, dem ein Wert zugewiesen werden muss), daran, dass es Aufgabe der Objekte dieser Klasse ist, die Vorlage auszufüllen ( oder möglicherweise diejenigen, die diese Objekte erstellen). Unterklassen sollen Funktionen hinzufügen, nicht Vorlagen ausfüllen.
Sie könnten versucht sein, eine "Superklasse Person
, Unterklassen John
und Mary
" Art von System zu erstellen , wie Sie es getan haben, weil Sie die Formalität mögen, die Ihnen dies bringt.
Auf diese Weise können Sie einfach sagen Person p = new Mary()
, anstatt Person p = new Person("Mary", 57, Sex.FEMALE)
. Es macht die Dinge organisierter und strukturierter. Wie wir bereits sagten, ist es kein guter Ansatz, für jede Kombination von Daten eine neue Klasse zu erstellen, da dies den Code für nichts aufbläht und Sie in Bezug auf die Laufzeitfähigkeiten einschränkt.
Hier ist eine Lösung: Verwenden Sie eine Basisfabrik, möglicherweise sogar eine statische. Wie so:
public final class PersonFactory {
private PersonFactory() { }
public static Person createJohn(){
return new Person("John", 40, Sex.MALE);
}
public static Person createMary(){
return new Person("Mary", 57, Sex.FEMALE);
}
// ...
}
Auf diese Weise können Sie leicht die Voreinstellungen verwenden, die mit dem Programm geliefert werden Person mary = PersonFactory.createMary()
, aber Sie behalten sich auch das Recht vor, neue Personen dynamisch zu entwerfen, zum Beispiel für den Fall, dass Sie dem Benutzer dies erlauben möchten . Z.B:
// .. requesting the user for input ..
String name = // user input
int age = // user input
Sex sex = // user input, interpreted
Person newPerson = new Person(name, age, sex);
Oder noch besser: Mach so etwas:
public final class PersonFactory {
private PersonFactory() { }
private static Map<String, Person> persons = new HashMap<>();
private static Map<String, PersonData> personBlueprints = new HashMap<>();
public static void addPerson(Person person){
persons.put(person.getName(), person);
}
public static Person getPerson(String name){
return persons.get(name);
}
public static Person createPerson(String blueprintName){
PersonData data = personBlueprints.get(blueprintName);
return new Person(data.name, data.age, data.sex);
}
// .. or, alternative to the last method
public static Person createPerson(String personName){
Person blueprint = persons.get(personName);
return new Person(blueprint.getName(), blueprint.getAge(), blueprint.getSex());
}
}
public class PersonData {
public String name;
public int age;
public Sex sex;
public PersonData(String name, int age, Sex sex){
this.name = name;
this.age = age;
this.sex = sex;
}
}
Ich wurde weggetragen. Ich denke, Sie haben die Idee.
Unterklassen sind nicht dazu gedacht, die von ihren Oberklassen festgelegten Vorlagen auszufüllen. Unterklassen sollen Funktionalität hinzufügen . Objekte sollen die Vorlagen ausfüllen, dafür sind sie da.
Sie sollten nicht für jede mögliche Kombination von Daten eine neue Klasse erstellen. (Genau wie ich nicht Scale
für jede mögliche Kombination von Note
s eine neue Unterklasse hätte erstellen sollen).
Hier ist eine Richtlinie: Überlegen Sie beim Erstellen einer neuen Unterklasse, ob sie neue Funktionen hinzufügt, die in der Oberklasse nicht vorhanden sind. Wenn die Antwort auf diese Frage "Nein" lautet, versuchen Sie möglicherweise, die Vorlage der Superklasse auszufüllen. Erstellen Sie in diesem Fall einfach ein Objekt. (Und evtl. eine Factory mit 'Presets', um das Leben leichter zu machen).
Hoffentlich hilft das.