【问题标题】:Java Objects and internal valuesJava 对象和内部值
【发布时间】:2014-01-03 12:49:06
【问题描述】:

我正在深入研究面向对象的世界,并尝试应用 Clean Code 一书的作者认为应该如何构建丰富对象的原则。

但是,我现在遇到了一个有点哲学的问题,我不太确定如何处理对象的表示。

在 Clean Code 一书中,您可以读到丰富的对象应该只公开行为,而不应该公开其内部变量。该声明还有其他来源。

但是,如果您只公开行为,那么如果您无法获得内部变量的值,您如何在 GUI 中显示对象的表示?

例子:

public class Todo {

    private final TodoID id;
    private final String createdBy;
    private final String currentOwner;
    private final String description;
    private final Status status;

    private Todo(String createdBy, DateTime creationDate, String description, Status status, String todoName) {
        this.id = new TodoID(creationDate, todoName);
        this.createdBy = createdBy;
                this.currentOwner = createdBy;
        this.description = description;
        this.status = status;

    }

    public void startWorking(String name){
        if(Status.IN_PROGRESS > this.status){
        // Set status to IN_PROGRESS
        // Set current owner to name    
        } else {
          throw new RuntimeException(this.currentOwner + " has already started working on this task or it might be finished already.");
    }

    public static TodoID create(TodoValues values) {
        Todo todo = new Todo(values.createdBy, 
                             values.creationDate, 
                             values.description, 
                             values.status, 
                             values.todoName);
        // Operations to save the Todo-instance in a database
        return todo.id;
    }

}

我的 Todo-aggregate root 上可能有一个方法,它返回所有新 Todo 的列表 public List<Todo> listAllNewTodos(){}。 列表中的所有 Todo 都有代表对象的 id。但是如果只允许一个对象暴露行为,我该如何表示 id 呢?

编辑:在发布这个问题后,我得到了一个启示。 Clean Code 一书还谈到了 OOP 中还有另一种类型的结构。不仅有行为的对象,还有数据结构。 在这种情况下,我可能应该将 Todo 类/实例视为对象,将 TodoID 实例视为公开其内部变量的数据结构。多亏了这一点,我可以在 GUI 中使用 TodoID 变量来表示 Todo。

【问题讨论】:

    标签: java oop domain-driven-design aggregate behavior


    【解决方案1】:

    你做得对,你应该使用 Getters 和 Setters 来访问内部变量

    http://docs.oracle.com/javaee/6/tutorial/doc/gjbbp.html

    【讨论】:

    • 在这种情况下,我不需要 getter 和 setter,因为我不想暴露 Todo 类的内部变量。但是您是对的,要遵守 Bean 定义,应该有 getter 和 setter。 (但这也是我在实验中试图质疑的问题。一个只公开行为和不可变数据结构的不可变富对象并不真正需要 getter。直接访问公共 final 变量更直接,而不是去通过一个为你做同样事情的方法)。
    【解决方案2】:

    您可能还想查看属性更改事件。使用这种机制,您的 UI 不必读取值,只要它们发生变化,它们就会由对象发送到 UI。

    【讨论】:

    • 你绝对是正确的。这是一种方法。我在问题中忘记提到的是,该示例应该位于 UI 下方,仅包含逻辑。但是这一层也应该产生诸如属性更改等事件。
    【解决方案3】:

    并非所有类变量都可能是“内部变量”。如果你的类中的某些行为使用它但你的类的消费者不需要它,你可以考虑一个内部变量。

    在您的示例中,假设您需要保留每个待办事项的总时间,并且用户可以多次启动任务。然后,您需要声明一个内部变量:

    private Date latestStartTime;
    

    显然,这个变量应该由 startWorking 和 pause 方法(我没有包括在内)使用,但不会提供给调用方法。对于其他字段,您可以按照@Gynnad 的建议提供 getter 方法(以及当您的字段不是最终字段时的 setter 方法)。您可以在 UI 中使用它们。

    可能还有其他(更好的)解决方案,例如您在编辑中提到的解决方案。请记住,这些都是创建更好代码的启发式方法,而不是硬性规则。

    【讨论】:

    • 您提出的观点非常好、有效且有趣。这是我必须考虑的事情,但是很容易对变量进行这种区分,即只有消费者不需要的变量才会被视为内部变量。我知道创建更好的代码而不是硬性规则都是指导方针,但是通过将它们当作硬性规则行事,这让我真正考虑了每一个决定。我想打破的每一条规则都会让我质疑规则并为它找到充分的理由。如果我找不到制定规则的充分理由或违反规则的充分理由,那么我就不会这样做。
    • 您可能还想在 C2 wiki 上阅读此讨论:C2Wiki Accessors Are Evil
    【解决方案4】:

    你看过CQRS吗?

    您可以在屏幕上拆分用户可以执行的任务和他需要的信息:

    当用户想做某事时,你不需要有内部状态:

    public class StartWorkingOnTodoCommandHandler : CommandHandler<StartWorkingOnTodoCommand>
    {
        public void handle(StartWorkingOnTodoCommand command)
        {
            Todo todo = session.get<Todo>(command.getId());
            User user = session.get<User>(command.getUserId());
            todo.startWorking(user);
        }
    }
    

    当你想查询信息时,你可以创建一个单独的层:

    public class UnfinishedTodo
    {
        public String getTitle()
        {
        }
    
        public TodoId getId()
        {
        }
    
        public String getCreatedBy()
        {
        }
    }
    

    (我的 Java 有点生锈了)

    这里有可能(我不知道是否需要)在你的 Todo 中有一个用户域对象,但仍然有一个返回 UI 的字符串。

    【讨论】:

      【解决方案5】:

      在 Clean Code 一书中,您可以读到富对象应该只 暴露行为而不是其内部变量。该声明有 其他来源也是如此。

      但是如果你只暴露行为,那么你如何展示一个 GUI 中对象的表示,如果您无法达到这些值 内部变量?

      这是因为 Clean code 一书中的丰富对象不是为在 GUI 中显示而设计的,而是为保护不变量而设计的。在你的情况下,业务规则不是很复杂,TODO 类也负责查询需求,所以现在有一些 getter 方法就可以了。

      【讨论】:

        猜你喜欢
        • 2012-02-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-10-05
        • 2019-11-10
        • 2017-12-01
        • 2020-10-06
        • 1970-01-01
        相关资源
        最近更新 更多