多くの人がすでに答えました。私自身の個人的な視点を与えると思った。
むかしむかし、私は音楽を作成するアプリを開発しました(そして今でもそうしています)。
アプリが抽象的だったScale
:いくつかのサブクラスを持つクラスをCMajor
、DMinor
など、Scale
そのようなものに見えました。
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. */
}
音楽ジェネレーターは、特定のScale
インスタンスと連携して音楽を生成しました。ユーザーはリストから音階を選択し、そこから音楽を生成します。
ある日、クールなアイデアが思い浮かびました。ユーザーが自分のスケールを作成できるようにしてみませんか?ユーザーはリストからノートを選択し、ボタンを押すと、リストに新しいスケールが追加されます。
しかし、私はこれを行うことができませんでした。これは、すべてのスケールがコンパイル時にすでに設定されているためです-クラスとして表現されているからです。それから私にぶつかった:
多くの場合、「スーパークラスとサブクラス」の観点から考えるのは直感的です。ほとんどすべてのものは、このシステムを介して表現できます。スーパークラスPerson
とサブクラスJohn
およびMary
; スーパークラスCar
とサブクラスVolvo
およびMazda
; スーパークラスMissile
とサブクラスSpeedRocked
、LandMine
およびTrippleExplodingThingy
。
このように考えることは非常に自然です。特にオブジェクト指向に比較的慣れていない人にとっては。
しかし、クラスはテンプレートであり、オブジェクトはこれらのテンプレートに注がれるコンテンツであることを常に覚えておく必要があります。必要なコンテンツをテンプレートに注ぎ、無数の可能性を作成できます。
テンプレートを埋めるのはサブクラスの仕事ではありません。それはオブジェクトの仕事です。サブクラスの役割は、実際の機能を追加するか、テンプレートを展開することです。
それがScale
、Note[]
フィールドを持つ具体的なクラスを作成し、オブジェクトにこのテンプレートを入力させるべきだった理由です。おそらくコンストラクターか何かを通して。そして最終的に、私はそうしました。
クラスでテンプレートを設計するたびに(たとえば、入力するNote[]
必要がある空のメンバー、またはString name
値を割り当てる必要があるフィールド)、テンプレートに入力するのはこのクラスのオブジェクトの仕事であることに注意してください(または、おそらくこれらのオブジェクトを作成する人)。サブクラスは、テンプレートに入力するためではなく、機能を追加するためのものです。
あなたがしたように「スーパークラスPerson
、サブクラスJohn
、Mary
」種類のシステムを作成したくなるかもしれません。
このようにするとPerson p = new Mary()
、の代わりにと言うことができますPerson p = new Person("Mary", 57, Sex.FEMALE)
。それは物事をより組織化し、より構造化します。しかし、前述したように、データのあらゆる組み合わせに対して新しいクラスを作成することは、コードを無制限に肥大化し、実行時の能力の面で制限されるため、良いアプローチではありません。
そのため、ここに解決策があります。基本的なファクトリーを使用します。そのようです:
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);
}
// ...
}
このように、次のように「プログラムに来る」「プリセット」を簡単に使用できますがPerson mary = PersonFactory.createMary()
、たとえばユーザーにそうすることを許可したい場合など、新しい人を動的に設計する権利も留保します。例えば:
// .. 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);
またはさらに良い:そのような何かをする:
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;
}
}
私は夢中になりました。あなたはそのアイデアを得ると思います。
サブクラスは、スーパークラスによって設定されたテンプレートを埋めるためのものではありません。サブクラスは機能を追加するためのものです。オブジェクトはテンプレートを埋めるためのものであり、それが目的です。
データのあらゆる可能な組み合わせに対して新しいクラスを作成するべきではありません。(sのScale
すべての可能な組み合わせに対して新しいサブクラスを作成するべきではなかったようにNote
)。
彼女のガイドラインです。新しいサブクラスを作成するときは、スーパークラスに存在しない新しい機能を追加するかどうかを検討してください。その質問に対する答えが「いいえ」の場合、スーパークラスの「テンプレートに入力」しようとしている場合よりも、その場合はオブジェクトを作成するだけです。(そしておそらく、人生を楽にするための「プリセット」を備えた工場)。
お役に立てば幸いです。