代码在继承时重复

时间:2015-02-02 20:10:10

标签: java inheritance methods

我从一本名为" 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覆盖每个子构造函数中的变量,但是我仍然感兴趣如果它可以寻址到子变量,并且如何做到这一点。

5 个答案:

答案 0 :(得分:3)

我会告诉你为什么我不喜欢那段代码。

基本上,BobJake不应该是类。它们应该是实例。他们不代表类的人,他们应该是个人。当然,你可以说班级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)

首先,我认为BobJake不应该是单独的类。没有什么根本不同,他们这样做是值得的。在内部,它们是相同的,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

现在,如果您引入了需要唯一逻辑来执行攻击的新角色(例如WarriorArcher),那么创建子类是值得的。

请参阅:

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课程。

它是如何工作的?通过制作主要方法(punchgetPunchedfinal,您可以放心,您希望在这些方法中提供的任何骨架实现都适用于每个扩展类。然后,像Bob这样的类只是插入你想要的行为。这个例子真的很基础,可能不是展示这种模式能力的最好的例子,但你可以想到更复杂的模板(或可插拔的行为),比如punch在特定情况下或getPunched具有更多效果,只有其中一个受派生类控制。