Pull to refresh

Comments 69

Уличили, а не увлекли. Сегодня вас уличили в незнании русского языка.
В случае если сервисы CMS будут спроектированы по шаблону Facade, можно будет добиться довольно хороших результатов. Все проверки, эскейпы и валидации в таком случае, должны проходить не в контроллере, что я довольно часто наблюдаю, а в сервисе, где данные будут отфильтрованы, а результат будет сообщен, как только задача будет выполнена — контроллеру, где и продолжится программный цикл.

Простите, а как вы понимаете паттерн Фасад, что вы такое пишете?

В любом случае, безотносительно понимания, предлагаемое вами — нарушение принципа единой ответственности. Тривиальная валидация входных данных и особенно санитизация (вы же это понимаете под «эскейпом»?) — это обязанность презентационного слоя, а проверка бизнес-условий — в бизнес-слое. Другое дело, что аутентификация/авторизация — это не совсем бизнес-условия, и поэтому они традиционно делаются в сервисном слое.

(кстати, вы перепутали аутентификацию и авторизацию в вашем примере)
Да я опустил множество мелких деталей, но не упустил важнейшего чего и хочу добиться. Такие вещи как авторизация / регистрация, должны реализовываться в момент (чего?), что и показала нам группа разработчиков Django

Мне кажется, или вы «забыли» самое главное слово во всем этом предложении? Так всю статью чего то не хватает.

Можете еще раз объяснить, чем вам так понравился подход с аутентификацией в Django?
«В момент» тут значит, скорее всего, «моментально», «быстро».
Тогда становится интересно, чем же такой подход отличается от любого другого фреймворка с модулем аутентификации? )
а зачем вообще смешивать APi и модули. API — должна быть отдельная часть системы, возможно со своей аутентификацией, роутингом и все такое. Если какой то метод использует данные из модуля пусть просто обратится к объектам этого модуля.
Вообще не очень понятна цель статьи. Ну делаете CMS в которой по другому расположены папки. Новичкам, которые не имея опыта, бросаются писать свои CMS наверно будет полезно, не более того.

Если вам нравится Django просто портируйте его на PHP один к одному.
Уверен есть достаточно разрабов, которым нравится Django но они не могут его заюзать, например, из-за ограничений шаред-хостинга. И людям польза и вам приятно.

к сожалению, Python не PHP и некоторые вещи, активно используемые в Django портировать не выйдет, например, декораторы
Django не видел, но загуглил и посмотрел немного, могу путать, но разве Doctrine\Annotations (http://docs.doctrine-project.org/projects/doctrine-common/en/latest/reference/annotations.html) не оно?
не совсем, декораторы в Python это фактически реализация паттерна «декоратор» — вы можете обернуть выполнение вашей функции/метода в какой-то декоратор. Например, в django на view функцию можно повесить @login_required и таким образом она будет доступна только для авторизованных пользователей

В PHP так просто сделать не получится (есть решения из области AOP, но это немного не то уже)

А аннотации в доктрине это копия аннотации из Hibernate в Java, и используется немного для другого :)
Например, в django на view функцию можно повесить @login_required и таким образом она будет доступна только для авторизованных пользователей
В PHP так просто сделать не получится

Это было крайне сложно, но похоже я справился
class LoginViewDecorator{
  private $controller;
  
  public function __construct($controller){
    $this->controller = $controller;
  }

  public function view(...){
    if(Annotation::fromMethod($this->controller, 'view')->has('login_required')){
      if(!$serviceLocator->get('user')->hasLogin()){
        throw new RuntimeException('...');
      }
    }
    return $this->controller->view();
  }
}


да, я согласен, что в PHP единственной простой альтернативой является декоратор/прокси.
Но везде, где у вас инстанцируется декорируемый класс, Вам придется обернуть этот класс декоратором. И хорошо если у Вас хорошая архитектура и такое место в коде одно :)

но тут разговор идет о портировании джанго в пхп, и я говорил о том, что многие вещи, которые сделаны очень удобно в джанго не будут таковыми в пхп из-за ограничений самого языка и во многом портирование будет бессмыслынным, потому что это по итогу получится подобие Symfony 2/ Laravel 5
декораторы в python ссылаются на функцию, которая может что-то сделать. Добавлять таким образом дополнительное поведение как минимум странно, так как декорируемый объект об этом поведении начинает что-то подозревать. Да удобно, но направление зависимостей как-то не в ту сторону смотрит, вы не думаете? В пайтоне такие штуки удобно использовать для DRY-га всяких второстепенных вещей вроде… логирования deprecated вызовов. Словом внутренняя кухня. Расширять таким образом поведение объектов как по мне не круто.

