«Статичный» как смысловой ключ к безгражданству?

11

Недавно я провел рефакторинг проекта среднего размера в Java, чтобы вернуться и добавить модульные тесты. Когда я понял, что такое издеваться над статуэтками и статикой, мне было больно, я наконец-то «получил» то, что читал о них все это время. (Я один из тех людей, которым нужно учиться на собственном опыте. Хорошо.)

Итак, теперь, когда я использую Spring для создания объектов и связывания их, я избавляюсь от staticключевых слов влево и вправо. (Если бы я мог потенциально хотеть высмеивать это, это не совсем статично в том же смысле, что и Math.abs (), верно?) Дело в том, что я привык использовать staticдля обозначения того, что метод не полагается на любом объекте состояния. Например:

//Before
import com.thirdparty.ThirdPartyLibrary.Thingy;
public class ThirdPartyLibraryWrapper {
    public static Thingy newThingy(InputType input) {
         new Thingy.Builder().withInput(input).alwaysFrobnicate().build();
    }
}

//called as...
ThirdPartyLibraryWrapper.newThingy(input);
//After
public class ThirdPartyFactory {
    public Thingy newThingy(InputType input) {
         new Thingy.Builder().withInput(input).alwaysFrobnicate().build();
    }
}

//called as...
thirdPartyFactoryInstance.newThingy(input);

Итак, вот где это становится раздражительным. Мне понравился старый способ, потому что заглавная буква говорила мне, что, как и Math.sin (x), ThirdPartyLibraryWrapper.newThingy (x) каждый раз делал одно и то же. Там нет состояния объекта, чтобы изменить, как объект делает то, что я прошу его сделать. Вот несколько возможных ответов, которые я рассматриваю.

  • Никто другой не чувствует так, так что со мной что-то не так. Может быть, я просто не усвоил ОО-способ ведения дел! Может быть, я пишу на Java, но думаю на FORTRAN или что-то в этом роде. (Это было бы впечатляюще, так как я никогда не писал на Фортране.)
  • Может быть, я использую статичность как своего рода прокси для неизменности в целях рассуждения о коде. При этом, какие подсказки я должен иметь в своем коде, чтобы кто-то пришел, чтобы поддержать его, чтобы знать, что является состоянием, а что нет?
  • Возможно, это должно прийти бесплатно, если я выберу хорошие метафоры объекта? Например thingyWrapper, не похоже, что у него есть независимый от обернутого состояния состояние, Thingyкоторое само может быть изменяемым. Точно так же thingyFactoryзвуки должны быть неизменными, но могут иметь разные стратегии, которые выбираются при создании.
leoger
источник

Ответы:

12

Мне понравился старый способ, потому что заглавная буква говорила мне, что, как и Math.sin (x), ThirdPartyLibraryWrapper.newThingy (x) каждый раз делал одно и то же. Там нет состояния объекта, чтобы изменить, как объект делает то, что я прошу его сделать.

Это правильный способ сделать это, да. Тем не менее, не staticдает никаких гарантий неизменности или устойчивости государства. То, что вы используете это как предложение для таких гарантий, имеет смысл, но это ни в коем случае не гарантировано.

Учтите следующее:

public static class Logger
{
    public static int LogLevel { get; set; }

    public static void Log(string message, int logLevel)
    {
        if (logLevel >= LogLevel)
        {
            // logs the message, but only if it is important enough.
        }
    }
}

Этот класс не только содержит состояние, но и поведение Logger.Log()метода изменяется при изменении этого состояния. Это совершенно законное упражнение staticшаблона класса, но оно не имеет никаких семантических гарантий, которые вы предлагаете.

Роберт Харви
источник
Я поставил вам галочку, потому что вы представляете полезный контрапункт на основе моей интуиции, но я все же хотел бы узнать ваши мысли о том, как я должен представлять этот контракт / ожидание о отсутствии состояния и отсутствии побочных эффектов посредством кодирования. конвенций.
Леогер
1
Обычно такие ожидания включены в документацию по методу; То же самое относится и к безопасности потоков.
Роберт Харви
5

По моему мнению, то, что вы говорите - все статические функции должны быть нулевыми - в большинстве случаев является хорошим ОО-способом написания кода, но я не верю, что когда вы смотрите на статическую функцию, вы должны автоматически предполагать, что она не имеет побочных эффектов , Ситуация может быть более сложной; Возьмем, к примеру, функцию, Class.forName(String)которая выглядит как не имеющая состояния, но фактически загружает класс в память и в конечном итоге создает статические поля / запускает статический инициализатор; это пример идемпотентной функции (возможные вызовы после первого не имеют значения), но это не чистая функция (нельзя сказать, что она не имеет побочных эффектов). Это также хороший выбор, но могут быть случаи, когда три разных вызова одной и той же функции дают три разных результата; например, если вы звонитеThread.currentThread() в многопоточном приложении нет гарантии, что вы будете получать один и тот же поток каждый раз.

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

Правильное решение - документировать (например, Javadoc); также из названия функции и из того, что она делает, иногда можно сделать вывод, что статическая функция является чистой функцией. Например, у кого-то нет оснований полагать, что Assert.assertTrue(boolean)из-за JUnit что-то изменится. С другой стороны, когда System.clearProperty(String)вызывается функция like , довольно очевидно, что будут побочные эффекты.

m3th0dman
источник
В чем разница между "нулипотентным" и "лицом без гражданства"?
Пейсер
@Pacerier Там не должно быть никакой разницы.
m3th0dman
4

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

Вы можете сделать их статичными. FxCop в мире C # подтолкнет вас к тому, чтобы сделать вещи статичными, которые не ссылаются на переменные-члены. Это правильная вещь (обычно).

Когда я понял, что такое издеваться над статуэтками и статикой, мне было больно, я наконец-то «получил» то, что читал о них все это время.

Перемещение всех их в инстансы, возможно, не правильный подход. Вместо этого отделите зависимости от статических методов от классов, которые их используют. Вы можете протестировать статические методы изолированно, а затем заменить зависимость у потребителей, когда они должны быть проверены.

Telastyn
источник
Можете ли вы нарисовать пример "развязки зависимостей от статических методов"? Мне не ясно, что вы активируете. Я уже скрыл детали реализации в статическом методе. В конце концов, класс, который его использует, должен достичь этой функциональности. Вы говорите, что я должен создать экземпляр объекта, который имеет тонкие оболочки для всех статических методов?
Leoger
1
@leoger - класс, который использует статический метод, не должен достигать функциональности, если вы тестируете этот класс, иначе вы не будете насмехаться над статическим методом.
Теластин