使用状态模式解耦状态

发布于 2024-12-12 16:22:55 字数 3673 浏览 0 评论 0原文

我不确定对于我正在实现的特定状态模式,最佳的面向对象设计方法应该是什么。请考虑以下事项:

public class World {
    private Animal dog_;
    private Animals cats_;
    …..
    public void sendDogRequest(DogRequest request) {
        dog_.sendRequest(request);
    }
    …
    public Cat getCat(String catName) {
        …
        return cat;
    }
    ...
}

public class Animal<RequestType extends Request, StateType extends State> {
    private State<StateType> currentState_;
    ….
    public void sendRequest(RequestType request) {
        request.sendToState(currentState_);
    }
    public void setState(StateType state) {
        currentState_ = state;
    }
}

public class Dog extends Animal<DogState> {
    …
}

public class DogState extends State {
    public DogState(Dog dog) {
    …
    }
    public void seeCat(Cat cat) {   }
}

public class OnLeashState extends DogState {
    public void seeCat(Cat cat) {
        dog.setState(new BarkingState());
    }
}

public class OffLeashState extends DogState {
    public void seeCat(Cat cat) {
        dog.setState(new ChasingAfterAnimalState(cat));
        cat.sendRequest(new RunAwayRequest(cat));
    }
}

public interface Request<StateType extends State> {
    public void sendToState(StateType state);
}

public class DogRequest extends Request<DogState> { }

public class SeeCatRequest extends DogRequest {
    private Cat cat_;   
    public SeeCatRequest(Cat cat) {
        cat_ = cat;
    }
    public void sendToState(DogState state) {
        state.seeCat(state);
    }
}

public class Controller() {
    public Controller(World model, View view) {
        …
    }
    ...
    public void catSelected(String catName) {
        Cat cat = world.getCat(catName);
        Dog dog = world.getDog();
        world.sendDogRequest(new SeeCatRequest(cat));
    }
    …
}

我犹豫的地方是此处“new”一词的用法,即。使用另一个 State 实例化一个 new SomeState(),或者在 Controller 或另一个 State 中实例化 new SomeRequest()。在我看来,这会在 State 及其兄弟姐妹之间以及 ControllerState 之间产生高度耦合。

要求如下:

  1. 必须能够添加新的状态,例如添加 SniffingState
  2. 还必须能够用新的状态取代现有的状态。例如,我应该能够用执行不同操作的不同 OffLeashState 替换 OffLeachState。例如(由于某种原因代码不会格式化):

    公共类 OffLeachState2 扩展了 DogState {
    公共无效seeCat(猫猫){
    if (dog.knows(cat)) {
    // 狗更改为“PlayWithCatState”
    // cat 收到“PlayWithDog”请求
    } 否则{
    // 狗更改为“ChaseAnimalState”
    }
    }
    }

  3. 最后,World 类中的所有更改都必须被记录。这意味着世界级有一个记录器可以跟踪正在发生的一切。这也是因为 World 类是一个模型,并且必须触发 notifyObservers() 以便视图知道要执行某些操作。

