Удобная реализация фасада модуля

WMix

герр M:)ller
Партнер клуба
как, говоришь, надо подменять инстанцию $this->dep1 для теста?
 

zuxel

Новичок
PHP:
<?php

class OtherClass1{}

class OtherClass1_Fake{}

DIContainer::set(OtherClass1::getDIKey(), new OtherClass1_Fake());
 

Вурдалак

Продвинутый новичок
Гугли по «service locator antipattern» и «singleton antipattern». Или тебе нужно персональное объяснение?
 

whirlwind

TDD infected, paranoid
От части согласен, но не вижу проблемы если использовать статические методы только для класс-специфичных вещей, а на ограничение до одного инстанса иду сознательно,

если честно, я просто не могу понять реальных проблем которых дает такое решение,
вот, например, есть объект приложения, его тоже надо делать не синглтоном? я сейчас про реальные потенциальные приложения на практике
для тестов можно будет всегда подменить зависимости и инициировать заново
Есть такое понятие, как best practices. Никто не говорит, что твой подход не работает. Просто понимание того, сколько времени понадобится на перепил в большинстве типичных случаев приходит только с количеством сделанных рефакторингов. Ты можешь использовать любую практику и тратить время на перепилы множества высокосвязанных мест, вместо того, что бы наслаждаться пивом. А можешь использовать обкатанные практики и работать не напрягаясь, внося точечные изменения в небольших количествах. Практика элементарная: не связывай код конретными именами классов и всегда оставляй возможность для расширения класса (модификация через наследование и внедрение зависимостей). Не нужно думать, что ты настолько гениален, что можешь сегодня предугадать что будет лучше для этого участка кода через месяц или год. Просто пиши так, что бы в случае необходимости ты мог легко вносить изменения в код. Для этого код должен быть слабосвязанным, расширяемым и повторно используемым. Открываешь википедию, читаешь SOLID и смотришь, как статические вызов влияет на код по каждому пункту.
 

zuxel

Новичок
Я понимаю, но разве мой вариант закрыт для расширения?
И чисто практический вопрос - чем передача в конструктор зависимости лучше, чем получение из контейнера, как у меня?

Мм, одну причину сам вспомнил, есть фреймы которые могут сами определять потребности в объекта и передавать ему ту или иную зависимость, а еще?
 
Последнее редактирование:

whirlwind

TDD infected, paranoid
Я понимаю, но разве мой вариант закрыт для расширения?
Ну ты попробуй унаследуй и переопредели с новой реализацией, а потом нам расскажешь. В твоем последнем примере interface DIPuttable абсолютно лишен смысла. Интерфейс говорит буквально
любой, кто имплементирует этот интерфейс имеет методы _класса_ с такими вот сигнатурами. По этому, вы можете вызывать эти методы, если оперируете с _экземпляром_ производным от DIPuttable.
У тебя нет ни одного экземпляра. Если твой этот код работает, то только изза "особенностей" реализации PHP. Потому что в нормальном языке ты был бы послан по причине неоднозначности между статическим методом и имплементируемым интерфейсом DIPuttable. Твоя реализация метода входит в противоречие с требованиями интерфейса по области действия.

И чисто практический вопрос - чем передача в конструктор зависимости лучше, чем получение из контейнера, как у меня?
У тебя твой класс жестко связан с классом контейнера. В нем прописано имя класса контейнера. Это значит что твой класс зависит от класса контейнера, хотя непосредственно для работы в этом нет никакой необходимости. Тут уже писали про сервис-локатор. Да, это вариант, но не лучший. Но он на порядок удачнее, чем твой первый вариант связка статического метода + впрыск в конструктор.

Мм, одну причину сам вспомнил, есть фреймы которые могут сами определять потребности в объекта и передавать ему ту или иную зависимость, а еще?
А это и есть единственная причина. Инверсия контроля называется. При правильном применении позволяет значительно повысить качество кода по всем параметрам.
 
Последнее редактирование:

WMix

герр M:)ller
Партнер клуба
PHP:
class OtherClass1{}
class OtherClass1_Fake{}DIContainer::set(OtherClass1::getDIKey(), new OtherClass1_Fake());


\DIContainer::get(OtherClass1::getDIKey())->getInstanceFromDI(); 
// Seems class hasnt been initiated yet
 

fixxxer

К.О.
Партнер клуба
Мм, одну причину сам вспомнил, есть фреймы которые могут сами определять потребности в объекта и передавать ему ту или иную зависимость, а еще?
Пожалуйста, не называй фреймворки устаревшим html-тегом. :)

Вместо "есть фреймворки" стоит сказать "все вменяемые фреймворки".
 

zuxel

Новичок
PHP:
class OtherClass1{}
class OtherClass1_Fake{}DIContainer::set(OtherClass1::getDIKey(), new OtherClass1_Fake());


