Одновременно герой против программы монстр


Я сделал простой параллельной программы. Он представляет собой сражение между героем и монстром. Я новичок в параллелизм, так я просто хочу услышать Ваше мнение. Какие плохие и хорошие стороны моего кода и как я могу улучшить его?

Атаку Интерфейс

public interface Attack {
public void attack(Object s, int dmg);
}

Основной класс

public class Main {
public static void main(String[] args) {
    Monster monster = new Monster(50, "monster");
    Hero hero = new Hero (50, "hero");
    ThreadFight tf = new ThreadFight(hero, monster);

    Thread t1 = new Thread (new Runnable() {
        @Override
        public void run() {
            tf.hitHero();
        }
    });

    Thread t2 = new Thread (new Runnable() {
        @Override
        public void run() {
            tf.hitMonster();
        }
    });

    t1.start();
    t2.start();
}
}

Герой Класса

public class Hero implements Attack{
private int hp;
private String name;
private Monster monster;

public Hero (int hp, String name) {
    this.hp = hp;
    this.name = name;
}

public int getHp() {
    return this.hp;
}

public String getName() {
    return this.name;
}

public void decreaseHp(int dmg) {
    this.hp = getHp() - dmg;
}

public void setMonster(Monster monster) {
    this.monster = monster;
}

@Override
public void attack(Object s, int dmg) {
    if (s instanceof Monster) {
        Monster m = (Monster) s;
        System.out.println("Monster hp: " + m.getHp());
        System.out.println("Hero dealt " + dmg + " damage");
        m.decreaseHp(dmg);
        System.out.println("Monster hp is now: " + m.getHp());
        System.out.println("--------------------------");
    }
}
}

Класс Монстр

public class Monster implements Attack{
private int hp;
private String name;
Hero hero;

public Monster (int hp, String name) {
    this.hp = hp;
    this.name = name;
}

public int getHp() {
    return this.hp;
}

public String getName() {
    return this.name;
}

public void decreaseHp(int dmg) {
    this.hp = getHp() - dmg;
}

public void setHero(Hero hero) {
    this.hero = hero;
}

@Override
public void attack(Object s, int dmg) {
    if (s instanceof Hero) {
        Hero h = (Hero) s;
        System.out.println("Hero hp: " + h.getHp());
        System.out.println("monster dealt " + dmg + " damage");
        h.decreaseHp(dmg);
        System.out.println("Hero hp is now: " + h.getHp());
        System.out.println("--------------------------");
    }
}
}

ThreadFight Класс

public class ThreadFight{
Hero hero;
Monster monster;
Random random = new Random();
Object lock = new Object();

public ThreadFight(Hero hero, Monster monster) {
    this.hero = hero;
    this.monster = monster;
}

public void hitMonster() {

    while (monster.getHp() > 0 && hero.getHp() > 0) {
        synchronized (lock) {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            hero.attack(monster, random.nextInt(20));

        }
        if (monster.getHp() < 0) {
            System.out.println("Hero won.");
        }
    }
}

public void hitHero() {

    while (hero.getHp() > 0 && monster.getHp() > 0) {
        synchronized (lock) {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            monster.attack(hero, random.nextInt(20));

        }
        if (hero.getHp() < 0) {
            System.out.println("Monster won.");
        }
    }
}
}


Комментарии
2 ответа

Одна большая проблема с этот код, что я вижу, заключается в том, что вы должны рассмотреть то, что вы синхронизация, и почему. Я понимаю, ваша главная цель здесь состоит в изучении параллельности, но я также добавил некоторые другие предложения, которые вы можете или не можете хотеть использовать.

Параллелизм

Есть несколько проблем с тем, как вы синхронизируете вещи. В вашем случае, что вы хотите, чтобы предотвратить это:


  1. Чередование на игроков атаки. Это может произойти, если два
    отдельные нити попробовать и урон такой же враг.

  2. Предотвращение как герой и монстр "победы" (убивая друг друга). Примечание: Вы может не хотите допустить этого

Синхронизированные блоки будут препятствовать любой код блокировки выполняются одновременно. Поэтому нужно синхронизировать:


  1. Изменения в здоровье себя или врага.

  2. Проверки здоровья себя или врага.

Вы не должны синхронизировать Thread.sleep(); звонки, потому что каждый поток будет останавливаться и ждать всех остальных, чтобы перестать спать. Так что это похоже на ваш код? Давайте посмотрим:

public void hitHero() {
boolean isFighting = true;
while (isFighting) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock) {
monster.attack(hero, random.nextInt(20));
if (hero.getHp() < 0) System.out.println("Monster won.");
isFighting = hero.getHp() > 0 && monster.getHp() > 0;
}

}
}

Обратите внимание, что я использовал логический isFighting для того чтобы переместить чеков для HP внутри синхронизированного блока.

Это решение не помешает двум убивать друг друга, если один погибает, а другой ждет, чтобы напасть. Чтобы предотвратить это, вам потребуется добавить другие проверки внутри синхронизированного блока.

Инкапсуляция

Рассмотреть монстра и Героя. Они оба разделяют некоторые общие черты. Они оба есть HP, имя, и может как-то атаковать. Почему бы не сделать родительский класс (назовем его Fighter), что не только интерфейс, но и обеспечивает реализацию для атаки, и хп + имя:

public class Fighter {
private int hp;
private String name;

public Fighter (int hp, String name) { ... }
public int getHp() { ... }
public String getName() { ... }
public void decreaseHp(int dmg) { ... }

public void attack(Fighter f, int dmg) {
System.out.println(f.name + " hp: " + f.getHp());
System.out.println(name + " dealt " + dmg + " damage");
f.decreaseHp(dmg);
System.out.println(f.name + " hp is now: " + f.getHp());
System.out.println("--------------------------");
}

Большая вещь об этом заключается в том, что вы теряете необходимость instanceof проверить, так как вы знаете, вы можете атаковать любого бойца вы хотите. Теперь при добавлении нового типа истребителя очень легко:

public class Hero extends Fighter {   
public Hero (int hp, String name) {
super(hp, name);
}
}

В дополнение к этому, я бы, конечно, рассмотреть все переменные внутри ThreadFight частная и создание + запуск двух Threadвнутри ThreadFight конструктор (или внутри startFight() метод).

Хотя в целом, я считаю, что вы сделали хорошую работу! Вы работаете нити правильно и чуть не синхронизации справа. Это может занять некоторое время, чтобы полностью понять, что происходит с несколько потоков и блокировок. Хотя возможны улучшения монстра и героя тоже были хорошо построены. Это небольшая программа, и вы, конечно, могли бы расширить его, если вам захотелось заняться более сложной резьбы ситуациях.

2
ответ дан 18 марта 2018 в 02:03 Источник Поделиться

Я бы добавил больше комментариев. Как док-строки в функции и классы, которые только стремится быть хорошей практикой, так как это повышает читабельность кода. В противном случае, насколько я могу сказать, Ты кода-это очень хорошо.

0
ответ дан 17 марта 2018 в 11:03 Источник Поделиться