MVC设计中模型对象存放在哪里?

Where to store model objects in MVC design?

我正在使用 MVC 架构。

SignupController 从 SignupView 创建一个 User 对象。

这个用户应该存储在哪里?

我试过将其存储到 MainController 中,但是当我需要访问 User 对象时,我需要再次初始化 MainController,因此 User 现在为 null。

谢谢!

据我了解,基于 fxml 标记,您正在使用 Java FX 构建应用程序,这意味着在任何时候都只有一个 User应用程序。在这种情况下,最好的方法是将其存储在单独的 class 中,例如服务或存储库。

示例:

public class UserService {

    protected User user;

    public Optional<User> getUser() {
        return Optional.ofNullable(user);
    }

    public void setUser(User user) {
        this.user = user;
    }

}

然后要么使用 Dependency Injection (e.g. Guice) or some sort of a Singleton Pattern 实现,例如getInstance().

如果您谈论的是 Web 应用程序,我建议在会话中保留引用,因为这会将 User 绑定到执行 HTTP 请求的实际用户。

好吧,让我们退一步。 User是数据,在这种情况下,数据应该由模型层来管理和维护。

控制器负责协调视图和模型之间的动作和通知。从这个意义上讲,当用户在视图中触发请求(即执行注册操作)时,视图会触发控制器订阅的事件。控制器从视图中获取所需的信息并将该信息传递给模型。这可能会向后工作,模型根据任务的成功触发事件,控制器将通知视图......根据需要

所有这一切的重要部分是,没有人知道另一部分实际上是如何工作的......并且不在乎

让我们从基础开始。您需要某种方式来表示“用户”

public interface User {
    public String getName();
}

现在,您需要创建一个 User...

public interface UserFactoryListener {
    public void userCreated(User user);
    public void userCreationFailed(Exception exp); // Or some other error object
}

public interface UserFactory {
    public void makeUser(String name);

    public void addUserFactoryListener(UserFactoryListener listener);
    public void removeUserFactoryListener(UserFactoryListener listener);
}

所以,这是工厂的基本概念,它将采用用户的名称并创建一个新的 User 对象,这里要注意的重要一点是,我们不关心“如何”完成了,只有完成了,它会生成成功或失败事件

但是,如果我们还希望能够对用户进行身份验证怎么办?

public interface UserAuthenticationistener {
    public void userAuthenticated(User user);
    public void userAuthenticationFailed(Exception exp); // Or some other error object
}

public interface UserAuthenticator {
    public void authenticateUser(String name, char[] password) throws SecurityException;

    public void addUserAuthenticator(UserAuthenticator listener);
    public void removeUserAuthenticator(UserAuthenticator listener);
}

所以,这是相同的基本思想,它需要一个名称和密码并以某种方式验证用户,我们不关心,并将生成一个我们可以订阅的成功或失败事件。

“是的,但是用户模型呢?”我听到你说,很高兴你问!

public interface UserSigninModel extends UserFactory, UserAuthenticator {
    
}

嗯,好的,这是登录模型,但它封装了我们要支持的两个主要功能,即注册或登录。

现在,我们需要一些方法来协调两者,这是控制器的工作...

public interface UserSigninController extends UserSigninViewListener, UserFactoryListener, UserAuthenticationistener {
    public UserSigninModel getModel();
    public UserSigninView getView();
}

...好吧,这有点让人印象深刻,但这就是重点

最后,我们需要某种方式与实际用户交互,即视图!

public interface UserSigninViewListener {
    public void signupUser(UserSigninView view);
    public void authenticateUser(UserSigninView view);
}

public interface UserSigninView {
    public Pane getView();
    
    public String getName();
    public char[] getPassword();
    
    public void addUserSigninViewListener(UserSigninViewListener listener);
    public void removeUserSigninViewListener(UserSigninViewListener listener);
    
    public void userSignupFailed(Exception exp);
    public void userAuthenticationFailed(Exception exp);
}

因此,所有这些都完成了一项简单的工作。它描述了每一层预期提供的意图(或契约)。任何一方都不会真正关心另一部分是如何实施的,只是合同得到维护

例如,可能的控制器实现可能类似于...

public class DefaultUserSigninController implements UserSigninController {
    private UserSigninModel model;
    private UserSigninView view;

    public DefaultUserSigninController(UserSigninModel model, UserSigninView view) {
        this.model = model;
        this.view = view;
    }

    @Override
    public UserSigninModel getModel() {
        return model;
    }

    @Override
    public UserSigninView getView() {
        return view;
    }

    @Override
    public void signupUser(UserSigninView view) {
        getModel().makeUser(view.getName());
    }

    @Override
    public void authenticateUser(UserSigninView view) {
        getModel().authenticateUser(view.getName(), view.getPassword());
    }

    @Override
    public void userCreated(User user) {
        // Coordinate with the navigation controller to move to the
        // next part of the program. This might pass the User object
        // to the navigation controller (as a event) so it
        // can be seeded into the next model
    }

    @Override
    public void userCreationFailed(Exception exp) {
        getView().userSignupFailed(exp);
    }

    @Override
    public void userAuthenticated(User user) {
        // Coordinate with the navigation controller to move to the
        // next part of the program. This might pass the User object
        // to the navigation controller (as a event) so it
        // can be seeded into the next model
    }

    @Override
    public void userAuthenticationFailed(Exception exp) {
        getView().userAuthenticationFailed(exp);
    }
    
}

控制器本身并没有做很多事情,除了协调视图和模型之间的通信。它还提供了向另一个“导航控制器”提供通知的可能性,该控制器可能负责确定接下来应该显示哪个 controller/view。它甚至可能创建包含新 User 对象的“基础”模型。

免责声明

这是一个过于简化的示例,旨在提供一个可证明的工作流程概念 - 重点是,“如何”并不重要,因为它应该全部隐藏在 interface 之后,重要的是是每一层同意执行的契约。

此外,我的 JavaFX 是最小的,因此可以更改事件管理以更好地适应 API 实际实现其可观察模式的方式