Когда лучше применять статические свойства, методы, классы? А когда их использовать не стоит?

AnrDaemon

Продвинутый новичок
Если вы не знаете, зачем вы что-то делаете, этого делать не стоит.
Точно так же не стоит пытаться устраивать нам тут экзамены.
 

Вурдалак

Продвинутый новичок
Пробежался по статье, автор похоже делает подмену понятий: описываемые недостатки присущи static-переменным, а не static-методам как таковым. Статический по своей сути метод ничего плохого в себе не несёт: я их регулярно использую, например, в качестве named constructors. Иногда использую в качестве обычной функции. Проблемы начинаются, если есть static-переменная (как в singleton, например).
Если посмотреть внимательно, во всех примерах кода есть некая статическая переменная типа self::$db. Автор этого в упор не видит и делает вывод, что static method — это bad practice.

Про «нарушение инкапсуляции» вот этой функции из-за того, что туда можно передать только int:
PHP:
public showAbs($num) { 
      return $num . "'s absolute value is " . abs($num); 
    }
— это вообще какой-то бред.
 

AmdY

Пью пиво
Команда форума
Блин, я в реальных проектах наелся гавна со статик конструктором как раз для времени, когда часовые пояса поломали. Это жёсткая зависимость и пропатчить без форков и переписывания вендорского кода было невозможно. Понятно откуда берутся тупики и проблемы с поддержкой.
 

Вурдалак

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

А если у тебя там какой-нибудь `new DateTime('now')` или другой какой синглтон вызывается — то мы опять приходим к статическим переменным. Сам метод-то тут причём?
 

AmdY

Пью пиво
Команда форума
Ну так не надо хардкодить зависимости ни синглетонами, ни статик методами, ни через new.
 

Adelf

Administrator
Команда форума
@AmdY, это касается всяких сервисных классов. А например доменные обьекты или такие вот системные как DateTime - как ты избавишься от зависимости на них? У тебя будет фабрика дат?
 

Вурдалак

Продвинутый новичок
Ну так не надо хардкодить зависимости ни синглетонами, ни статик методами, ни через new.
Мне кажется, что ты просто пришёл потроллить, либо ты не совсем понимаешь о чём идёт речь и решил просто слиться.

Ещё раз: static method без static-переменных, когда мы используем её как named constructor — это обычный синтаксический сахар, его не нужно ни мокать, ни переписывать «вендорский код». Его нужно просто вызвать.

Блин, я в реальных проектах наелся гавна со статик конструктором как раз для времени, когда часовые пояса поломали. Это жёсткая зависимость и пропатчить без форков и переписывания вендорского кода было невозможно. Понятно откуда берутся тупики и проблемы с поддержкой.
Ты рассказываешь какие-то сказки.
 

Вурдалак

Продвинутый новичок
А например доменные обьекты или такие вот системные как DateTime - как ты избавишься от зависимости на них? У тебя будет фабрика дат?
От какой такой зависимости? Ты имеешь в виду как избавиться от DateTime::getInstance()->getSystemTime() (чем по сути и является new DateTime('now'))? Просто передавать из уровня выше, либо использовать какой-то сервис а-ля Clock.
 

AmdY

Пью пиво
Команда форума
От какой такой зависимости? Ты имеешь в виду как избавиться от DateTime::getInstance()->getSystemTime() (чем по сути и является new DateTime('now'))? Просто передавать из уровня выше, либо использовать какой-то сервис а-ля Clock.
О, сервис заворачиваем в сервис в котором будет делаться статический вызов, а затем молимся чтобы никто не сделал прямой вызов статического метода в обход этого сервиса.
 

Вурдалак

Продвинутый новичок
О, сервис заворачиваем в сервис в котором будет делаться статический вызов, а затем молимся чтобы никто не сделал прямой вызов статического метода в обход этого сервиса.
Ты опять фантазируешь, я ничего такого не говорил.
PHP:
final class User
{
    public static function register(int $id, string $name, DateTimeImmutable $registrationDate): User
    {
        return new self($id, $name, $registrationDate);
    }

    // ...
}
— расскажи мне как ты будешь это мокать, менять «вендорский код» и главное зачем.
 
Последнее редактирование:

fixxxer

К.О.
Партнер клуба
О, сервис заворачиваем в сервис в котором будет делаться статический вызов, а затем молимся чтобы никто не сделал прямой вызов статического метода в обход этого сервиса.
Это ничем не отличается от прямого инстанциирования сервиса с прямым же конструированием зависимостей в обход DI. Дурак всегда найдет свой стеклянный буй, код-ревью для того и существует.

Впрочем, в сервисах named constructor-ы нафиг не нужны. А во всяких там entities и value objects - какая разница, new Foo($arg) или Foo::new($arg)? Никакой разницы, окромя чисто синтаксической.

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

fixxxer

К.О.
Партнер клуба
описываемые недостатки присущи static-переменным, а не static-методам как таковым
Я бы даже, наверное, обобщил: проблемы появляются тогда, когда у класса (а не у его инстанса) появляется понятие состояния и он сам со себе становится мутабелен.

В php такое без статических переменных придумать не так просто (кроме откровенных глупостей типа глобалов), а в каком-нибудь JS или Ruby - запросто.
 

Вурдалак

Продвинутый новичок
Впрочем, в сервисах named constructor-ы нафиг не нужны.
Не нужны скорее по той причине, что они описываются в DI-конфиге, там какой-то экпрессивности особо и не нужно. Но навскидку: у нас есть сервис для работы с внешним API, плюс два способа аутентификации: сертификат или login/password.
PHP:
final MyAwesomeExternalApiService
{
    public static function loginPassword(
        string $login,
        string $password,
        Guzzle $guzzle
    ): MyAwesomeExternalApiService {
        // ...
    }

    public static function cert(string $certPath, Guzzle $guzzle): MyAwesomeExternalApiService {
        // ...
    }

    public function query(Query $query): Response {
        // ...
    }
}
Учитывая, что у нас тут взаимоисключающие параметры, это бы привело к полотну nullable-параметров, нужно ещё понять в каком сочетании они имеют смысл и т.д.
 

Вурдалак

Продвинутый новичок
Ага. А в твоем примере, думаю, скорее эти все методы будут в каком-нибудь MyAwesomeExternalApiServiceFactory на практике.
Честно говоря, не думаю, что на практике у меня было бы так, поскольку это обозначает, что мы должны передать в конструктор уже какой-то sessionId. Тут либо мы теряем lazy loading, либо нужно будет делать дальнейшие усложнения для того, чтобы вернуть lazy loading (инжектить саму фабрику, париться с магией а-ля ProxyManager или ещё как-то).

Т.е. я что-то не вижу особых плюсов у отдельной фабрики. Но ситуации бывают разные.
 

Вурдалак

Продвинутый новичок
Я бы даже, наверное, обобщил: проблемы появляются тогда, когда у класса (а не у его инстанса) появляется понятие состояния и он сам со себе становится мутабелен.
Ну, что уж там, для инстанса мутабельность тоже ни к чему хорошему не приводит :) Правда тут под «состоянием» скорее уже стоит понимать не столько внутренние зависимости, сколько зависимость поведения от предыдущих вызовов.
 

fixxxer

К.О.
Партнер клуба
Ну, в твоем примере я бы, наверное, вообще передавал отдельно реализацию MyAwesomeExternalApiAuthScheme, они как раз могут конструироваться без зависимостей, а ему уж там в метод authenticate инстанс Guzzle передаст MyAwesomeExternalApiService. А фабрику чтобы это все ручками не собирать.

Хотя хз, действительно, ситуации могут быть разные.
 

Вурдалак

Продвинутый новичок
Ну, в твоем примере я бы, наверное, вообще передавал отдельно реализацию MyAwesomeExternalApiAuthScheme, они как раз могут конструироваться без зависимостей, а ему уж там в метод authenticate инстанс Guzzle передаст MyAwesomeExternalApiService. А фабрику чтобы это все ручками не собирать.
Иногда просто лениво заниматься подобной декомпозицией, от неё должен быть какой-то толк. А если разницы нет, то я не хочу платить больше.
 
Сверху