Skip to content

Instantly share code, notes, and snippets.

@getjump
Last active January 28, 2022 04:14
Show Gist options
  • Save getjump/25fd919f4fd45f8346ceb036b9ba9bac to your computer and use it in GitHub Desktop.
Save getjump/25fd919f4fd45f8346ceb036b9ba9bac to your computer and use it in GitHub Desktop.
<?php
class UserOrder
{
public $id;
/** @var string */
public $contactName;
/** @var string */
public $phone;
/** @var string ISO 3166-1 alpha-2 */
public $country;
/** @var string */
public $state;
/** @var string */
public $city;
/** @var string */
public $street;
/** @var string */
public $house;
/** @var string */
public $apartment;
/** @var array */
public $orderItems;
}
class UserAddress
{
public $id;
/** @var string */
public $name;
/** @var string */
public $phoneNumber;
/** @var string ISO 3166-1 alpha-2 */
public $countryCode;
/** @var string */
public $state;
/** @var string */
public $city;
/** @var string */
public $street;
/** @var string */
public $house;
/** @var string */
public $apartmentNumber;
}
class PostPackage
{
public $id;
/** @var string */
public $contactName;
/** @var string */
public $phoneNumber;
/** @var string ISO 3166-1 alpha-2 */
public $countryName;
/** @var string */
public $state;
/** @var string */
public $city;
/** @var string */
public $address;
/** @var array */
public $packageItems;
}
<?php
/**
Есть несколько вариантов решения проблемы поставленной в ТЗ:
1. Мы можем для каждого вида объектов содержащих информацию о адресе реализовать так называемый адаптер, что полезно,
когда мы не можем или не хотим изменять класс
2. Мы можем реализовать интерфейс в классах с адресами, но тогда мы нарушаем принцип Single Responsibility (т.е. дата класс
занимается дополнительно форматированием данных в необходимый формат)
Оптимальным вариантов в данном случае вижу решение с адаптером, в данном случае - это скорее Presenter или Transformer, если
нам необходим соотвествующий уровень абстракции мы можем разбить наш вспомогательный класс дальше
Данным способом мы легко можем удовлетворить условие разнородности данных, а добавление нового класа с адресом заключается в
добавлении объекта и создании соотвествующего трансформера.
Хорошим тоном так же будет являться использование гетеров для получения свойств объекта, но это не обязательно.
Таким образом, добавлением одного слоя, мы сняли отвественность с дата класса и не наложили дополнительную отвественность на класс
PostLabelProcessor.
Вполне возможно местами появится дублирующийся код для форматов - можно реализовать абстрактные классы в этом случае,
либо вынести в helper трейт. Если появится что-то сложнее можно разбить логику ещё больше.
Также, вполне вероятно PostLabelProcessor может агрегировать данные из нескольких мест по приоритету, в этом случае мы
можем реализовать агрегирующий трансформер, не меняя ни один из имеющихся классов.
*/
interface AddressTransformerInterface {
public function getContactName(): string;
...
}
class UserOrderAddressTransformer implements AddressTransformerInterface {
private $userOrder;
public function __construct(UserOrder $userOrder) {
$this->userOrder = $userOrder;
}
public function getContactName(): string {
return $this->userOrder->contactName;
}
...
}
class PostLabelProcessor
{
public function getLabelData(AdressTransformerInterface $addressTransformer) {
return [
'contactName' => $addressTransformer->getContactName(),
'phoneNumber' => $addressTransformer->getPhoneNumber(),
'countryCode' => $addressTransformer->getCountryCode(),
'state' => $addressTransformer->getState(),
'city' => $addressTransformer->getCity(),
'address' => $addressTransformer->getAddress()
];
}
}

1. Чем отличаются PHP и javascript?

Между PHP и JavaScript существует несколько ключевых отличий - PHP в основном используется в качестве скриптового языка для разработки бэкендов, причём исторически предполагалось что он именно скриптовый язык, который отдаёт целиком страницы. JavaScript в свою очередь разрабатывался как язык для интерактивности в браузерах, что со временем сильно изменилось, и сегодня JavaScript позволяет не только разработку в браузере, но и предлагает разработку бэкендов с его использованием.

В оригинальном JavaScript, как такового практически нет ООП, впрочем в ранних версиях PHP его тоже не было, но сегодня существуют диалекты JavaScript, как например TypeScript и т.д. PHP исторически создан для того чтобы "умирать" - т.е., пришёл запрос по FCGI (например через Nginx) приложение отработало, вернуло ответ и умерло. JavaScript на бэкенде работает иначе (в основном) - существует реализации веб-серверов на Event Loop (что очень хорошо подходит для асинхронной модели, поскольку задача по большей части I/O bound, а не CPU - если всё сделано правильно), которые способны прослушивать порт и без использования внешнего веб-сервера отдавать запросы, но в продакшене всё равно используют внешний веб сервер, поскольку в данном случае можно обработать намного больше запросов, имплементировать горизонтальное масштабирование и т.д.

С точки зрения типизации оба довольно похожи, оба являются интерпретируемыми (впрочем существует и реализации с использованием JIT, трансляции и т.д., например kPHP).

