使用状态模式

时间:2015-05-04 20:26:40

标签: java design-patterns

我被指派设计一个软件(在java中),其中包含各自具有状态的作业的项目和作业。当我们了解GoF-patterns时,状态模式对我来说似乎是一个明显的选择,但现在我在执行方面遇到了一些麻烦。

在我目前的设计中,我有一个Job - 类来表示具有特定状态的作业和一个Project - 具有作业列表的类。为了表示四种可能的状态(AVAILABLE - UNAVAILABLE - FINISHED - FAILED),我创建了一个枚举State,其中包含依赖于状态的工作方法枚举。

状态模式我遇到的问题如下:

  1. Project中,我希望有一个方法可以返回项目的所有AVAILABLE个工作(甚至可以返回所有FINISHED或{{1个但是,状态模式没有提到如何做到这一点。我们还听说在使用状态模式时不应该使用FAILEDpublic boolean isAvailable()中的Job方法(因为这正是人们在使用此模式时要避免的)
  2. 需要一种方法,将状态从State作业更改为AVAILABLEFINISHED。这个问题是我在使用State模式时不确定如何实现它。
  3. 我有一些解决方案,但我想知道是否有其他/更好的解决方案来很好地实现状态模式。

    1. 对于第一个问题,我将在FAILED中实现一个public Collection<Job> filter(Collection<Job>)方法,它将返回给定集合的子集,只返回当前状态的那些作业。 State中的getAvailableJobs()方法如下所示。这种方法对我来说的缺点是,当Project需要了解Project时,耦合也会增长。
    2. 对于第二个问题,我可以编写一个State方法来检查update(State newState)是完成还是失败,或者我可以编写方法newState和方法{{1 (当然在fail())。第二个问题似乎是某些状态逻辑回到finish(),但我不确定State方法是否也是最好的方法。
    3. 我希望我已经足够清楚,并且有人可以帮助我理解为什么这些解决方案足够好或者不好以及可能的替代方案

      提前致谢。

      一些额外的代码可以使一些事情变得清晰:

      Job

4 个答案:

答案 0 :(得分:1)

嗯,我有一些时间,我喜欢实现模式,所以我试着在这里实现你的概念。似乎挑战是关于如何维护按作业状态排序的作业列表,当状态隐藏在作业中时。以下是我将如何做到这一点,如果你不能在Job中公开使用一个字段。

Job将保存对Project对象的引用,对它的状态对象的引用,并定义接口IJob(这是用C#编写的)。然后,每个州通过调用Project中的方法来确保状态与其环境的一致性,以通知其变化。您必须在Project上创建一个有意义的接口。我在这里添加了一个结构,但是你必须定义自己的结构,无论什么都有意义。

public class Project {

    private List<Job> listOfJobs;
    private List<Job> listOfAvailJobs;
    private List<Job> listOfNotAvailJobs;

    // Factory Method
    public Job CreateJob() {}

    public void JobIsAvailable(Job job) {
        listOfAvailJobs.Add(job);   
    }
    public void JobIsNotAvailable(Job job) {
        listOfNotAvailJobs.Add(job);
    }

}


public interface IJob {
    void Run();
    void Abort();
    void Delete();
}


public class Job : IJob {

    public Project project;
    protected JobState currentState;

    public Job(Project project) {
        this.project = project;
        currentState = new JobStateInit();
        project.JobIsAvailable(this);
    }

    // IJob Interface
    public void Run();
    public void Abort();
    public void Delete();

}

public abstract class JobState : IJob {

    protected Job job;

    public JobState(Job job) {
        this.Job = job;
    }

    // IJob Interface
    public void Run();
    public void Abort();
    public void Delete();

}

public class JobStateInit : JobState {
    // IJob Interface
    public void Run();
    public void Abort();
    public void Delete();
}

public class JobStateAvail : JobState {

    // IJob Interface
    public void Run() {
        this.job.project.JobIsNotAvailable(this.job);
    }
    public void Abort();
    public void Delete();
}

public class JobStateFailed : JobState {
    // IJob Interface
    public void Run() {
        throw new InvalidOperationException;
    }

    public void Abort() {}
    public void Delete() {}
}

答案 1 :(得分:1)

@Question 1:只要一个项目必须过滤其作业,过滤方法应该是Project类的一部分。您可以提供一个通用方法,根据给定状态和具体状态进行筛选(如果经常使用它,为什么不将其作为API的一部分):

class Project {
  private Set<Job> jobs;

  public Set<Job> filterJobs(JobState state) {
    return jobs.stream().filter(j -> j.getState() == state).collect(Collectors.toSet());
  }

  public Set<Job> availableJobs() {
    return filterJobs(JobState.AVAILABLE);
  }

}

两者都很好,对用户来说是透明的:

project.availableJobs();
project.filterJobs(JobState.<your state>);

@Question 2:还在思考&#34;多少州和#34;在里面。在描述中,只有来自AVAILABLE(2)的状态转换和仅与之相关的一种方法。根据我的经验和理解,应该在必要时应用设计模式,而不是从头开始。你的解决方案非常好。但考虑到这里描述的一些要求,没有状态模式可能会更简单。 (= YAGNI

答案 2 :(得分:0)

为避免Job和State之间的耦合,您可以移动&#34;过滤器&#34;单独一个类的方法,你可以通过让Job实现一个只有一个返回状态的方法的接口来进一步减少耦合。

此时,您已经创建了一个功能界面。如果您正在使用Java 8,您可以轻松创建Lambda表达式来执行相同的操作。当然,这可能会比你所处的领先一点,但希望你能得到这个想法。

答案 3 :(得分:0)

您在问题中引用了状态模式,但问题的大多数细节与状态模式无关。

状态模式仅限于Job的界面设计,以及可以对Job执行的操作的处理。 Job所处的每个状态必须提供此接口的某种实现。即:run(),quit(),abort()。这些方法将在Job上调用,实现将根据Job所处的状态而改变(即:AVAILABLE,FINISHED或FAILED)。

要实现这一点,您需要声明一个类Job,然后声明一个抽象类JobState,其中包含(具体)子类JobStateAvailJobStateFailed等。每个子类都将实现Job界面。然后,从客户端对Job的任何调用都将被委托给当前的JobState子类,以便正确处理。

JobProject之间的关系对我来说有点不清楚。然而,这种关系与州模式无关,所以这就是为什么你可能会感到困惑。我需要有关Project类目的的更多信息,以及如何创建Jobs。要根据状态实现作业列表,您可以做的是将作业添加到Project中的已定义列表中。也许这就是你想要的?