Но может быть я не прав.
я может не совсем вас понял, но чем отличается это от паттерна «декоратор»

в нем вы так же ссылаетесь на декорируемый объект

насчет того, что функция начинает, что-то подозревать тоже наверное не понял вас

но вы правы, что основная цель их в python — это DRY. с их помощью легко можно избавиться от сквозного функционала в духе логирования/кэширования
я говорю о маленьких вещах, вроде пометки депрекейтед методов. Это реально удобно и читабельно, в PHP для этого средств действительно нет (ну как… есть, но это кастыли с phpdoc).

насчет того, что функция начинает, что-то подозревать тоже наверное не понял вас

Если вы у метода ставите какой-то атрибут что бы добавить какое-то дополнительное поведение — это я и называю «метод начинает что-то подозревать».

в нем вы так же ссылаетесь на декорируемый объект

Да, но декорируемый объект не подозревает что его декорируют. Как на счет реализации таким же способом адаптера?
ну вот насчёт «метод подозревает» не могу согласится

при вызове данного метода из клиентского кода мы не знаем декорированный он или нет, точно так же как и при реализации паттерна. сам метод тоже не может получить данных о том, декорируют его или нет
Так каким образом именно метод может узнать декорирован он или нет?
Вы хотите чтоб я еще раз постарался и справился?
простите, справились с чем именно? портированием джанго? или такой реализацией, чтобы не приходилось оборачивать инстанцируемые классы?
На второе я бы взглянул
Зачем портировать джанго?
или такой реализацией, чтобы не приходилось оборачивать инстанцируемые классы

Внутри там все оборачивается, просто вы этого не видите.
В пару строк кода
interface Controller{
  ...
}

abstract class Decorator{
  protected $controller;

  public function wrap(Controller $controller){
    $this->controller = $controller;
  }
}

class LoginActionDecorator extends Decorator{
  public function __call($method, $args){
    if(substr($method, -6) == 'Action'){
      if(!ServiceLocator::get('user')->isLogin()){
        throw new RuntimeException('Нет прав');
      }
      else{
        return call_user_func_array([$this->controller, $method], $args);
      }
    }
  }
}

class ControllerFactory{
  private $wrappers = [];

  public function get($className){
    $controller = new $className;
    foreach($this->wrappers as $wrap){
      $controller = $wrap->wrap($controller);
    }
    return $controller;
  }

  public function addWrap(Decorator $wrap){
    array_push($this->wrappers, $wrap);
  }
}

$myControllerFactory = new ControllerFactory;
$myControllerFactory->addWrap(new LoginActionDecorator);
$controller = $myControllerFactory->get('app\Controllers\TaskController');


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

и не я предлагаю портировать джанго, я наоборот считаю это бессмыслынным
Ну так в чем же тогда проблема? Иль вам нужно чтоб это было именно в ядре интерпретатора?
да, скорее мне просто не хватает именно такого в ядре языка, так же как и именнованных параметров функций и еще нескольких вещей
Ну языки потому и называются по разному, что у них реализации отличаются )
портировании джанго в пхп

Зачем, если Symfony лучше? Для PHP правильно мигрировать практики из Java/C# мира, так как семантика языка ближе. Скажем symfony это по большей части клон spring-а.
так я и не предлагал портировать, не моя идея же :) я ведь и утверждаю, что выйдет то по факту что-то около симфони или ларавел
и я не вижу в этом ничего плохого.
C symfony всё значительно сложнее, это скорее клон гибрида OSGi (концепция бандлов), Spring (концепция сервисов и архитектуры в целом) с прилеплными Pebble (местный Twig) и Hibernate/MyBatis (то, из чего выросла Doctrine), модифицированный личным восприятием Фабиена… И этот клон чертовски хорош, некоторых крутых штук в оригинале просто нет
twig это порт jinja скорее, как и pebble. Но по остальным пунктам я полностью согласен.
ну не совсем, аннотации в этом контексте служат лишь способом конфигурации листенеров слоя секьюрити, то есть это не декорация. И да, я что-то запутался в вашем полете мысли. Вы просто хотите добавлять поведение дописав строчку у метода? Ну так для вас есть go-aop, я думаю это как раз таки то что вы хотите. И да я осуждаю людей которые таким образом добавляют кеширование например.
согласен, с точки зрения реализации отличается, но результат у @login_required и у Security один и тот же и выглядят одинаково, я говорил об этом