\DIContainer::get(OtherClass1::getDIKey())->getInstanceFromDI();
// Seems class hasnt been initiated yet
естественно, для этого значение которое возвращает функция getDIKey в классе OtherClass1_Fake должно совпадать с OtherClass1

У тебя твой класс жестко связан с классом контейнера. В нем прописано имя класса контейнера. Это значит что твой класс зависит от класса контейнера, хотя непосредственно для работы в этом нет никакой необходимости. Тут уже писали про сервис-локатор. Да, это вариант, но не лучший. Но он на порядок удачнее, чем твой первый вариант связка статического метода + впрыск в конструктор.
да, я сделал ошибку, методы в интерфейсе надо сделать статическими
 

whirlwind

TDD infected, paranoid
да, я сделал ошибку, методы в интерфейсе надо сделать статическими
Нет. Ты пытаешься сделать ошибку используя интерфейс там где это не нужно. Методы интерфейса не могут быть статическими. Интерфейс работает на основе полиморфизма, как и наследование. Статические члены не полиморфны. Статические члены жестко привязаны к классу, то есть к реализации. Полиморфные члены работают только для объектов классов. Объект имеет таблицу методов, которая может быть переписана (перегружена). Класс не имеет такой динамической возможности. Это очевидно. Потому что если ты перегрузишь метод класса, ты сломаешь шаблон класса, то есть получишь сломанный (поврежденный) тип - тип данных, который не соответствует изначально данному описанию класса. Полиморфны только объекты. Выкинь статику и не ломай себе мозг. Эти красоты с точками в именах не стоят того.
 

zuxel

Новичок
Если честно не понял, как набор методов у объекта может быть изменен динамически? Js, python да, но как в php уже созданному объекту добавить метод?
И в любом случае ведь полиморфность проявляется как раз в перегрузке методов класса, а не объекта, разве я не прав?
И не вижу преступления в использовании статических методов, главное всю логику на них строить, я рассматриваю их, как хелперы и переменные класса доступные из глобальной областью видимости и они также могут иметь разные значение для разных классов, но существуют даже если класс не инициирован.
Если вернуться к своему примеру, чего плохого, объявить абстрактный статический метод, который будет реализовываться во всех классах-потомках (и будет разный от класса-к классу) и их объектах, но доступ к которым можно получать непосредственно из класса.
 
Последнее редактирование:

WMix

герр M:)ller
Партнер клуба
смысл в них какой? почемуб функции с префиксами не писать?
 

whirlwind

TDD infected, paranoid
Если честно не понял, как набор методов у объекта может быть изменен динамически? Js, python да, но как в php уже созданному объекту добавить метод?
И в любом случае ведь полиморфность проявляется как раз в перегрузке методов класса, а не объекта, разве я не прав?
Никак не добавить, если язык этого не позволяет. Да это и не нужно. Ты не на том фокусируешься. Ты фокусируешься на расширении, а нужно на использовании.

Полиморфностью обладают объекты, а не классы. Ты можешь подменить один объект другим объектом при условии, что он соблюдает требования интерфейса. Это может быть вообще объект любого класса со своей таблицей методов и иерархией наследования. Главное условие - соответствие требуемому интерфейсу. Что бы в его таблице методов были такие методы, как объявлены в интерфейсе. Теперь представь у тебя есть некий метод или функция с такой сигнатурой:

PHP:
function doSomething(DIPuttable $puttable);
За счет полиморфизма эта функция может работать с любым объектом, который имплементирует интерфейс DIPuttable. Как это возможно? Потому что $puttable имеет таблицу методов, в котором есть записи, соответствующие всем сигнатурам из интерфейса DIPuttable. По этому для функции doSomething важно только одно, что бы эти методы были. А как они работают или какому классу эти методы принадлежат это абсолютно без разницы и на работу функции doSomething не влияет. Это называется полиморфизм. А когда ты пишешь однозначно OtherClass1::что_нибудь - то все, труба полиморфизму. Такой вызов не связан с таблицей методов объекта, он связан с конкретным классом. И уже повтороно за счет полиморфизма ты ничего использовать не сможешь. У тебя будет расти дублирование кода и уменьшаться повторное использование. Больше кода - больше багов. И т.д. и т.п. Высший пилотаж заключается не в том, что бы красивые имена получить типа One::doDomething, Two::doAnother типа сделать понятно к какому классу этот вызов относится. Нет, это полная ерунда. Высший пилотаж это выполнить такую декомпозицию сложной системы на компоненты, что бы повторное использование стремилось к максимуму а зависимости к минимуму. Зачем это нужно? Высокий уровень повторного использования сокращает объем кода. Это значит, что при прочих равных ошибок должно быть меньше. Высокая связанность означает что здесь поменял, а там отвалилось. Низкая связанность означает что потенциальных мест, где что то может отвалиться при изменении, меньше. Все это вместе совокупно улучшает качество кода.
 
Сверху