在抽象类中定义具体类是否合适?

时间:2015-06-28 19:40:13

标签: java class oop inheritance

我正在阅读下面这样的java代码,: 它在抽象类ClientEntry中定义了具体的类ConfirgurationEntryLogEntry

这是一个好习惯吗? 通常我会在不同的文件中定义它们。

public abstract class LogEntry {

    /**
     * The four allowed {@link LogEntry} types.
     */
    public static enum Type {

        /**
         * {@link LogEntry} with {@code index} = 0, {@code term} = 0.
         */
        SENTINEL,

        /**
         * {@link LogEntry} that stores a noop entry.
         */
        NOOP,

        /**
         * {@link LogEntry} that stores cluster membership information about the Raft cluster.
         */
        CONFIGURATION,

        /**
         * {@link LogEntry} that stores a {@link Command} submitted by a client.
         */
        CLIENT,
    }

    /**
     * Singleton instance of the {@link LogEntry.Type#SENTINEL} log entry.
     */
    public static final LogEntry SENTINEL = new LogEntry(Type.SENTINEL, 0, 0) {

        @Override
        public String toString() {
            return Objects
                    .toStringHelper(this)
                    .add("type", "SENTINEL")
                    .add("index", getIndex())
                    .add("term", getTerm())
                    .toString();
        }
    };

    //----------------------------------------------------------------------------------------------------------------//
    //
    // Base class
    //

    private final Type type;
    private final long index;
    private final long term;

    // keeping this constructor private restricts
    // the number of allowed LogEntry types to those defined in this
    // compilation unit
    // i.e. using 'protected' instead would allow anyone to define additional
    // LogEntry types, which I don't support
    private LogEntry(Type type, long index, long term) {
        checkArgument(index >= 0, "index must be positive:%s", index);
        checkArgument(term >= 0, "term must be positive:%s", term);
        if (index == 0 && term == 0) {
            checkArgument(type == Type.SENTINEL);
        }
        this.type = type;
        this.index = index;
        this.term = term;
    }

    /**
     * Get the type of this log entry.
     *
     * @return {@link LogEntry.Type} of this log entry
     */
    public final Type getType() {
        return type;
    }

    /**
     * Get the log entry's position in the 0-indexed Raft log.
     *
     * @return index >= 0 of this log entry's position in the Raft log
     */
    public long getIndex() {
        return index;
    }

    /**
     * Get the election term in which this log entry was created.
     *
     * @return election term >= 0 in which this log entry was created
     */
    public final long getTerm() {
        return term;
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(type, index, term);
    }

    @Override
    public boolean equals(@Nullable Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        LogEntry other = (LogEntry) o;
        return type == other.type && index == other.index && term == other.term;
    }

    //----------------------------------------------------------------------------------------------------------------//
    //
    // Subclasses
    //

    /**
     * {@code LogEntry} that contains a client {@link Command}.
     * <p/>
     * Once this entry is committed
     * the client is notified via {@link io.libraft.RaftListener#applyCommitted(Committed)}
     * that this {@code Command} can be applied locally.
     */
    public static final class ClientEntry extends LogEntry {

        private final Command command;

        /**
         * Constructor.
         *
         * @param index index > 0 of this log entry's position in the log
         * @param term election term > 0 in which this log entry was created
         * @param command instance of {@link Command} to be replicated
         */
        public ClientEntry(long index, long term, Command command) {
            super(Type.CLIENT, index, term);
            this.command = command;
        }

        /**
         * Get the {@link Command} to be replicated.
         *
         * @return instance of {@code Command} to be replicated
         */
        public Command getCommand() {
            return command;
        }

        @Override
        public boolean equals(@Nullable Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            if (!super.equals(o)) return false;

            ClientEntry other = (ClientEntry) o;
            return getType() == other.getType()
                    && getIndex() == other.getIndex()
                    && getTerm() == other.getTerm()
                    && command.equals(other.command);
        }

        @Override
        public int hashCode() {
            return Objects.hashCode(getType(), getIndex(), getTerm(), command);
        }

        @Override
        public String toString() {
            return Objects
                    .toStringHelper(this)
                    .add("type", getType())
                    .add("index", getIndex())
                    .add("term", getTerm())
                    .add("command", command)
                    .toString();
        }
    }