А go-aop мне не очень нравится, потому что очень сложный и выглядит реально как борьба с языком

на самом деле это больше мои прихоти, не знаю почему такой большой тред вышел из-за этого)

а как вы решаете вопросы кеширования/логирования? классы-декораторы?
классы-декораторы?

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

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

Если некий объект агрегирует другой, значит он уже декоратором зовется? За управление доступом к субъекту отвечает прокси, а не декоратор.
в python декораторы как раз-таки «декораторы», а не «прокси» с точки зрения функции — они позволяют динамически добавлять новую функциальность

Но соглашусь, что @login_required похож на прокси, так как управляет доступом к субъекту
а что, прокси не могут управлять доступом к субъекту?
так вроде написал же, что могут, и этим занимаются :)
в python декораторы это не «декораторы» в контексте GoF-ких паттернов. Это указатель на функцию высшего порядка и только. Позвольте привести маленькую но емкую цитату из документации:

A function returning another function, usually applied as a function transformation using the wrapper syntax. Common examples for decorators are classmethod() and staticmethod().

The decorator syntax is merely syntactic sugar, the following two function definitions are semantically equivalent:
def f(...):
    ...
f = staticmethod(f)

@staticmethod
def f(...):
    ...




грубо говоря говорить о декорации тут не выходит, так как, если мы уберем синтаксический сахар, наша «декорируемая» функция будет более чем знать об этом дополнительном поведении. Это всего лишь позволяет нам сократить код и вынести дублирование в пределах модуля. Но добавление кеширования таким образом например грубо будет противоречить принципу единой отвественности.
А еще в пайтоне, функции это объекты)
ну я не хотел упоминать функции высшего порядка раз мы старались говорить в терминах ООП :)

да, я в курсе что это просто синтаксический сахар, но это не нарушает принцип единственной ответственности, f занимается своим, a staticmethod своим

вы же не говорите, когда оборачиваете объект бизнес-логики кэширующим декоратором, что теперь у объекта 2 ответственности — и кеширование и бизнес логика

так каким образом при оборочивании функции другой функцией нарушается принцип единственной ответственности?

есть очень большая разница между трансформирующими функциями (staticmethod нужен здесь и сейчас, так как это часть реализации f), и добавлением поведения (кеширование через декорацию). В об этом говорю.
По поводу именно staticmethod согласен, но декораторы в python, механизм намного более мощный, чем просто реализация паттерна. Это уже специфика реализации и классов, и самих декораторов в пайтоне ( я про @staticmethod, @classmethod, @abstractmethod и тд )

но представьте, что мы используем декоратор cache или log. Каким образом в таком случае нарушается принцип единственной ответственности?
вообще немного не правильно с моей стороны говорить о gof паттернах в контексте функций, поэтому уж лучше рассматривать class decorators в python, вот это как раз «классические» декораторы
ну раз уж не корректно говорить о gof паттернах в контексте функций (они ж в пайтоне объекты, объекты можно декорировать), то тогда совсем не уместно сравнивать пайтоновские недо-классы (и это в хорошем смысле) с тем что в пыхе или жабе. Намного проще сравнить пайтоновские классы с оными в javascript (те что из es2015). Вот там да, можно брать пайтоновские практики и радоваться (более того, аннотации/декорацию там тоже путят в рамках es2016).
да я и не пытался их сравнивать, в smalltalk классы тоже не такие как в java, но тем не менее паттерны применимы
вот и в случае с python, для программиста незнакомого с python более уместным/классическим будут выглядитт class decorators

А в js такое часьл называют decorator function ;)
В PHP легко можно реализовать паттерн декоратор, а обернуть его в декоратор так, как как в Python/Django (аннотацией "@") можно вышеуказанной библиотекой доктрины
чтобы получить похожий функционал

