Jestę Builderę post
2015-10-08
Jestę builderem bo implementuję fluent interface? A może jestem builderem bo buduję? Często te dwa wzorce są ze sobą mylone. Wyjaśnijmy różnicę.
Fluent Interface - metody w klasie zwracają jako rezultat instancję tej klasy przez co można robić coś takiego:
$object->method1()->method2()->method3();
Builder - używany do konstruowania skomplikowanych obiektów mogących przyjąć sporo parametrów, zarówno wymaganych lub opcjonalnych. Od fabryki różni się tym, że z każdym wywołaniem jakiejś metody na builderze tworzona lub rozszerzana jest część budowanego obiektu.
Dlaczego te dwa wzorce są mylone?
Ponieważ najwygodniej jest korzystać z buildera który implementuje fluent interface. Na przykład:
$queryBuilder->add('select', 'u')
->add('from', 'User u')
->add('orderBy', 'u.name ASC')
->setFirstResult( $offset )
->setMaxResults( $limit );
Nie wszystko co implementuje fluent interface jest jednak builderem. Pierwszy z brzegu przykład to Eloquent Query Builder W tym wypadku nic nie jest budowane przez builder. Builder wprawdzie coś składa, ale nie zwraca budowanego query, a rezultat jego wykonania. Dlaczego taka konstrukcja może być szkodliwa? Ponieważ zwiększa ilość powodów dla których moglibyśmy chcieć klasę tego buildera zmodyfikować. Pierwszym powodem jest oczywiście rozszerzenie sposobu budowania query. Ponieważ jednak zbudowane query nie jest zwracane, tylko jest używane w celu wykonania odpowiediego zapytania może się okazać, że trzeba będzie buildera modyfikować również z powodu samego wykonania query.
Po co tak rozszerzać buildera? Chociaż by po to żeby dołożyć system zapisywania do logu wykonanych zapytań lub
zmodyfikować wszystkie zapytania w celu dołożenia jakiegoś warunku na podstawie parametru locale
z requesta.
Gdyby query zostało najpierw zbudowane, następnie zwrócone można by z nim coś jeszcze zrobić zanim zostanie przez coś innego
wykonane.
Ogólnie sam fluent interface bardziej szkodzi niż pomaga, metody robią co innego niż mówi ich nazwa. Przykładowo od metody "add" można by się spodziewać dodania czegoś, jednak na podstawie nazwy ciężko wywnioskować, że zwróci ona także instancję obiektu, na którym została wywołana. Więcej na temat szkodliwości fluent interface do poczytania na blogu jednego z twórców Doctrine ORM.
Jednak zgodnie z założeniem, że kodu nie można oceniać bez kontekstu, ciężko o stwierdzenie czy ten wzorzec jest jednoznacznie zły czy jednoznacznie dobry. Trzeba mieć bardzo dobry powód, żeby go użyć i takim powodem nie jest ułatwienie wykonywania metod.