我从一本名为" Thinking in Java"我想到尝试一些继承/多态。但是我遇到了一些我不太喜欢它的代码。这就是......
class Person{
private int hp, attack, speed;
public status() { System.out.println("HP : " + hp + ", ATT : " + attack + ", SPD : " + speed); }
public punch(){ System.out.println( " punches"); }
public getPunched(){ hp -= 5; }
/* plus standard getters/setters to save up your time */
}
class Bob extends Person{
private String name = "Bob";
public Bob(){ setHp(100); setAttack(25); setSpeed(15); }
public punch(){ System.out.println(name); super.punch(); }
public getPunched(){ System.out.println(name + " gets punched"); super.getPunched(); }
}
class Jake extends Person{
private String name = "Jake";
public Jake(){ setHp(120); setAttack(30); setSpeed(10); }
public punch(){ System.out.println(name); super.punch(); }
public getPunched(){ System.out.println(name + " gets punched"); super.getPunched(); }
}
现在这一切都按原样运行(可能有一些拼写错误,我的版本有点长,所以我在这里重新设置了一个更短的错误。)
我想知道的是,有没有办法让Bob和Jake类的方法punch()和getPunched()不重复自己?更具体地说,我可以将它们都放在一个Person类中,同时保持一个人仍然可以打印出其子名称的功能(" Bob / Jake拳击"),或者我是否必须将它放入其他地方的静态功能?如果我必须把它放在一个函数中,它将如何看起来像,因为我还使用了保留命令" super"。
我知道这段代码看起来非常简单和无用,对此感到抱歉,但它本来就是这样,所以我可以真正看到我在编写代码时所做的事情。
谢谢! :)
编辑:在写这篇文章时,我意识到我可以在父类中创建一个受保护的字符串名称" Person"并且通过setter覆盖每个子构造函数中的变量,但是我仍然感兴趣如果它可以寻址到子变量,并且如何做到这一点。
答案 0 :(得分:3)
我会告诉你为什么我不喜欢那段代码。
基本上,Bob
和Jake
不应该是类。它们应该是实例。他们不代表类的人,他们应该是个人。当然,你可以说班级Bob
代表世界上所有名叫鲍勃的人,但这不是一个真实世界的用例,甚至不是一个非常有益的用例。为什么所有Bobs都具有相同的HP,速度等?
一旦你意识到这一点,就清楚了为什么代码会重复 - 因为这个糟糕的设计,你正在写一些应该对所有个人都是共同的东西,或者至少对某些个人来说(这将是一个子类)每个人一次又一次地Person
}。这违背了面向对象编程和一般编程的目的。
所以,考虑个人的名字。所有人都有一个名字。所以这确实应该是Person
类中的一个字段。它应该有一个吸气剂和一个像其他所有东西一样的定型器。
使Bob与Jake不同的东西应作为参数传递给构造函数。
如果你想拥有一些人而不是其他人共同的行为,那么它应该在Person
的单个子类中定义。例如,PersonWithAnnouncements
:
class PersonWithAnnouncements extends Person {
public PersonWithAnnouncements( String name, int hp, int attack, int speed ) {
super( name );
setHp( hp );
setAttack( attack );
setSpeed( speed );
}
public getPunched() {
System.out.println( this.getName() + " gets punched." );
super.getPunched();
}
public punch() {
System.out.println( this.getName() + " punches." );
super.punch();
}
}
然后你可以创建个人 - 实例 - 像这样:
bob = new PersonWithAnnouncements( "Bob", 100, 25, 15 );
jake = new PersonWithAnnouncements( "Jake", 120, 30, 10 );
这是一个更好的世界代表 - 个人获得个人惠普,速度和名称。行为是一个阶级问题。如果它属于一个自己的班级" - 比方说,一个ChuckNorris
类 - 然后它的行为并不意味着在几个类中重复。你仍然需要在那个班级里创造一个人。
答案 1 :(得分:1)
但我仍然感兴趣,如果它可以解决一个子变量,那怎么会这样做。
要实现这种关系,只需在父类中保护变量name
即可。然后,您可以在继承的类中访问它,如私有属性。
另一种方法是提供受保护(或公共)构造函数,它接受String name
,然后在父类中设置它。然后在punch()
中实施Person
。不要忘记在您的子课程中通过Person
致电super()
的构造函数'构造函数。
我更喜欢第二种方式,因为它更清洁(但这是我的个人观点)。这样你就不需要一个setter方法,并使name
属性成为不可变的,这通常是可取的。
答案 2 :(得分:1)
另一种可能性是在Person和Bob或Jake之间的类层次结构中插入一个类,例如BobOrJake。
abstract public class BobOrJake extends Person
{
public void setName(String name) {
this.name = name;
}
protected String name;
public void punch(){ System.out.println(name); super.punch(); }
public void getPunched(){ System.out.println(name + " gets punched"); super.getPunched(); }
}
子类Bob不会被要求实现punch()或getPunched(),因为它们是在BobOrJake类中实现的。
鲍勃变得非常简单(但请注意额外的setName()来设置Bob的名字):
public class Bob extends BobOrJake {
public Bob(){ setName("Bob");setHp(100); setAttack(25); setSpeed(15); }
}
你不必像我一样使BobOrJake抽象化,但如果你永远不会实例化一个BobOrJake对象,你可能想把它留下来。
答案 3 :(得分:1)
首先,我认为Bob
和Jake
不应该是单独的类。没有什么根本不同,他们这样做是值得的。在内部,它们是相同的,Bob
/ Jake
应该只是同一个类Person
的实例。
例如,如果我们有100个人,除了他们的“统计数据”之外,他们都是相同的,你想创建100个单独的课程吗?如果我们想在运行时创建新人(无论是通过用户输入还是从文件中读取信息),该怎么办?你无法做到。
相反,内部统计(名称,攻击等)应至少在对象初始化时可配置。
import java.util.*;
import java.lang.*;
import java.io.*;
class Person
{
protected String _Name;
protected int _Health;
protected int _Attack;
protected int _Speed;
public Person(final String name, final int health, final int attack, final int speed)
{
_Name = name;
_Health = health;
_Attack = attack;
_Speed = speed;
}
public String getName()
{
return _Name;
}
public void status()
{
System.out.println(_Name + ":\nHP : " + _Health + ", ATT : " + _Attack + ", SPD : " + _Speed);
}
public void punch(final Person target)
{
System.out.println(_Name + " punches " + target.getName() + " for " + _Attack);
target.getPunched(_Attack);
}
public void getPunched(int damage)
{
_Health -= damage;
}
}
class Ideone
{
public static void main (String[] args) throws java.lang.Exception
{
Person bob = new Person("Bob", 100, 25, 15);
Person joe = new Person("Joe", 120, 30, 10);
bob.status();
joe.status();
bob.punch(joe);
joe.punch(bob);
bob.status();
joe.status();
}
}
输出:
Bob:
HP : 100, ATT : 25, SPD : 15
Joe:
HP : 120, ATT : 30, SPD : 10
Bob punches Joe for 25
Joe punches Bob for 30
Bob:
HP : 70, ATT : 25, SPD : 15
Joe:
HP : 95, ATT : 30, SPD : 10
现在,如果您引入了需要唯一逻辑来执行攻击的新角色(例如Warrior
或Archer
),那么创建子类是值得的。
请参阅:
import java.util.*;
import java.lang.*;
import java.io.*;
class Person
{
protected String _Name;
protected int _Health;
protected int _Attack;
protected int _Speed;
public Person(final String name, final int health, final int attack, final int speed)
{
_Name = name;
_Health = health;
_Attack = attack;
_Speed = speed;
}
public String getName()
{
return _Name;
}
public void status()
{
System.out.println(_Name + ":\nHP : " + _Health + ", ATT : " + _Attack + ", SPD : " + _Speed);
}
public void punch(final Person target)
{
System.out.println(_Name + " punches " + target.getName() + " for " + _Attack);
target.getPunched(_Attack);
}
public void getPunched(int damage)
{
_Health -= damage;
}
}
class Warrior extends Person
{
private int _SwordModifier;
public Warrior(final String name, final int health, final int attack, final int speed)
{
super(name, health, attack, speed);
_SwordModifier = 2;
}
@Override
public void punch(Person target)
{
int damage = _Attack * _SwordModifier;
System.out.println(_Name + " punches " + target.getName() + " with his sword for " + damage);
target.getPunched(damage);
}
}
class Ideone
{
public static void main (String[] args) throws java.lang.Exception
{
Person bob = new Person("Bob", 100, 25, 15);
Person joe = new Warrior("Joe", 120, 30, 10);
bob.status();
joe.status();
bob.punch(joe);
joe.punch(bob);
bob.status();
joe.status();
}
}
输出:
Bob:
HP : 100, ATT : 25, SPD : 15
Joe:
HP : 120, ATT : 30, SPD : 10
Bob punches Joe for 25
Joe punches Bob with his sword for 60
Bob:
HP : 40, ATT : 25, SPD : 15
Joe:
HP : 95, ATT : 30, SPD : 10
答案 4 :(得分:1)
免责声明:我的个人解决方案在某种程度上类似于ssell's one:在您的示例中不需要继承。
但另一方面,我们总是从基础开始学习。因此,提出的解决方案与继承/良好实践(实例而不是类)有关,因此我想为您提出一种不同的方法:design patterns。在您的情况下,我认为模板模式可能是一个不错的选择。
即。 (样本,也许不完整)
abstract class Person{
private int hp, attack, speed;
public status() { System.out.println("HP : " + hp + ", ATT : " + attack + ", SPD : " + speed); }
public final punch(){ doPunch(); }
public final getPunched(){ doGetPunched(); }
protected abstract void doPunch();
protected abstract void doGetPunch();
/* plus standard getters/setters to save up your time */
}
class Bob extends Person{
private String name = "Bob";
public Bob(){ setHp(100); setAttack(25); setSpeed(15); }
protected doPunch(){ System.out.println(name + " punches");}
protected doGetPunched(){ System.out.println(name + " gets punched"); setHp(getHp()-5); }
}
同样适用于Jake
课程。
它是如何工作的?通过制作主要方法(punch
和getPunched
)final
,您可以放心,您希望在这些方法中提供的任何骨架实现都适用于每个扩展类。然后,像Bob
这样的类只是插入你想要的行为。这个例子真的很基础,可能不是展示这种模式能力的最好的例子,但你可以想到更复杂的模板(或可插拔的行为),比如punch
在特定情况下或getPunched
具有更多效果,只有其中一个受派生类控制。