1) все классы содержащие эти аннотации должны создаваться через фабрики/DI контейнеры/сервис локаторы

2) это будут не декораторы, а прокси, который будет через __call__ отлавливать вызовы, парсить аннотации, вызывать нужный код, потом вызывать метод проксируемого класса

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

ну и по итогу
1) в хорошей архитектуре в большинстве случае классы так и будут создаваться, это не проблема

2) из-за использования такого прокси теряется читаемость, да и вызывать call_user_func_array на каждый вызов оборачиваемого метода тоже не класс

поэтому в PHP как и Fesor я предпочитаю использовать явные декораторы
Есть ещё кодогенерация декоратов
это можно имитировать или обойтись.
Суть фреймворка ж не реализация декораторов а реализация опрделенной логики
Тому кто будет пользовать фреймворк все равно как там внутри. иначе какой смысл в фреймворках.

Я свой фреймворк с явы портировал хотя в PHP нет например ни анонимных классов ни нормально реализованных замыканий.

мне вот лично очень не хватает декораторов в PHP

вот пример, как они используются в Django
как вы бы реализовали это в PHP?
docs.djangoproject.com/en/1.8/topics/auth/default/#django.contrib.auth.decorators.login_required

это не то, как фреймворк устроен внутри, это то, что он предоставляет для использования, довольно активного использования, и без это во многом теряется его удобство и простота
мне вот лично очень не хватает декораторов в PHP

вам не хватает аннотаций, давайте выражаться яснее. Те штуки которые вы хотите частично решает стремная штука под названием go-aop, и я не одобряю подобные тенденции и веяния. Обычный старый добрый интерфейс, отдельный объект декорирующий наш маленький объект и все хорошо.
согласен, не хватает аннотаций

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

Но я скорее за то, чтобы они были, чем не были

ну а так с вами согласен, для многих вещей лучше подходит паттерн декоратор, да и go-aop не совсем мне нравится на самом деле
Могу с вами поделится реализацией аннотаций на PHP в стиле Java или JSON, если вам их так сильно не хватает
я бы с удовольствием взглянул, правда, без какой-либо иронии или сарказма
но я говорю вот о таких декораторах/аннотациях gist.github.com/CHH/4520200

а о библиотеках в духе php annotations я конечно вскурсе
Это больше похоже на какие то костыли для обхода динамической типизации в PHP, если честно. Подозреваю что это предварительно еще компилируется?
а о библиотеках в духе php annotations я конечно вскурсе

Тогда не буду делиться ))
Этот пропозал был подготовлен в рамках обсуждения добавления в PHP нативной поддержки DbC (если я правильно помню эти обсуждения в интерналс). То есть эти «декораторы» это грубо говоря «аспекты» в контексте АОП. Возможность декларировать дополнительную логику. Вне чисто функционального языка (PHP не является функциональным языком, согласитесь) в этом нет как по мне ровным счетом никакого смысла. В Python да, это удобно для некоторых кейсов. Но не для добавления логирования/кеширования (хотя можно, но я считаю это увеличением технического долга и допустимым только при быстрой разработке)
Иерархия папок и файлов, на мой взгляд, ввообще не имеет значения: файлы подгружаются по __autoload.

