继承时代码重复
Code repeating while inheriting
我正在从一本名为 "Thinking in Java" 的书中学习 Java,我想尝试一些 inheritance/polymorphism。但是我 运行 进入了一些我不太喜欢的代码。开始了...
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(); }
}
现在一切正常(可能有一些拼写错误,我的版本有点长,所以我在这里重新输入了一个较短的版本)。
我想知道的是,有没有办法让 classes Bob 和 Jake 的 punch() 和 getPunched() 方法不重复?更具体地说,我可以将它们都放在 class Person 中,同时保留 person 仍会打印其子名 ("Bob/Jake punches") 的功能,还是我必须将其放在静态中在别处发挥作用?如果我必须把它放在一个函数中,它会是什么样子,因为我也在使用保留命令 "super".
我知道这段代码看起来非常琐碎且毫无用处,对此我深表歉意,但它确实如此,所以我可以在编写代码时真正看到自己在做什么。
谢谢! :)
EDIT :在写这篇文章时我意识到我可以在父 class "Person" 中创建一个受保护的字符串名称并通过 setter 覆盖每个子构造函数中的变量,但是我仍然感兴趣如果有可能寻址到一个子变量,那么一个这样做。
But I'm still interested if it's possible to address to a child variable, and how would one do that.
要实现这样的关系,只要在你的parentclass中保护变量name
即可。然后您可以在继承的 classes 中访问它,就像私有属性一样。
另一种方法是提供一个受保护的(或 public)构造函数,它接受一个 String name
,然后将其设置在您的 parent class 中。然后在Person
中实现punch()
。不要忘记在 child classes 的构造函数中通过 super()
调用 Person
的构造函数。
我更喜欢方式二,因为它更干净(但这是我个人的意见)。这样您就不需要 setter 方法并使 name
属性成为一种不可变的类型,这通常是可取的。
另一种可能性是在 class 层次结构中插入一个 class 在 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(); }
}
child class Bob 将不需要实现 punch() 或 getPunched(),因为它们已在 class BobOrJake 中实现。
Bob 变得非常简单(但请注意额外的 setName() 来设置 Bob 的名字):
public class Bob extends BobOrJake {
public Bob(){ setName("Bob");setHp(100); setAttack(25); setSpeed(15); }
}
您不必像我那样将 BobOrJake 抽象化,但如果您永远不打算实例化 BobOrJake,则可能希望将其抽象化 object。
我会告诉你为什么我不喜欢那个代码。
基本上,Bob
和 Jake
不应该是 class。它们应该是实例。他们不代表class人,他们应该是个人。当然,您可以说 class Bob
代表世界上所有名字叫 Bob 的人,但这不是真实世界的用例,甚至不是很有启发性的用例。为什么所有 Bob 的生命值、速度等都应该相同?
一旦你意识到这一点,代码重复的原因就很清楚了——因为这种糟糕的设计,你正在编写一些应该对所有人通用的东西,或者至少对某些人(这将是一个子class of Person
) 对每个人一次又一次。这违背了面向对象编程和一般编程的目的。
所以,考虑个人的名字。所有人都有名字。所以这确实应该是Person
class中的一个字段。它应该像其他所有东西一样有一个 getter 和一个 setter。
Bob 与 Jake 不同的地方应该作为参数传递给构造函数。
如果您想要一些人共有的行为而不是其他人的行为,它可能应该在 Person
的单个子 class 中定义。例如,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 );
这是对世界更好的表现——每个人都有自己的生命值、速度和名字。行为是 classes 的问题。如果它属于 "a class on his own" 的个人 - 例如 ChuckNorris
class - 那么它的行为并不意味着在几个 class 中重复。而且您仍然需要在 class.
中创建一个个体
首先,我认为 Bob
和 Jake
不应该是分开的 class。他们所做的事情并没有根本不同,值得这样做。在内部它们是相同的并且 Bob
/Jake
应该只是相同 class Person
.
的实例
例如,如果我们有 100 个人,除了他们的 'stats' 之外,他们都是相同的,您是否要创建 100 个单独的 classes?如果我们想在运行时创建新人(无论是通过用户输入还是从文件中读取信息)怎么办?你做不到。
相反,内部统计信息(名称、攻击等)应该至少在对象初始化时是可配置的。
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
),那么创建子 classes 是值得的.
参见:
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
免责声明:我的个人解决方案在某种程度上类似于:您的示例中不需要继承。
但是,另一方面,我们总是从基础开始学习。因此,提出的解决方案与 inheritance/good 实践有关(实例而不是 classes),因此我想向您推荐一种不同的方法: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
class.
也是如此
它是如何工作的?通过使主要方法(punch
和 getPunched
)成为 final
,您可以放心,您希望在这些方法中提供的任何框架实现都适用于每个扩展 class。然后,class 就像 Bob
一样简单地插入您的预期行为。该示例非常基础,可能不是展示此类模式功能的最佳示例,但您可以想到更复杂的模板(或可插入行为),例如特定情况下的 punch
或 getPunched
效果比较多,只有一个是派生控制的class.
我正在从一本名为 "Thinking in Java" 的书中学习 Java,我想尝试一些 inheritance/polymorphism。但是我 运行 进入了一些我不太喜欢的代码。开始了...
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(); }
}
现在一切正常(可能有一些拼写错误,我的版本有点长,所以我在这里重新输入了一个较短的版本)。
我想知道的是,有没有办法让 classes Bob 和 Jake 的 punch() 和 getPunched() 方法不重复?更具体地说,我可以将它们都放在 class Person 中,同时保留 person 仍会打印其子名 ("Bob/Jake punches") 的功能,还是我必须将其放在静态中在别处发挥作用?如果我必须把它放在一个函数中,它会是什么样子,因为我也在使用保留命令 "super".
我知道这段代码看起来非常琐碎且毫无用处,对此我深表歉意,但它确实如此,所以我可以在编写代码时真正看到自己在做什么。
谢谢! :)
EDIT :在写这篇文章时我意识到我可以在父 class "Person" 中创建一个受保护的字符串名称并通过 setter 覆盖每个子构造函数中的变量,但是我仍然感兴趣如果有可能寻址到一个子变量,那么一个这样做。
But I'm still interested if it's possible to address to a child variable, and how would one do that.
要实现这样的关系,只要在你的parentclass中保护变量name
即可。然后您可以在继承的 classes 中访问它,就像私有属性一样。
另一种方法是提供一个受保护的(或 public)构造函数,它接受一个 String name
,然后将其设置在您的 parent class 中。然后在Person
中实现punch()
。不要忘记在 child classes 的构造函数中通过 super()
调用 Person
的构造函数。
我更喜欢方式二,因为它更干净(但这是我个人的意见)。这样您就不需要 setter 方法并使 name
属性成为一种不可变的类型,这通常是可取的。
另一种可能性是在 class 层次结构中插入一个 class 在 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(); }
}
child class Bob 将不需要实现 punch() 或 getPunched(),因为它们已在 class BobOrJake 中实现。
Bob 变得非常简单(但请注意额外的 setName() 来设置 Bob 的名字):
public class Bob extends BobOrJake {
public Bob(){ setName("Bob");setHp(100); setAttack(25); setSpeed(15); }
}
您不必像我那样将 BobOrJake 抽象化,但如果您永远不打算实例化 BobOrJake,则可能希望将其抽象化 object。
我会告诉你为什么我不喜欢那个代码。
基本上,Bob
和 Jake
不应该是 class。它们应该是实例。他们不代表class人,他们应该是个人。当然,您可以说 class Bob
代表世界上所有名字叫 Bob 的人,但这不是真实世界的用例,甚至不是很有启发性的用例。为什么所有 Bob 的生命值、速度等都应该相同?
一旦你意识到这一点,代码重复的原因就很清楚了——因为这种糟糕的设计,你正在编写一些应该对所有人通用的东西,或者至少对某些人(这将是一个子class of Person
) 对每个人一次又一次。这违背了面向对象编程和一般编程的目的。
所以,考虑个人的名字。所有人都有名字。所以这确实应该是Person
class中的一个字段。它应该像其他所有东西一样有一个 getter 和一个 setter。
Bob 与 Jake 不同的地方应该作为参数传递给构造函数。
如果您想要一些人共有的行为而不是其他人的行为,它可能应该在 Person
的单个子 class 中定义。例如,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 );
这是对世界更好的表现——每个人都有自己的生命值、速度和名字。行为是 classes 的问题。如果它属于 "a class on his own" 的个人 - 例如 ChuckNorris
class - 那么它的行为并不意味着在几个 class 中重复。而且您仍然需要在 class.
首先,我认为 Bob
和 Jake
不应该是分开的 class。他们所做的事情并没有根本不同,值得这样做。在内部它们是相同的并且 Bob
/Jake
应该只是相同 class Person
.
例如,如果我们有 100 个人,除了他们的 'stats' 之外,他们都是相同的,您是否要创建 100 个单独的 classes?如果我们想在运行时创建新人(无论是通过用户输入还是从文件中读取信息)怎么办?你做不到。
相反,内部统计信息(名称、攻击等)应该至少在对象初始化时是可配置的。
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
),那么创建子 classes 是值得的.
参见:
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
免责声明:我的个人解决方案在某种程度上类似于
但是,另一方面,我们总是从基础开始学习。因此,提出的解决方案与 inheritance/good 实践有关(实例而不是 classes),因此我想向您推荐一种不同的方法: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
class.
它是如何工作的?通过使主要方法(punch
和 getPunched
)成为 final
,您可以放心,您希望在这些方法中提供的任何框架实现都适用于每个扩展 class。然后,class 就像 Bob
一样简单地插入您的预期行为。该示例非常基础,可能不是展示此类模式功能的最佳示例,但您可以想到更复杂的模板(或可插入行为),例如特定情况下的 punch
或 getPunched
效果比较多,只有一个是派生控制的class.