已经有很多人回答了。以为我会发表自己的个人看法。
曾几何时,我致力于(现在仍在)创作音乐的应用程序。
该应用程序有一个抽象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
。
用这种方式思考是很自然的,尤其是对于OO相对较新的人。
但是我们应该永远记住,类是模板,对象是被充入这些模板的内容。您可以将所需的任何内容倒入模板中,从而创造了无数种可能性。
填充模板不是子类的工作。这是对象的工作。子类的工作是添加实际功能或扩展模板。
这就是为什么我应该创建一个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;
}
}
我刚刚分心了。我想你应该已经明白了。
子类无意填写其超类设置的模板。子类用于添加功能。对象是用来填充模板的,这就是它们的用途。
您不应该为每种可能的数据组合创建一个新类。(就像我不应该Scale
为Note
s的每种可能的组合创建一个新的子类一样)。
她的指导方针是:每当您创建一个新的子类时,都要考虑它是否添加了超类中不存在的任何新功能。如果该问题的答案为“否”,则可能是您试图“填充”超类的模板,在这种情况下,只需创建一个对象即可。(也许还有一个带有“预设”的工厂,以使生活更轻松)。
希望能有所帮助。