Замечу, что CMS (как создатель своей CMS) очень сложный проект, на сегодня это 30000 строк кода — за сколько вы осилите такой объем?
UFO landed and left these words here
Да даже с должным изначальным планированием многое (читать — все) будет критиковаться и переписываться. Программирование это «грязная задача», тут это в порядке вещей, особенно когда на это есть время.
UFO landed and left these words here
Мой фреймворк помог мне устроится на работу, так что время потрачено не зря )
UFO landed and left these words here
Такие штуки надо писать втихую, чтобы никто о них не знал. Тем более когда каша в голове, а амбиции словно в космос собрался :)
Несколько советов от меня:
1. Используй как можно больше готовых библиотек и подключай их через composer.
Например нужен роутинг, вводишь в гитхабе в поиске php routing и смотришь какие библиотеки есть на эту тему. Поэкспериментируй с каждой, и поймешь какая наиболее подходит тебе. Писать все с нуля заколебешься. Тоже самое касается кеширования, шаблонизации, авторизации и прочего. Уже практически для всех целей есть независимые библитотеки.
2. engine лучше тоже вынести в отдельный модуль и подключать через composer. Чтобы не копировать эту папку каждый раз в новый проект.
3. Сейчас в основном фреймворки используют шаблон проектированияя Dependency Injection, почитай на эту тему.
4. Раз уж ты пишешь CMS, то имеет смысл основать ее на каком-нибудь популярном фреймворке типа Laravel, Symfony, Yii. Так другим людям будет проще вникнуть в твою архитектуру, писать новые модули и т.д.
5. Не сдавайся :)
Не, там бесполезно говорить ему о Composer'e и чужих, написанных и оттестированных библиотеках. Автор в прошлой статье практически сказал, что все текущие CMS и фреймворки — фуфло. Он будет все с нуля писать. Он даже здесь докопался до Django и Symfony. Мне теперь даже стало интересно, насколько быстро его CMS, которая игнорирует все best practiсe, загнется от того, что автор не сможет безболезненно её модифицировать. А на «вторую попытку» силы вряд ли останутся.
Web такая среда где игнорирование best practiсe часто залог не только выживания, но и долгого контрпродуктивного развития. Хотя не данном случае конечно. В данном случае вы абсолютны правы — все уткнется в «не сможет безболезненно её модифицировать».
Запилили бы на rust или nim, там пока непаханое поле. А на пхп свою цмс писал каждый ещё в 2000 году.
я все же продолжу как и планировал.

вас не отговорить пытались от идеи написания, пишите конечно, это полезная практика. Вас от вашего плана пытались отговорить. У вас судя по тому что вы пишите банально недостаточно опыта что бы сделать что-то лучше чем то что есть сейчас.

К примеру реализация задачи по быстрому и простому способу создания блога для человека, которому довольно сложно объяснить как им пользоваться — чрезвычайно сложна по своей природе

Она разбивается на множество весьма простых задач, каждую из которых можно решить, предоставить решению интерфейс, предоставить стандартную реализацию (или несколько) и это позволит уже сильно быстро создать бложик. А вопрос админки это уже отдельная песня. Мне в этом плане нравится концепция выделения функционала управления этим делом в rest api сервис и отдельный независимый интерфейс. Так поддерживать все это добро будет проще.

Вы можете себе представить насколько удобно можно реализовать интеграцию социальных сетей, API Аналитики и прочих «сладостей» нынешних возможностей мира информационных технологий для конечного пользователя?

нынче это весьма тривиальная задача которую решают люди каждый день. И количество качественных готовых решений не равно нулю.

Я могу довольно долго продолжать описывать мои идеи и цели

Пока ваши идеи цели выражаются как «хочу написать CMS и при этом использовать хабр как жж-ку где буду записывать как у меня дела, при этом я не читал ни одной книжки по ООП и т.д. но эффект даннинга-крюгера дает о себе знать».

Иерархия поддиректорий проекта

Почему вы так пристали к иерархии директорий? Или это уже синоним архитектуры приложения?

В свое оправдание могу сказать что паттерны проектирования уместны не во всех ситуациях.

Ну да, если мы не знаем о паттернах то они не применимы, согласен. Паттерны это не цель, это статистическая данность, типичные решения для уменьшения связанности системы.

Но если взять тот же Pattern Facade или Observer, общая картина становится не такой уж очевидной, хотя и представить ее можно.


Фасад — смотрим что значит это слово в любом словаре. Грубо говоря это шаблон проектирования, суть которой скрыть сложность реализации красивым и удобным интерфейсом. Это не та чушь которая была в Laravel 4 (хотя частично можно так сказать, ибо это упрощение доступа к контейнеру но все же...).

Observer — есть штука которая эмитит события, есть слушатели которые на события подписаны. Что тут сложного?

Да я опустил множество мелких деталей, но не упустил важнейшего чего и хочу добиться.

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

Ай короче… печально это. удачи вам что-ли.
Чтобы написать что-то стоящее нужно подробно изучить уже существующие фреймворки и cms, почитать что такое ооп, почитать gang of 4, почитать Фаулера, затем попрактиковать что вы почитали. Еще более важно определиться с целью, надо знать что вы делаете и зачем. Но это лично мои советы. Я в свое время не нашел фреймворка с полноценным отделением бизнес логики от инфраструктуры и это стало целю в одном из проектов.
Sign up to leave a comment.

Articles