我的问题是,状态、请求等应该存储在哪里?例如:

  1. Dog 中应该有状态“getters”吗?例如,dog.getBarkingState()、dog.getOnLeashState()等?这似乎有道理,但它并不会使 Dog 类抵制更改。即,每次添加新的 DogState 类时,我还必须确保 Dog 有一个 getter。此外,World 不知道这些更改,因此它不会记录它们,也不会通知观察者。

  2. 是否应该有一个名为 DogStates 的类并且我可以运行 DogStates.getBarkingState()?再次出现与上面类似的问题。

  3. 它们应该是 World 类的一部分吗?例如,world.setDogState(dog, world.getDogBarkingState()?这可以解决日志记录/更新问题,但会给 World 类带来太多责任。 p>

  4. 应该是它们的某种组合吗,例如 world.setState(dog,dog.getBarkingState())?这可能会很好。 ,但不能保证类型安全。例如,我可以传入 Dog 对象和 CatState,但它不会知道其中的区别。

解决方案 #4 对我来说似乎是最好的,但我想了解有关此问题的一些其他意见。

同样的问题也适用于 Request 对象。我最初想通过与对象关联的字符串发送请求,例如 world.sendRequest(dog, DogRequests.SEE_CAT) ,但后来我无法通过cat 对象作为参数。

非常感谢您抽出时间!

I am unsure as to what the best OO design approach should be regarding a particular State pattern I am implementing. Please consider the following:

public class World {
    private Animal dog_;
    private Animals cats_;
    …..
    public void sendDogRequest(DogRequest request) {
        dog_.sendRequest(request);
    }
    …
    public Cat getCat(String catName) {
        …
        return cat;
    }
    ...
}

public class Animal<RequestType extends Request, StateType extends State> {
    private State<StateType> currentState_;
    ….
    public void sendRequest(RequestType request) {
        request.sendToState(currentState_);
    }
    public void setState(StateType state) {
        currentState_ = state;
    }
}

public class Dog extends Animal<DogState> {
    …
}

public class DogState extends State {
    public DogState(Dog dog) {
    …
    }
    public void seeCat(Cat cat) {   }
}

public class OnLeashState extends DogState {
    public void seeCat(Cat cat) {
        dog.setState(new BarkingState());
    }
}

public class OffLeashState extends DogState {
    public void seeCat(Cat cat) {
        dog.setState(new ChasingAfterAnimalState(cat));
        cat.sendRequest(new RunAwayRequest(cat));
    }
}

public interface Request<StateType extends State> {
    public void sendToState(StateType state);
}

public class DogRequest extends Request<DogState> { }

public class SeeCatRequest extends DogRequest {
    private Cat cat_;   
    public SeeCatRequest(Cat cat) {
        cat_ = cat;
    }
    public void sendToState(DogState state) {
        state.seeCat(state);
    }
}

public class Controller() {
    public Controller(World model, View view) {
        …
    }
    ...
    public void catSelected(String catName) {
        Cat cat = world.getCat(catName);
        Dog dog = world.getDog();
        world.sendDogRequest(new SeeCatRequest(cat));
    }
    …
}

My area of hesitation is with the usages of the word new here, ie. instantiating a new SomeState() with another State, or new SomeRequest() within the Controller or another State. It seems to me that this would produce high coupling between the States and their siblings, as well as the Controller and States.

The requirements are as follows:

  1. It MUST be possible to add new States, for example adding a SniffingState.
  2. It also MUST be possible to replace existing States with new ones. For example, I should be able to replace OffLeachState with a different OffLeashState that performs a different action. For example (for some reason the code won't format):

    public class OffLeachState2 extends DogState {
    public void seeCat(Cat cat) {
    if (dog.knows(cat)) {
    // dog changes to "PlayWithCatState"
    // cat gets a "PlayWithDog" request
    } else {
    // dog changes to "ChaseAnimalState"
    }
    }
    }

  3. Finally, all changes within the World class MUST be logged. That means that the World class has a logger which is keeping track of everything that is going on. This is also because the World class is a model, and has to fire off a notifyObservers() so that the view knows to do something.

My question is, where should the states, requests etc be stored? For example:

  1. Should there be state "getters" in Dog? for example, dog.getBarkingState(), dog.getOnLeashState(), etc? This seems to make sense, but it doesn't make the Dog class resistant to change. Ie, every time I add a new DogState class, I also have to make sure that Dog has a getter for it. Also, the World doesn't know about these changes, so it doesn't log them nor notify observers.

  2. Should there be a class called DogStates and I can run DogStates.getBarkingState()? Again, similar problems to the one above.

  3. Should they be a part of the World class? For example, world.setDogState(dog, world.getDogBarkingState()? This would solve the logging/updating problem, but puts too much responsibility on the World class.

  4. Should it be some combination thereof, for example world.setState(dog, dog.getBarkingState()? This COULD be good, but doesn't assure type safety. For example, I could pass in a Dog object with a CatState, and it wouldn't know the difference.

Solution #4 seems the best to me, but I would like some other opinions about this issue.

The same question applies to the Request object. I originally wanted to send Requests by Strings which were associated with an object, for example world.sendRequest(dog, DogRequests.SEE_CAT), but then I couldn't pass the cat object as an argument.

Thank you very much for your time!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

苍暮颜 2024-12-19 16:22:55

1.) 这看起来像是一道编程考试题。在这种情况下,如果不确定该怎么做,使用模式!因此,每个 State 都应该由 StateFactory 生成,并为 Factory 实例提供一些有关 World 的信息,以便它可以决定创建哪个特定 State 实例。

这是日志记录的内容:

public class World implements StateChangeListener {
  private Animal dog_;
  private Animals cats_;

  private final List<StateChangeListener> listeners = new ArrayList<StateChangeListener>();

  public World() {
    listeners.add(this);
  }

  // Instead of sending DogRequests to Dogs via the sendDogRequest method:
  public <RequestType extends Request> void sendRequest(
      Animal<RequestType, ?> animal, Request<RequestType> request) {
    animal.sendRequest(request);
    for(StateChangeListener listener : listeners) {
      listener.stateChanged(animal, request);
    }
  }

  public void stateChanged(Animal<?, ?> animal, State<?> state) {
    // ... log here ...
  }
...

还有工厂的内容(可能有点心不在焉,泛型可能无法正常工作;o)。

public enum LocationEnum {
  HOME, PARK, POND, FOREST
}

public interface StateFactory<StateType extends State> {
  State<StateType> create(Animal<StateType, ?> animal, Context context);
}

// Do stuff Dogs do.
public class DogStateFactory<DogState> {
  public State<DogState> create(Animal<DogState, ?>, Context context) {
    if(context.currentAnimalLocation==LocationEnum.POND) {
      return new IgnoreEverythingState();
    }else if(context.currentAnimalLocation==LocationEnum.HOME){
      return new PerpetualBarkState();
    }else {
      return new FollowEveryCatState();
    }
  }
}

public class Animal<RequestType extends Request, StateType extends State> {
  private StateFactory<StateType> stateFactory;
  private State<StateType> currentState_;

  public void sendRequest(Request<RequestType> request) {
    request.sendToState(currentState_);
  }

  // A specific animal knows what it wants to do, depending on it's current
  // state and it's situational context. We don't want other animals
  // to set the state for us.
  public void determineState() {
    currentState_ = stateFactory.create(this, new Context(...));
    // One might want to extend the messaging stuff in a way that
    // the World instance can log this state change.
  }
}

public class Dog extends Animal<DogRequest, DogState> {
  public Dog() {
    this.stateFactory = new DogStateFactory<DogState>();
  }
}

2.) 如果你想让世界知道其中发生的一切,你可以用消息代替状态设置者,让世界实例监听每个人的状态变化。

1.) This looks like a programming exam question. In such scenarios, if unsure what to do, use a Pattern! So every State should be generated by a StateFactory and give the Factory instance some information about the World so it can decide which specific State instance to create.

Here's the logging stuff:

public class World implements StateChangeListener {
  private Animal dog_;
  private Animals cats_;

  private final List<StateChangeListener> listeners = new ArrayList<StateChangeListener>();

  public World() {
    listeners.add(this);
  }

  // Instead of sending DogRequests to Dogs via the sendDogRequest method:
  public <RequestType extends Request> void sendRequest(
      Animal<RequestType, ?> animal, Request<RequestType> request) {
    animal.sendRequest(request);
    for(StateChangeListener listener : listeners) {
      listener.stateChanged(animal, request);
    }
  }

  public void stateChanged(Animal<?, ?> animal, State<?> state) {
    // ... log here ...
  }
...

And that Factory stuff (probably a bit scatterbrained, Generics might not work correctly ;o).

public enum LocationEnum {
  HOME, PARK, POND, FOREST
}

public interface StateFactory<StateType extends State> {
  State<StateType> create(Animal<StateType, ?> animal, Context context);
}

// Do stuff Dogs do.
public class DogStateFactory<DogState> {
  public State<DogState> create(Animal<DogState, ?>, Context context) {
    if(context.currentAnimalLocation==LocationEnum.POND) {
      return new IgnoreEverythingState();
    }else if(context.currentAnimalLocation==LocationEnum.HOME){
      return new PerpetualBarkState();
    }else {
      return new FollowEveryCatState();
    }
  }
}

public class Animal<RequestType extends Request, StateType extends State> {
  private StateFactory<StateType> stateFactory;
  private State<StateType> currentState_;

  public void sendRequest(Request<RequestType> request) {
    request.sendToState(currentState_);
  }

  // A specific animal knows what it wants to do, depending on it's current
  // state and it's situational context. We don't want other animals
  // to set the state for us.
  public void determineState() {
    currentState_ = stateFactory.create(this, new Context(...));
    // One might want to extend the messaging stuff in a way that
    // the World instance can log this state change.
  }
}

public class Dog extends Animal<DogRequest, DogState> {
  public Dog() {
    this.stateFactory = new DogStateFactory<DogState>();
  }
}

2.) If you want the World to know everything happening in it, you could substitute the state setters whith messages and let the World instance listen to everybody's state changes.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文