2. Когда стоит делать классу приставку final и почему? А abstract?

Ключевое слово final в PHP позволяет ограничить дальнейшее наследование класса, т.е. мы не сможем наследовать текущий класс в другом классе. Это помогает избавиться от дальнейших проблем с деревом наследования, так же поощрает использование композиции, поскольку в случае с наследованием бывает тяжело проводить тестирование. Большое дерево наследования считается плохим тоном.

abstract - означает абстрактный класс, в PHP его активно используют для базовых объектов, от которых в дальнейшем наследуются другие классы. По сути это интерфейс с возможностью задать некоторые общие методы для классов имплементирующих данный интерфейс. Пример - хранение данных, это может быть SQLite, MySQL, S3 и т.д. мы можем поместить в базовый абстрактный класс задание и получение конфига (что лучше на самом деле реализовать через отдельный интерфейс).

3. Для чего может быть уместно использовать git rebase?

Из того что сразу приходит в голову: Мы находимся в ветке, в которой разрабатываем новую фичу, в это время параллельно ведётся работа в других ветках, например ветке dev (где ведётся разработка), мы хотим смерджить новые коммиты, поскольку мы отбранчевались раньше, но не хотим иметь коммиты Merged into.. - в этом случае мы можем сделать ребейз - в случае конфликтов мы можем в дальнейшем их разрезолвить средствами Git (в случае с rebase покомитно для каждого где есть конфликт).

5. С какой целью стоит деактивировать кнопку отправки данных HTML-формы (если данные отправляются асинхронно через AJAX, без перезагрузки страницы), до получения ответа от сервера?

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

6. Вопрос-задачка Ваша библиотека работает с объектами классов, имплементирующих интерфейс, в котором есть метод getFormName(). На данный момент в проекте имеется несколько классов, имплементирующих этот интерфейс, у которых есть публичное свойство $formName, а также метод getFormName(), возвращающий это свойство.

<?php
interface FormInterface
{
    public function getFormName(): string;
}

class ClassOne implements FormInterface
{
    public $formName = 'ClassOneForm';
    public function getFormName(): string
    {
        return $this->formName;
    }
}

class ClassTwo implements FormInterface
{
    public $formName = 'ClassTwoForm';
    public function getFormName(): string
    {
        return $this->formName;
    }
}

function workWithForm(FormInterface $formClass): string
{
    if ($formClass->formName) {
        return 'Form Name: '.$formClass->formName;
    }
    return '';
}

echo workWithForm(new ClassTwo());
?>

Вопросы:

  • Что будет выведено на экран при выполнении данного скрипта?
  • Почему можно сказать, что тело функции workWithForm() написано некорректно?

Ответы

  • PHP - динамический язык программирования, поэтому будет выведено '...: ClassTwoForm', мы воспользовались свойством языка, что иногда может быть полезно, но это не является правильным решением.
  • Мы используем интерфейс, в котором содержится метод получающий formName, но вместо использования этого метода, мы используем особенности реализации нескольких классов, что может в определенный момент быть не так (может не быть этого свойства, или оно будет иметь другой уровень защиты) и такую проблему может быть не легко обнаружить. Так же, тестирование в этом случае осложнено.

Вопросы:

  • Если в проект будет подключена библиотека, в которой есть класс ClassHundred, что будет выведено на экран в следующем скрипте?
<?php
class ClassHundred implements FormInterface
{
    public $formNameSimple = 'ClassHF';
    public $formNameFull = 'ClassHundredForm';
    public $simple = false;
    public function __construct($simple = false)
    {
        $this->simple = $simple;
    }
    
    public function getFormName(): string
    {
        if ($this->simple) {
            return $this->formNameSimple;
        }
    
        return $this->formNameFull;
    }
}
echo workWithForm(new ClassHundred(true));
?>
  • Почему можно сказать, что тело функции workWithForm() написано некорректно?
  • Если в проект будет подключена библиотека, в которой есть класс ClassHundred, что будет выведено на экран в следующем скрипте?

Ответы

  • До исправления - функция workWithForm вернёт пустую строку, а так же если мне не изменяет память warning о том, что свойства formName не существует
  • Необходимо заменить проверку свойства и return на return ...$formClass->getFormName()
  • Интерфейсы уместно использовать когда нам необходим соотвествующий уровень абстракции, например получение данных из разных источников. Или различные API для каких-либо задач, например использование нескольких СМС гейтов. Про уместность использования проще говорить предметно, но моё личное мнение, что излишняя абстракция зачастую только вредит, когда наступит момент, что действительно необходимо добавить слой, я считаю имено тогда необходимо это делать. Интерфейсы сильно помогают с уменьшением связанности кода и с следованием всяким хорошим вещам как Паттерны, DRY, KISS, SOLID и т.д.

7. В чём разница между интерфейсом и трейтом в PHP? Для чего их уместно использовать в проекте?

Трейт - примесь, грубо говоря там где используется трейт происходит помещение кода трейта в этом месте, что-то вроде #include в C. Я считаю использование трейтов чаще вредит и лучше прибегнуть к композиции, но для простых вещей, которые не связывают между собой критические элементы их можно использовать.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment