Шаблон состояния Java с другой реализацией, приводящий к трудной для понимания ошибке

1

Почему этот Java-код не работает.

abstract class Actor {
    Actor a = new HappyActor();
    abstract void act();
    void change() { a = new SadActor(); }
    void go() { a.act(); }
}

class HappyActor extends Actor {
    public void act() {
        System.out.println( ":)" );
    }
}

class SadActor extends Actor {
    public void act() {
        System.out.println( ":(" );
    }
}

public class TransmogrifyMe {
    public static void main( String[] args ) {
        Actor s = new HappyActor();
        s.go(); // Prints ":)"
        s.change();
        s.go(); // Prints ":("
    }
}

Это была моя модификация шаблона популярного штата, но, к сожалению, не работает: оператор присваивания в абстрактном классе вызывается в начале каждого конструктора класса, наследующего его. Поэтому оператор присваивания пытается создать объект производного класса. т.е. Actor a = new HappyActor() вызывается, когда вызывается конструктор класса HappyActor который приводит к бесконечному циклу вызова конструктора. Это действительно то, что происходит?

Рабочая версия выглядит следующим образом:

abstract class Actor {
    abstract void act();
}   

class HappyActor extends Actor {
    public void act() {
        System.out.println( ":)" );
    } 
}   

class SadActor extends Actor {
    public void act() {
        System.out.println( ":(" );
    } 
}   

class Stage {
    Actor a = new HappyActor();
    void change() { a = new SadActor(); }
    void go() { a.act(); }
}   

public class Transmogrify {
    public static void main( String[] args ) {
        Stage s = new Stage();
        s.go(); // Prints ":)"
        s.change();
        s.go(); // Prints ":("
    }
}

Полученное сообщение об ошибке в первом коде представляет собой нескончаемый цикл этих двух строк:

at Actor.<init>(TransmogrifyMe.java:6)
at HappyActor.<init>(TransmogrifyMe.java:12)

Возможно ли реализовать такое изменение состояния с помощью наследования. Я нашел это в книге Брюса Эккеля " Думая на Java". Напротив, вы не можете решить наследовать по-разному во время выполнения; которые должны быть полностью определены во время компиляции

  • 5
    "Это действительно то, что происходит?" Да. Помимо этого, каков твой вопрос?
  • 0
    Вы пытаетесь просто добавить больше переходов между состояниями?
Показать ещё 3 комментария
Теги:
polymorphism
abstract-class

4 ответа

0
Лучший ответ

Избавьтесь от переменной класса. Ваши методы должны выглядеть так:

State doSomething() {
  // do something, e.g. println.
  return new NextState();
  // or return this to stay in the current state.
}

и они затем используются следующим образом:

state = state.doSomething();

т.е. вы return следующее состояние.

например

class HappyState implements State {
  public State change() {
    return new SadState();
  }
}
0

Поэтому я сделал это так, согласно предложению Анони-Муса:

// StateChange.java
// Implementing the state pattern using inheritance

interface State {
    public State change();
    public void print();
}

class SadState implements State {
    public State change() {
        return new SadState();
    }
    public void print() {
        System.out.println( ":(" );
    }
}
class HappyState implements State {
    public State change() {
        return new SadState();
    }
    public void print() {
        System.out.println( ":)" );
    }
}

public class StateChange {
    public static void main( String[] args ) {
        State h = new HappyState();
        h.print(); // prints :)
        h = h.change();
        h.print(); // prints :(
        h = h.change();
        h.print(); // prints :)
    }
}

Это лучшее, что я мог сделать!

0

Во втором примере кода, который действительно является правильным, Actor является "State" Stage. (Возможно, Актер, будучи настолько независимым от Сцена, больше похож на Стратегию, но на этом уровне сложности разница не имеет смысла.)

Вы хотите сделать что-то еще?

  • 0
    То, что я хочу реализовать, это изменение состояния с наследованием . Так что давайте избегать класса Stage. Я хочу создать объект «Актер» (ни «Счастливый», ни «Грустный»), и заставить этот объект иногда вести себя как «Счастливый актер», а иногда «грустный». Поэтому мой вопрос: есть ли способ сделать это без композиции?
  • 0
    Переименуйте классы: текущую иерархию актеров в Brain, HappyBrain и SadBrain и текущую сцену в актера (содержащую ссылку на мозг). Любая ссылка от Актера на другого Актера (или от Мозга к другому Мозгу) просто неверна, даже без циклических цепочек вызова конструктора.
0

Подумайте, что происходит, когда вы HappyActor создать свой первый HappyActor. По умолчанию ctor нужно сначала создать кишки Actor, и первое, что делает, пытается построить HappyActor. Бум, бесконечная рекурсия.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню