Применяются ли инварианты к объектам без состояния?

Я читаю об инвариантах и ​​не уверен, что полностью это понял. Из Википедии,

Инвариант класса ограничивает состояние, хранимое в объекте.

Так что, если я правильно понял, инварианты не применяются к объектам без состояния, потому что нет никаких ограничений. Это верно? Я ошибся? Я сравниваю яблоки с апельсинами?

3 ответа

У объекта без состояния нет состояния. Возьмите пример, скажем, служебного класса, который не имеет никакого поля или имеет постоянные поля, как показано ниже:

public class MyClass {
    public static final int number = 1;   
    private final int count = 1;
}

Это означает, что число / количество не изменится и останется неизменным независимо от того, будет ли указано число потоков n.

Хотя, если я возьму класс с скажем приватное поле и, скажем, получаю доступ через setter / getter, то это будет выглядеть так:

public class MyInvariantClass {
    private int number;

    public int getNumber() {
        return number;
    }
    public void incrNumber() {
        this.number++;
    }
    public void setNumber() {
        this.number = number;
    }
}

Этот класс MyInvariantClass имеет состояние с числовым полем. Подумайте об этом объекте, передаваемом нескольким потокам. Некоторые могут увеличивать его, некоторые могут устанавливать. Гарантирует ли это, что число, которое мы используем, никогда не выходит за пределы диапазона int и становится по умолчанию отрицательным? Поэтому здесь мы не можем поддерживать инвариант целого числа, когда происходит переполнение.

Инвариант будет что-то вроде:

myBankAccount.Balance >= 0 между вызовами к любому из объектов (mybankAccount) методы. Balance переменная является членом myBankAccount объект.

Если у нас есть объект без состояния, то есть объект без каких-либо элементов данных, связанных с ним, то мы не можем реально определить инвариант (на чем мы будем его определять?).

Класс без состояния не имеет членов экземпляра, и, следовательно, нет состояния, чтобы поддерживать его в силе.

Из википедии:

В информатике инвариант - это условие, на которое можно положиться во время выполнения программы или в течение некоторой ее части.

В Java эти условия (инварианты) могут быть запрограммированы в вашем коде несколькими способами. Вот некоторые:

  1. используя последнее ключевое слово
  2. используя утверждения
  3. бросать исключения
  4. используя методы мутатора

Например, возьмем класс Age:

public class Age {
    private final int age;

    public Age(int age) {
        // Use exceptions...
        if (age < 1) {
            throw new IllegalArgumentException("Age cannot be less than 1");
        }

        // Or use assertions...
        assert age > 0;
        this.age = age;
    }

    // A method can be a invariant as well, because it makes sure
    // member variables cannot be set to an invalid state.
    public Age changeAge(int age) {
        assert age > 1;
        return new Age(age);
    }
}

Итак, последнее ключевое слово является инвариантом времени компиляции, потому что оно гарантирует, что переменная не переназначена. Исключение и утверждение в конструкторе являются инвариантами времени выполнения, потому что они ограничивают возраст и гарантируют, что возраст больше 0. Открытый метод также является инвариантом, потому что он утверждает, что его аргумент недопустим.

Некоторые языки имеют встроенную концепцию инвариантов. На странице википедии, на которую вы ссылаетесь, они перечисляют D и Eiffel в качестве примеров. Это означает, что вместо возбуждения исключений, утверждения и проверки условий вы можете определить ограничение (инвариант) в одном месте, и у языка есть поддержка, чтобы убедиться, что переменные экземпляра остаются в пределах установленных ограничений. В Java вы должны сделать это "вручную".

Другие вопросы по тегам