    // FIXME (AG): the design of this log entry is incorrect and has to be reworked
    /**
     * {@code LogEntry} that contains the
     * configuration state of the Raft cluster.
     */
    public static final class ConfigurationEntry extends LogEntry {

        private final Set<String> oldConfiguration;
        private final Set<String> newConfiguration;

        /**
         * Constructor.
         *
         * @param index index > 0 of this log entry's position in the log
         * @param term election term > 0 in which this log entry was created
         */
        public ConfigurationEntry(long index, long term, Set<String> oldConfiguration, Set<String> newConfiguration) {
            super(Type.CONFIGURATION, index, term);
            this.oldConfiguration = oldConfiguration;
            this.newConfiguration = newConfiguration;
        }

        public Set<String> getOldConfiguration() {
            return oldConfiguration;
        }

        public Set<String> getNewConfiguration() {
            return newConfiguration;
        }

        @Override
        public boolean equals(@Nullable Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            if (!super.equals(o)) return false;

            ConfigurationEntry other = (ConfigurationEntry) o;

            return getType() == other.getType()
                    && getIndex() == other.getIndex()
                    && getTerm() == other.getTerm()
                    && oldConfiguration.equals(other.oldConfiguration)
                    && newConfiguration.equals(other.newConfiguration);
        }

        @Override
        public int hashCode() {
            return Objects.hashCode(getType(), getIndex(), getTerm(), oldConfiguration, newConfiguration);
        }

        @Override
        public String toString() {
            return Objects
                    .toStringHelper(this)
                    .add("type", getType())
                    .add("index", getIndex())
                    .add("term", getTerm())
                    .add("oldConfiguration", oldConfiguration)
                    .add("newConfiguration", newConfiguration)
                    .toString();
        }
    }

    /**
     * noop {@code LogEntry}.
     * <p/>
     * {@code NoopEntry} is for {@link RaftAlgorithm}
     * internal use only. The client <strong>will not</strong>
     * be notified when a {@code NoopEntry} instance
     * is committed.
     */
    public static final class NoopEntry extends LogEntry {

        /**
         * Constructor.
         *
         * @param index index > 0 of this log entry's position in the log
         * @param term election term > 0 in which this log entry was created
         */
        public NoopEntry(long index, long term) {
            super(Type.NOOP, index, term);
        }
    }

    @Override
    public String toString() {
        return Objects.toStringHelper(this)
                .add("type", getType())
                .add("index", getIndex())
                .add("term", getTerm())
                .toString();
    }
}

3 个答案:

答案 0 :(得分:4)

我说实话。如果我在工作中这样做,我的老板可能会给我的机器喷一个喷灯。但这仅仅是因为我工作场所的编码惯例。

引用着名的Jon Skeet

  

我对这种技术的建议名称(包括单个源文件中的多个顶级类)将是&#34; mess&#34;。说真的,我认为这不是一个好主意 - 我在这种情况下使用嵌套类型。然后,它仍然很容易预测它所处的源文件。我不相信这是这种方法的官方术语。

传统观点是每个文件都要坚持一个类,特别是如果您要添加到遵循此模式的现有代码库中。它不是禁止编译器,但它们都不是:

int x = 4;
int y = (4 * x * 6 * x * 10 * x * 56 * x);
String s = "s" + "t" + "r" + "i" + "n" + "g";

答案 1 :(得分:1)

我不推荐这种做法,当它们位于不同的文件中时,对多个类进行排序要容易得多。有些人在一个文件中声明多个类,因为他们喜欢代码的可访问性。如果你想这样做,我建议你不要,只有一个声明的类可以是公共的(公共类也应该包含main方法)。如果你要编译上面的代码,你将不得不删除&#39; public&#39;除了一个班级以外的所有人当编译包含多个类的代码时,它会创建多个.class文件(一个用于源代码中声明的类)。因此,即使源代码都在一个.java文件中,您仍然会得到多个类文件。

答案 2 :(得分:1)

在抽象类中使用具体类没有任何问题。事实上,在某种情况下,这种方法最好。假设您希望抽象类的所有子类都能访问您在此抽象类中定义的受保护类。

定义静态类很少见。它主要是避免的。但我们可以在java的包中看到一些示例,其中包含静态类。但是除了heirarchy之外,在另一个文件中使用另一个类没有什么不同。