Statyczne jest złe, ale co z wzorcem fabrycznym?

13

Jestem przy projekcie TDD, więc staram się jak najlepiej trzymać dobrych praktyk związanych z tego rodzaju rozwojem. Jednym z nich jest unikanie w jak największym stopniu statycznego i globalnego.

Mam do czynienia z tym problemem: mam obiekt „artykuł”, który może mieć powiązane z nim „opcje” (dodatkowe „mikro-artykuły”).

Nie mogę wymyślić, jak zastosować dobre podejście, które nie przyniesie efektów nieproduktywnych lub nie wygeneruje zbyt wielu zapytań, ponieważ byłbym w sytuacji, w której wszystko jest tak oddzielone, że w zasadzie będę musiał wykonać 1 zapytanie na obiekt.

Z mojej obecnej perspektywy widzę 3 opcje:

1) Zbuduj artykuł wewnętrzny:

class Article
{
    //[...]
    public function getArrOption(){
        //Build an array of Options instance.
        //return an array of Options.
    }
}

Pro: prosto

Const: Maintenanceability: Obiekt artykułu zawiera teraz logikę budowania dla obiektu Option. Prawdopodobnie doprowadzi to do duplikacji kodu.

2) Korzystanie z opcjiFactory

class Article
{
    //[...]
    public function getArrOption(){
        return OptionFactory::buildFromArticleId($this->getId());
    }
}

Pro: Logika budowy nie jest poza klasą artykułów

Const: Łamię zasadę „statyczny jest trudny do kpienia”, co utrudnia testowanie mojej klasy artykułów.

3) Oddziel wszystkie logiki.

//Build the array of Option instance in a controller somewhere, using a Factory:
$arrOption = OptionFactory::buildFromArticleId($article->getId());

Pro: Artykuł radzi sobie tylko z własną odpowiedzialnością i nie dba o link „ojca” do opcji. Rzeczy są naprawdę oddzielone

Const: Będzie wymagał więcej kodu wewnątrz Kontrolera za każdym razem, gdy będę potrzebował uzyskać dostęp do Opcji. Oznacza to, że nigdy nie powinienem używać fabryki wewnątrz obiektu, a to brzmi dla mnie trochę utopijnie ...

Jaka jest najlepsza droga? (Czy coś przeoczyłem?) Dzięki.

Edytować:

Nie wspominając już o tym, że jeśli nie mogę zadzwonić do fabryki wewnątrz klasy, w zasadzie nigdy nie mogę użyć leniwego wzorca inicjalizacji ...

FMaz008
źródło
Nie jestem pewien, czy ma to znaczenie, ale piszę w PHP, więc „aplikacja” ma mniej stanu. Musimy ponownie załadować wszystkie dane między każdą stroną, jeśli nie są zapisane w sesyjnym pliku cookie. Oznacza to, że nie możemy wstępnie załadować wszystkiego, tak jak w języku aplikacji.
FMaz008,
@job: Cóż, ponieważ wywołanie statyczne w metodzie jest w większości niemożliwe do zastąpienia podczas testowania jednostkowego. Celem jest zastosowanie wstrzykiwania zależności. Ale fabryka jest zwykle statyczna, więc nie można jej wstrzyknąć.
FMaz008,

Odpowiedzi:

12
  1. Statyczne nie jest „złe”, jest niemożliwe do wyśmiewania. Nadal możesz go używać tam, gdzie drwiny nie mają sensu.

  2. To nie jest wzór fabryczny, wygląda jak wzór repozytorium, chociaż może nie być. Fabryka to miejsce, w którym masz wiele klas z tym samym interfejsem / klasą podstawową i chcesz oddzielić logikę, która decyduje, którą klasę zwrócić. Repozytorium pobiera dane ze swojego repozytorium, wyodrębniając implementację tego repozytorium (artykuł nie musi wiedzieć, czy jego opcje są przechowywane w tym samym DB, innym, pliku XML, pliku CSV, cokolwiek).

  3. Zignorowałeś możliwość nadania klasie Article obiektu ObjectFactory (lub repozytorium lub cokolwiek innego) w konstruktorze, na którym może on wywołać metodę buildFromArticle.

Mój PHP jest zardzewiały, ale myślę, że wygląda to tak:

class Article
{
    private $_option_repository;

    public function __construct($option_repository) {
        $_option_repository = $option_repository;
    }

    //[...]

    public function getArrOption(){
        return $_option_repository->buildFromArticleId($this->getId());
    }
}

Myślę, że to spełnia wszystkie twoje zalety powyżej.

pdr
źródło
Czy możesz mieć instancje Factory / Repository / Mapper? Będę potrzebował kontenera zależności lub czegoś takiego, ponieważ jeśli będziemy musieli wstrzyknąć wszystkie fabryki / repozytorium / mapowanie dla wszystkich możliwych obiektów, które mogą zostać zwrócone przez obiekt, to szybko zrobiło dużo. (Artykuł -> OptionGroup -> Opcja -> Artykuł itp.)
FMaz008
1
To więcej niż ok, lepiej. Zasadniczo zastrzegam użycie statyczne do usuwania powtarzającego się kodu, który jest wystarczająco mały, aby przetestować go w wielu klasach. I tak, kontener IOC / DI znacznie ułatwi ci życie. Użyj jednego.
pdr
1

Oto cytat z papieru, który dowodzi, że nigdy nie potrzebujesz metod statycznych, że wykazano, że abstrakcyjne fabryki są mylące, i sugeruje niewielką zmianę języka w kierunku wstrzykiwania zależności jako rozwiązania.

Ścisłe sprzężenie między instancjami i ich klasami przerywa enkapsulację, a wraz z globalną widocznością metod statycznych komplikuje testowanie. Uczyniając wstrzykiwanie zależności cechą języka programowania, możemy całkowicie pozbyć się metod statycznych. Stosujemy następujące zmiany semantyczne:

(1) Zastąp każde wystąpienie globalne dostępem do zmiennej instancji;

(2) Niech zmienna instancji zostanie automatycznie wstrzyknięta do obiektu, gdy zostanie utworzona instancja.

„Seuss: Obowiązki oddzielenia od metod statycznych w celu szczegółowej konfiguracji”

Link do maszyny Wayback

nes1983
źródło
3
Chociaż ten link może odpowiedzieć na pytanie, lepiej dołączyć tutaj istotne części odpowiedzi i podać link w celach informacyjnych. Odpowiedzi zawierające tylko łącze mogą stać się nieprawidłowe, jeśli połączona strona ulegnie zmianie.
komar