评论主题对象的面向对象设计

Object-oriented design for comment subject object

我正在尝试找出设计 class 的最佳方法,它封装了 JSON 派生的注释。每个评论都针对一个特定的主题,可以是整个文件,也可以是文件的一行。这是一个示例评论:

{
    "text":"This is my favorite line!",
    "path":"My file.txt",
    "line":42
    ...
}

如果主题是整个文件,line就是null

我希望 Comment class 有一个 subject() 方法,但我不确定设计 CommentSubject class 的最佳方法.这是我目前所拥有的:

import javax.json.JsonObject;

class Comment {
    private final JsonObject json;
    private final CommentSubject subject;

    public JsonObject json() { return json; }
    public CommentSubject subject() { return subject; }

    public Comment(JsonObject json) {
        ...
        this.json = json;
        subject = json.isNull("line") ? new FileSubject(this) :
            new LineSubject(this);
        ...
    }

    ...
}

abstract class CommentSubject {
    enum SubjectType {
        FILE, LINE
    }

    public abstract SubjectType type();
    public abstract String path();
    protected abstract Comment comment();
}

class FileSubject extends CommentSubject {
    private final Comment comment;
    private final String path;

    public FileSubject(Comment comment) {
        this.comment = comment;
        path = comment.json().getString("path");
    }

    public FileSubject(CommentSubject subject) {
        this(subject.comment());
    }

    @Override public SubjectType type() { return SubjectType.FILE; }
    @Override public String path() { return path; }
    @Override protected Comment comment() { return comment; }

    ...
}

class LineSubject extends CommentSubject {
    private final Comment comment;
    private final String path;
    private final int line;

    public LineSubject(Comment comment) {
        this.comment = comment;
        path = comment.json().getString("path");
        line = comment.json().getInt("line");
    }

    public LineSubject(CommentSubject subject) {
        this(subject.comment());
    }

    @Override public SubjectType type() { return SubjectType.LINE; }
    @Override public String path() { return path; }
    @Override protected Comment comment() { return comment; }

    public int line() { return line; }

    ...
}

客户端代码可能如下所示:

doSomething(CommentSubject subject) {
    if (subject.type() == SubjectType.LINE) {
        LineSubject line = new LineSubject(subject);
        ...
    }

    ...
}

但是,我不喜欢这样的事实,即我当前的设计需要客户端代码中的新 LineSubject 对象:subjectline 在上面的示例中是相同的,所以创建新对象似乎是在浪费 space。此外,为了将 CommentSubject 对象传递给另一个 CommentSubject 构造函数,如上面的客户端代码中所示,所有主题都需要由 comment() 方法可访问的注释支持。我也不知道我怎么看 SubjectType enum.

我想要的是 Comment 有一个 subject() 方法并且能够区分文件和行主题。还有更好的设计吗?

如果文件注释和行注释之间的唯一区别是文件注释没有行号,您可以将 class 层次结构折叠为单个 class,并使行号可选(即 returning Integer 而不是 int)。这将使客户端程序区分文件和行注释,因为文件注释将 return null 用于行号:

public class CommentSubject {

    private final Integer line;
    private final String path;
    private final String comment;

    public String path() { return path; }
    public Integer line() { return line; }
    public Comment comment() { return comment; }

    public static CommentSubject forFile(String p, String c) {
        return new CommentSubject(p, null, c);
    }
    public static CommentSubject forLine(String p, int i, String c) {
        return new CommentSubject(p, i, c);
    }

    private CommentSubject(String p, Integer i, String c) {
        path = p;
        line = i;
        comment = c;
    }
}

客户可以这样写:

doSomething(CommentSubject subject) {
    Integer optLine = subject.line();
    if (optLine != null) {
        int line = optLine.intValue();
        ...
    }
    ...
}

如果您希望避免客户端中的条件调度,您可以采用类似访问者的方法,并让 CommentSubject 回调您评论的处理者,如下所示:

interface CommentProcessor {
    void onFileComment(String path, String comment);
    void onLineComment(String path, int line, String comment);
}
public class CommentSubject {

    private final Integer line;
    private final String path;
    private final String comment;

    public void process(CommentProcessor p) {
        if (line != null) {
            p.onLineComment(path, line.intValue(), comment);
        } else {
            p.onFileComment(path, comment);
        }
    }

    public static CommentSubject forFile(String p, String c) {
        return new CommentSubject(p, null, c);
    }
    public static CommentSubject forLine(String p, int i, String c) {
        return new CommentSubject(p, i, c);
    }

    private CommentSubject(String p, Integer i, String c) {
        path = p;
        line = i;
        comment = c;
    }
}

注意注释、路径和行是如何隐藏在 CommentSubject 中的。现在访问它们的唯一方法是传递 CommentProcessor 的实例,该实例将收到 CommentSubject.

的适当类型的回调