Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Kod doskonały vs rzeczywistość
Forum PHP.pl > Inne > Hydepark
Stron: 1, 2, 3, 4
com
No dokładnie te same dla tej grupy wideo, tak samo będzie dla audio i czego sobie tam jeszcze nie wymyślisz.

Każda grupa to jakiś schemat, który dodatkowo rozbudowujesz i zależnie od tego jak skomponujesz te obiekty taki uzyskasz context, ale dla każdej operacji będzie on inny choć operujesz dalej na tym samym obiekcie, wiec oczekujesz dostać ten obiekt, a nie cokolwiek.

Cytat
Każdy framework tak ma, nie wiesz?


Wskaż mi gdzie framework ma coś nie udokumentowane i nie wiesz co uzyskasz jak wykonasz dany fragment jego kodu.

To trochę tak jakbyś z kota chciał zrobić mysz

To jak się uparłeś już na te twoje "dekoratory" to zrób chociaż Fasade, żeby to można było produktywnie wykorzystać. Tylko to jest trochę bez sensu.

Bo ile teraz ktoś kto chce dodać głupi obrazek musi pamiętać, żeby taki plik dodać. A wzorce maja dawać te możliwość, że jedno rozwiązanie możesz wprost wyjąc z tego projektu i dodać do innego i tam też będzie działać i teraz zamiast kod się sam dokumentować, piszesz how to use.

Dekorator jest rozwiązaniem które można tu zastosować ale tak jak już zostało napisane:
Cytat
Dekoratorem może być tutaj tylko Thumbnail bo on faktycznie coś tam robi na źródłowych danych.


czy jak na tym stacku który podrzucałeś:
  1. process = new Upload(myImage); // concrete component
  2. process = new Resize(process); // decorator
  3. process = new Clip(process); // decorator
  4. process->run();


Cytat
Jak we wzorcu dekorator przekazywać pomiędzy obiektami atrybuty elementu dekorowanego, tak, żeby każda klasa miała do nich dostęp i mogła je modyfikować i, żeby każdy obiekt widział te zmiany.
Tak:

http://blogophp.com/2009/08/16/dekorator/#more-40

Pierwsze co powinno To Cie właśnie zastanowić, że coś jest tutaj nie tak, skoro tak nie jest. Jak już używamy wzorców to trzeba ich używać dobrze, bo inaczej zamiast kod rozjaśnić zaciemniamy go.

Podsumowując, właściwości nie powinny wędrować miedzy dekoratorami, bo wtedy już to nie jest dekorator! Dekorujesz konkrety obiekt bazę która rozszerzasz, a nie budujesz nad obiektem bazy nowego obiektu do rozszerzeń. Każdy dekorator to niezależny byt, który możesz wywołać bezpośrednio na obiekcie bazowym zawsze i nie potrzebujesz do tego więcej dekoratorów po drodze.

To:
  1. $video = new PropertyGeter(
  2. new ThumbnailFromVideoCuter(
  3. new ContentGeter(
  4. new FilePuter)));
  5.  

i to:
  1. $video = new ThumbnailFromVideoCuter(
  2. new PropertyGeter(
  3. new ContentGeter(
  4. new FilePuter)));
  5.  


Ma dać ten sam efekt, daje, zapewne nie bo ThumbnailFromVideoCuter() zależy pewnie od obiektu PropertyGeter(), skoro potrzebny był Ci obserwator.
Omenomn
Cytat
Wskaż mi gdzie framework ma coś nie udokumentowane i nie wiesz co uzyskasz jak wykonasz dany fragment jego kodu.

To trochę tak jakbyś z kota chciał zrobić mysz


Mówię o routingu. Dziwnie Cię zdziwiło, że są metody przypisane do konkretnej akcji z urla.

Cytat
Podsumowując, właściwości nie powinny wędrować miedzy dekoratorami, bo wtedy już to nie jest dekorator! Dekorujesz konkrety obiekt bazę która rozszerzasz, a nie budujesz nad obiektem bazy nowego obiektu do rozszerzeń. Każdy dekorator to niezależny byt, który możesz wywołać bezpośrednio na obiekcie bazowym zawsze i nie potrzebujesz do tego więcej dekoratorów po drodze.


Miedzy dokoratorami będę przekazywał obiekt, w którym będzie się znajdował kontent pliku plus inne atrybuty, które można modyfikować, a nie jak do tej pory sam kontent. Dlatego musiałem rozwiązać problem przekazywania właściwości, ale z obiektem będzie prościej o wiele.

Cytat
To:
  1. $video = new PropertyGeter(
  2. new ThumbnailFromVideoCuter(
  3. new ContentGeter(
  4. new FilePuter)));


i to:
  1. $video = new ThumbnailFromVideoCuter(
  2. new PropertyGeter(
  3. new ContentGeter(
  4. new FilePuter)));




Ma dać ten sam efekt, daje, zapewne nie bo ThumbnailFromVideoCuter() zależy pewnie od obiektu PropertyGeter(), skoro potrzebny był Ci obserwator.


Nieprawda, znajdź mi informację, że w dekoratorze kolejność dekoratorów ma nie mieć znaczenia.
Choćby zawsze musi istnieć końcowy dekorator, który nie przyjmuje wartości do konstrukora, więc jeżeli musi istnieć końcowy, to może istnieć początkowy i może istnieć ustalona kolejność.
daro0
Prawda jest taka, że gdybym miał się tak zastanawiać nad tym wszystkim to bym nigdy nie skończył w terminie żadnego projektu a wymagania są określone przez klienta. Owszem czasem trzeba coś dodać, coś nie było przewidziane, jednak na ogół nie jest to problemem. Tak na szybko (pierwsze co mi przychodzi na myśl). Zakładam że jest formularz typu Multipart/Formdata w widoku a w nim daje się jakiś opis w pole tekstowe, jest pole do wybierania obrazka do uploadu oraz opcja checkbox, czy ma być miniaturka czy też nie a rozmiary są określone w konfiguracji frameworka.

application/classes/Request.php

  1. <?php defined('SYSPATH') OR die('No direct script access.');
  2.  
  3. class Request extends Kohana_Request
  4. {
  5. public function files($key = NULL)
  6. {
  7. if ($key === NULL)
  8. {
  9. return $_FILES;
  10. }
  11. else
  12. {
  13. return Arr::path($_FILES, $key);
  14. }
  15. }
  16. }
  17.  


i metoda kontrolera

  1. public function action_upload()
  2. {
  3. if ($this->request->method() == HTTP_Request::POST)
  4. {
  5. if ($this->request->files('attachment'))
  6. {
  7. $config = Kohana::$config->load('app.uploads.images');
  8. $options = array(
  9. 'image' => array(
  10. 'width' => Arr::path($config, 'image.width'),
  11. 'height' => Arr::path($config, 'image.height'),
  12. ),
  13. 'thumbnail' => array(
  14. 'width' => Arr::path($config, 'thumbnail.width'),
  15. 'height' => Arr::path($config, 'thumbnail.height'),
  16. ),
  17. 'use_thumbnail' => $this->request->post('chk_use_thumbnail') ? TRUE : FALSE,
  18. );
  19.  
  20. $result = Uploader::factory('Image')
  21. ->options($options)
  22. ->data($this->request->files('attachment'))
  23. ->execute();
  24. }
  25. }
  26. // do something
  27. }


To jest tylko jedno z możliwych podejść. Tutaj to czy jest miniaturka czy nie to jest określone na podstawie tego co zwraca $_POST a ten helper musiałby na podstawie tego wykonywać pewne operacje. Zakładam użycie helperów Image oraz Upload z tego frameworka.

Nie wiem jak by to wyglądało w Laravelu a już tym bardziej gdyby pisać całkowicie od zera.
Omenomn
Jak się poświęci więcej czasu przy jednym projekcie na dobre rozwiązanie problemu, to później tego rozwiązania można używać w kolejnych, i czas wykonania skraca się o wiele.
Piszą o tym w książkach o wzorcach i dobrych praktykach.
Problem jest rozwiązany i działa meeega!!
Teraz mam bibliotekę rozszerzalną na miliony sposobów, gotową do użycia w innych projektach.
ohm
Cytat(Omenomn @ 11.01.2017, 11:32:25 ) *
Teraz mam bibliotekę rozszerzalną na miliony sposobów, gotową do użycia w innych projektach.


Tylko pytanie czy inni też będą wiedzieli jak tę bibliotekę szybko i łatwo użyć?
nospor
Cytat
Tylko pytanie czy inni też będą wiedzieli jak tę bibliotekę szybko i łatwo użyć?
Soryy, ale nie bede sobie "nieulatwial" pracy tylko dlatego ze ktos moze kiedys nie wiedziec jak tego uzyc... Ja tez mam wlasne liby w Symfony ktore generalizuje mi rozne rzeczy, np Grid, edycja, usuwanie, w sume caly CRUD. Na napisanie tego poswiecilem chwilke czasu ale teraz kazdy kolejny CRUD to 15 minut robotyi wiem ze wszystko bedzie dzialac tak samo.

Wracajac do twojego pytania:
Po pierwsze, skoro klasa byla uzywana w projekcie, to sa przyklady uzycia
Po drugie, zazwyczaj jak potrzeba pisze sie komentarz.
Omenomn
Zgadzam się z nospor
com
Cytat
Mówię o routingu. Dziwnie Cię zdziwiło, że są metody przypisane do konkretnej akcji z urla.


Nie napisałem że coś w tym dziwnego, napisałem, że masz tu strategie o której ja i inni mówiliśmy.

Cytat
Nieprawda, znajdź mi informację, że w dekoratorze kolejność dekoratorów ma nie mieć znaczenia.
Choćby zawsze musi istnieć końcowy dekorator, który nie przyjmuje wartości do konstrukora, więc jeżeli musi istnieć końcowy, to może istnieć początkowy i może istnieć ustalona kolejność.


Wgl nie zrozumiałeś dekoratorów.

Jedyne co musi istnieć to klasa która rozszerzasz, ona ma jakaś funkcjonalność w Twoim przypadku bazą jest Upload. I kiedy podczas tego uploadu zależy Ci żeby powstała miniaturka, wiec dodajesz do istniejącego obiektu Uploadu zrób mi miniaturkę. Dlatego tworzysz dekorator, którego jedynym zadaniem jest zmienić obiekt bazowy tak żeby zamiast pełnego obrazu zwrócona została miniaturka w jego miejscu.
Dla innego route chcesz miniaturkę obrócona o 180 stopni. Wiec bierzesz klase Upload bierzesz dekorator Thumbnail i dekorator Rotate i masz miniaturkę i obrócony. Ale nie ma znaczenia czy najpierw obrócisz potem zmniejszysz czy na odwrót i tak i tak efekt zawsze jest taki sam i tak powinno być u Ciebie, a tak nie jest bo u Ciebie każda klasa zależy od innej. W przypadku prawdziwego dekoratora nadawane ograniczenia/zmiany na obiekcie są od siebie nie zależne, wiec mogę mięć: miniaturkę; miniaturkę i obrócony == obrócony i miniaturka; obrócony, a wszystko to bez jakiejkolwiek zmiany w kodzie. Twoje początkowe założenie było blednę, czyli:
Cytat
Choćby zawsze musi istnieć końcowy dekorator

Nie ma czegoś takiego jak początkowy czy końcowy dekorator, jest obiekt który dekorujesz i jeśli dekoratory maja ten sam interface co ten obiekt, to można je ze sobą składać tak jak było opisane w przykładach, ale tylko i wyłącznie wtedy.

Cytat
Soryy, ale nie bede sobie "nieulatwial" pracy tylko dlatego ze ktos moze kiedys nie wiedziec jak tego uzyc...

Tu wcale nie chodzi o nie ułatwianie pracy, bo po to zaproponowane zostało użycie wzorców, żeby te rozwiązanie było jak najbardziej elastyczne i można było wykorzystać je w wielu projektach, ale wzorce zostały zdefiniowane w taki sposób, żeby każdy kto sporzy na kod, znając je wie jaki będzie efekt. A kiedy kolega Omenomn po roku czy dwóch latach wróci do tego kodu coś zmienić, najpierw znów będzie musiał wdrożyć sam siebie jak to działa i czemu tak to zrobiłem wtedy a nie inaczej.

Przeczytaj jeszcze raz uważnie co tam zostało napisane
http://blogophp.com/2009/08/16/dekorator/#more-40
Najistotniejsze fragmenty
Cytat
// obiekty tej klasy beda dekorowane
class SimpleText extends Information

Cytat
Proszę zwrócić uwagę na fakt, iż gdy obiekt dekorowany jest tego samego typu co dekoratory jesteśmy w stanie ?dekorować? również dekoratory


obiekt dekorowany !== dekorator i nie możesz go nazwać dekoratorem końcowym.

Pomijając już sam fakt, że tak jak tam było:
new FirstLetter(new TextLength(new SimpleText()));
Dekoratory idą od środka w górę, a u Ciebie odnoszę wrażenie, że idziesz od początku w dół, przynajmniej tak wynikało z pierwszego opisu.

Trochę wracając jeszcze do tematu frameworka i jego roli, o czym mówiliśmy wcześniej to przykład od olx:
solificati
Cytat(Omenomn @ 11.01.2017, 02:46:22 ) *
Nieprawda, znajdź mi informację, że w dekoratorze kolejność dekoratorów ma nie mieć znaczenia.
Choćby zawsze musi istnieć końcowy dekorator, który nie przyjmuje wartości do konstrukora, więc jeżeli musi istnieć końcowy, to może istnieć początkowy i może istnieć ustalona kolejność.

Principle of least astonishment jak to się w kulturze OOP nazywa. Gdy kolejność ma znaczenie to wprowadzasz składnię a to się liczy conajmniej jako "zaskoczenie".

Precyzując, Dekorator implementuje ten sam interfejs, który implementuje obiekt który jest dekorowany. Żeby dekorator mógł korzystać z pól dodanych przez inny dekorator, musisz wprowadzić drugie drzewo dekoratorów bazujące na poszerzonym interface albo ich produkcie.
daro0
Jeszcze rok albo dwa lata temu przy wcześniejszych projektach to też myślałem że to co wtedy pisałem jest super. Jakże się myliłem, bo teraz jak na to popatrzę to aż szlak trafia bo można było to napisać inaczej. Ale dalej. Weźmy np. nie uploader ale StaticServer

  1. class Controller_Image extends Controller
  2. {
  3.  
  4. public function action_serve()
  5. {
  6. $image = Image::factory(DOCROOT . '/test.jpg');
  7.  
  8. $size = $this->request->query('size');
  9. $rotate = $this->request->query('rotate');
  10. $sharp = $this->request->query('sharp');
  11. $quality = $this->request->query('quality');
  12.  
  13. if ($size)
  14. {
  15. $width = $image->width;
  16. $height = $image->height;
  17. $image->resize($width * $size / 100, $height * $size / 100);
  18. }
  19.  
  20. if ($rotate)
  21. {
  22. $image->rotate($rotate);
  23. }
  24.  
  25. if ($sharp)
  26. {
  27. $image->sharpen($sharp);
  28. }
  29.  
  30. if ($quality)
  31. {
  32. $image = $image->render('jpg', $quality);
  33. }
  34.  
  35. $this->response->headers('Cache-Control', 'max-age=3600, public');
  36. $this->response->headers('Content-Type', 'image/jpg');
  37. $this->response->body($image);
  38. }
  39.  
  40. }


I tak:

http://localhost/image/serve - wyświetla oryginalny obrazek JPG który wczyta
http://localhost/image/serve?size=50 - wyświetla obrazek w rozmiarach 50% oryginału
http://localhost/image/serve?size=50&sharp=100 - to samo ale wyostrzone
http://localhost/image/serve?quality=10 - jakość JPG 10%
http://localhost/image/serve?rotate=90 - obraza o 90 stopni w prawo

Domyślnie metoda __toString() wywołuje na render() w tym module.

I teraz jest pytanie, co w takim przypadku daje zastosowanie dekoratora (bo też można by to zrobić) vs. to podejście jakie jest tu?
Omenomn
daro0 seriooo?questionmark.gif Ten kod jest naprawdę kiepski...
to podejście jest nawet gorsze od tego, które chciałem poprawić.

com

przykładowy dekorator
  1. $decorator = new Decorator( new Decorator1( new Decorator2( new Decorator3);
  2. $decorator->make($decorationObject);


Widzisz, że Decorator3 nie przyjmuje obiektu?
Chcesz mi tłumaczyć coś, czego sam nie kumasz i jeszcze wmawiasz mi, że tego nie rozumiem.
Pyton_000
@Omenomn weź w końcu przeczytaj co to jest Decorator i przestań się upierać przy czymś o czym nie masz pojęcia bo aż szkoda.
Omenomn
Szlag mnie trafi:
http://blogophp.com/2009/08/16/dekorator/#more-40

Cytat
Jeśli chcemy zezwolić na wyświetlanie na wyjściu wiadomości, których długość przekracza pięć znaków wystarczy zapis:

  1. $obj = new TextLength(new SimpleText());
  2. $obj->Write('Napis');

Warunki mogą być łatwo dołączane jak zaprezentowano poniżej:

  1. // wyswietli napis gdy bedzie on mial wiecej niz
  2. // 5 znakow a pierwszym bedzie litera A.
  3. $obj = new FirstLetter(new TextLength(new SimpleText()));
  4. $obj->Write('Napis');

W ten prosty sposób możemy dowolnie nakładać ograniczenia na parametr $message. Proszę zwrócić uwagę na fakt, iż gdy obiekt dekorowany jest tego samego typu co dekoratory jesteśmy w stanie ?dekorować? również dekoratory ? tak właśnie dzieje się w powyższym przykładzie. Zachęcam do korzystania z tego wzorca.


Cytat z linku, widzisz, żeby SimpleText przyjmowała obiekt? Więc chyba musi być ostatnia, no nie?questionmark.gifquestionmark.gifquestionmark.gif?
Proste jak konstrukcja cepa.
Najwyraźniej nie dla wszystkich.
Pyton_000
SimpleText nie jest ostatnim a pierwszym. Na dodatek nie jest dekoratorem a Obiektem bazowym który jest DEKOROWANY a NIE jest DEKORATOREM.
com
Omenomn Specjalnie Ci już podkreśliłem fragmenty z tego bloga, ale zamiast przeczytać ze zrozumieniem uparłeś się na swoje rozwiązanie. Przeczytaj jeszcze raz co ja napisałem i co napisał Pyton_000 i można zakończyć te dyskusję.

Ostatecznie dekoratory bez klasy bazowej nie istnieją bo nie maja co dekorować, dlatego zawsze musi być jakaś baza tak jak tam jest to SimpleText, który nie jest dekoratorem, nim są dopiero TextLength oraz FirstLetter. Źle przeczytałeś i potem dobudowałeś sobie do tego teorie, zdarza się smile.gif SimpleText możesz zawsze wywołać wgl bez dekoratorów i spełni swoje podstawowe zadanie, dekoratora nie da się bo nie będzie klasy która dekoruje i tyle.

  1. $obj = new Decorator( new Decorator1( new Decorator2( new ObjectToDecorate);
  2. $obj->make($object);


Tak miał wyglądać Twój kod dla Twojego przykładu wink.gif
Omenomn
Okej, teraz widzę, że w tych tutorialach oni ostatni dekorator przekazują jako obiekt do dekorowania.

Jednak klasa SimpleText musi implementować interfejs dekoratora, wymagany w konstruktorze dekoratora TextLength, więc jeśli implementuje interfejs dekoratora to też nim jest, według mojego rozumowania.

Pierwszy dekorator jaki wywołuje metodę write() to FirstLetter, więc jest pierwszy, a ostatnia wywołana metoda jest z SimpleText.

  1. $obj = new Decorator( new Decorator1( new Decorator2( new ObjectToDecorate);
  2. $obj->make($object);


Co przekazujesz w tym kodzie do zmiennej $object? Nie widzisz tu błędu logicznego?

Teorycznie metoda make() może nie przyjmować parametru i wtedy pracujesz na sztywnych danych z ObjectToDecorate(), ale według mnie lepiej jest przekazać do metody make($object), obiekt do obróbki a klasę ObjectToDecorate (w tym przypadku nazwa błędna) potraktować jako ostatni krok obróbki obiektu $object.

Takie podejście wydaje mi się o wiele sensowniejsze.

Po za tym co to ma do rzeczy, że jest w dekoratorze ustalona kolejność. Może być tak, że któryś z dekoratorów zmieni dane, które otrzymał w taki sposób, że dekorator przed nim nie byłby w stanie ich obsłużyć, jeśli wystąpi po nim.

Wymyślanie problemów tam gdzie ich nie ma.
daro0
Omenomn
Tym razem nie Kohana i nie wiem czy jest to do końca poprawnie ale podczas pisania tego kodu myślałem że zwariujęexclamation.gif! Dosłownie.

  1. <?php
  2.  
  3. define('DOCROOT', realpath(dirname(__FILE__)).DIRECTORY_SEPARATOR);
  4.  
  5. interface IDecoratedImage
  6. {
  7. public function render();
  8. }
  9.  
  10. class Driver
  11. {
  12. protected $file;
  13. protected $resource;
  14.  
  15. public function __construct($file)
  16. {
  17. $this->file = $file;
  18. $this->resource = imagecreatefromjpeg($this->file->getRealPath());
  19. }
  20.  
  21. public function __destruct()
  22. {
  23. imagedestroy($this->resource);
  24. }
  25.  
  26. public function getResource()
  27. {
  28. return $this->resource;
  29. }
  30. }
  31.  
  32. class Image implements IdecoratedImage
  33. {
  34. protected $driver;
  35. protected $resource;
  36.  
  37. public function __construct($driver)
  38. {
  39. $this->driver = $driver;
  40. $this->resource = $this->driver->getResource();
  41. }
  42.  
  43. public function render()
  44. {
  45. imagejpeg($this->resource);
  46. }
  47.  
  48. public function getResource()
  49. {
  50. return $this->resource;
  51. }
  52. }
  53.  
  54. class GrayscaleImage implements IDecoratedImage
  55. {
  56. protected $image;
  57.  
  58. public function __construct($image)
  59. {
  60. $this->image = $image;
  61. }
  62.  
  63. public function render()
  64. {
  65. $resource = $this->image->getResource();
  66. imagefilter($resource, IMG_FILTER_GRAYSCALE);
  67. imagejpeg($resource);
  68. }
  69.  
  70. public function getResource()
  71. {
  72. return $this->resource;
  73. }
  74.  
  75. }
  76.  
  77. class NegateImage implements IDecoratedImage
  78. {
  79. protected $image;
  80.  
  81. public function __construct($image)
  82. {
  83. $this->image = $image;
  84. }
  85.  
  86. public function render()
  87. {
  88. $resource = $this->image->getResource();
  89. imagefilter($resource, IMG_FILTER_NEGATE);
  90. imagejpeg($resource);
  91. }
  92. }
  93.  
  94.  
  95. header('Cache-Control: max-age=3600, public');
  96. header('Content-Type: image/jpeg');
  97.  
  98. $path = DOCROOT . 'test.jpg';
  99.  
  100. // Display original image
  101. //$image = new Image(new Driver(new SplFileInfo($path)));
  102.  
  103. // Display decorated grayscale image
  104. //$image = new GrayscaleImage(new Image(new Driver(new SplFileInfo($path))));
  105.  
  106. // Display decorated negative image
  107. $image = new NegateImage(new Image(new Driver(new SplFileInfo($path))));
  108. $image->render();


Też chodzi o ładowanie JPEG oraz przy użyciu jak (poprawnie?questionmark.gif) myślę dekorowania bazowego Image tak żeby uzyskać efekty w odcieniach szarości albo nagatywu obrazu. W takim razie odpowiedz mi na pytanie: po co tyle zabawy?questionmark.gif? Mam jeszcze dekorować ten udekorowany obiekt w ten sposób:

  1. $image = new GrayscaleImage(new NegateImage(new Image(new Driver(new SplFileInfo($path)))));
  2. $image->render();


A co z dodatkowymi opcjami? Albo jeszcze takie coś:

  1. $image = new SharpenImage(new GrayscaleImage(new NegateImage(new Image(new Driver(new SplFileInfo($path))))));
  2. $image->render();


vs. to:

  1. $image = Image::factory(DOCROOT . 'test.jpg')
  2. ->grayscale()
  3. ->negate()
  4. ->sharpen()
  5. ->render();


Co też moim zdaniem można zrobić bardzo prosto w tylko jednej klasie operując na resource i tych wbudowanych funkcjach z modułu GD.
Pyton_000
single responsibility principle - słyszał?
Omenomn
Cytat
  1. $image = Image::factory(DOCROOT . 'test.jpg')
  2. ->grayscale()
  3. ->negate()
  4. ->sharpen()
  5. ->render();

Co też moim zdaniem można zrobić bardzo prosto w tylko jednej klasie operując na resource i tych wbudowanych funkcjach z modułu GD.


Wydaje mi się, że o wiele gorzej z dodawaniem kolejnych funkcji przy takim rozwiązaniu, bo w końcu klasa może urosnąć do gigantycznych rozmiarów z ogromną ilością metod, a właśnie ten problem chciałem wyeliminować.
Trzeba pamiętać o tym, że to ma obsługiwać różne pliki, nie tylko obrazy, ale też wideo.

Czyli właśnie zasada jednej odpowiedzialności jest tu wykluczona, bo jeśli jeszcze można by ją odnieść do obrazu, to nie odniesie się tego do uploadu filmu z wygenerowaniem trzech miniaturek.

Po za tym istnieje taka klasa do obróbki obrazów jaką przedstawiasz daro0:
http://image.intervention.io/

i Ja ją wykorzystuję właśnie w moim dekoratorze.
mrc
@Omenomn

Męczysz się chłopak niemiłosiernie. Przeczytaj i przeanalizuj to, co jest napisane w Czystym Kodzie + SOLID. Sam kiedyś tak kombinowałem jak Ty. Tak jak ktoś już wcześniej napisał - uzbrój się w pokorę, przeczytaj, i zastanów się nad sensem tego, co tam jest napisane. Nie bez powodu jest to best seller.
Omenomn
Przeczytam na pewno.

Ps.
Nie męczę się, bo już to jest gotowe i dobrze działa.

Tak sobie ogólnie pomyślałem teraz, że tak jak backend tworzy się pod frontend, tak bebechy bibliotek do obsługi jakichś zadań powinno się tworzyć pod sposób ich wywoływania. Czyli najpierw zastanowić się trzeba jak chce się korzystać z danej biblioteki, jak ją wywoływać, a później dopiero tworzyć jej wnętrze.
com
Dalsza dyskusja nie ma sensu wiec na tym poście zakończę.
Cytat
Okej, teraz widzę, że w tych tutorialach oni ostatni dekorator przekazują jako obiekt do dekorowania.

No bo taka jest definicja dekoratora. Masz obiekt do dekorowania który istniej zanim wgl ktokolwiek pomyśli o tym żeby stworzyć dekorator i masz dekoratory które dodają nowe funkcjonalności do klasy nie modyfikując jej.
Cytat
Jednak klasa SimpleText musi implementować interfejs dekoratora


Co za bzdury gadasz, to nie klasa która dekorujesz musi implementować interfejs dekoratora, a dekoratory interfejsy klasy która dekorują. Czyli zupełnie na odwrót.

Cytat
Teorycznie metoda make() może nie przyjmować parametru i wtedy pracujesz na sztywnych danych z ObjectToDecorate(), ale według mnie lepiej jest przekazać do metody make($object), obiekt do obróbki a klasę ObjectToDecorate (w tym przypadku nazwa błędna) potraktować jako ostatni krok obróbki obiektu $object.


Pracując na sztywnych danych jaki miałby sens dekorować klasę, skoro bez jej modyfikacji niczego nie jesteś wstanie zmienić. Podstawowym zadaniem które da się tu zrobić jest wykonanie takiego kodu:
  1. $obj = new ObjectToDecorate();
  2. $obj->make($object);

Nie potrzebuje mieć ani jednego dekoratora, żeby taki kod wywołać, bo on dostarcza to co daje mi interfejs.

To co Ty stworzyłeś nie można nazwać po prostu dekoratorem. Stworzyłeś własna implementacje jako rozwiązanie dla tego problem, bo to co Ty masz nawet obok dekoratora nie stało.

Tylko te rozwiązanie ma dwa podstawowe problemy, nie jest testowalne i skalowalne. Wraz z każdym dekoratorem wzrasta złożoność tego rozwiązania i uzależnienie jednego obiektu od drugiego.

Ale jedno muszę Ci przyznać wykazałeś się pomysłowością w wymyślaniu "koła" na nowo, bo otrzymałeś coś co w programowaniu istnieje od dawna pod nazwą dziedziczenie, tylko zamiast zrobić to tradycyjnie, stworzyłeś implementacje poprzez składanie obiektów. biggrin.gif
Omenomn
i tak głównie chodzi o warstwowe nakładanie klas new Class(new Class1( new Class2( itd.)))

To, czy Class2 traktujesz jako obiekt dekorowany, czy ostatni dekorator nie ma takiego znaczenia.

Cytat
Ale jedno muszę Ci przyznać wykazałeś się pomysłowością w wymyślaniu "koła" na nowo, bo otrzymałeś coś co w programowaniu istnieje od dawna pod nazwą dziedziczenie, tylko zamiast zrobić to tradycyjnie, stworzyłeś implementacje poprzez składanie obiektów.


To nie jest dziedziczenie tylko przypisanie, co czyni ogromną różnicę.

Cytat
Tylko te rozwiązanie ma dwa podstawowe problemy, nie jest testowalne i skalowalne. Wraz z każdym dekoratorem wzrasta złożoność tego rozwiązania i uzależnienie jednego obiektu od drugiego.


Jest testowalne. Przetestowałem, działa, chociaż jest dość zawiłe w implementacj, jeszcze nad tym popracuję

Cytat
Cytat
Jednak klasa SimpleText musi implementować interfejs dekoratora

Co za bzdury gadasz, to nie klasa która dekorujesz musi implementować interfejs dekoratora, a dekoratory interfejsy klasy która dekorują. Czyli zupełnie na odwrót.


Chodziło mi o to, że mają ten sam interfejs.

Widzę, że wszędzie piszą tak jak Ty tutaj, rzuciłem okiem na ten wzorzec i jakoś mi umknęło to, że ostatni obiekt ma być traktowany jako obiekt dekorowany, bo sensowniejsze dla mnie jest podejście, że obiekt dekorowany to ten który przekazujesz do make, jakoś tak podświadomie to potraktowałem:

  1. $obj = new ObjectToDecorate();
  2. $obj->make($object);


bo załóżmy przekazujemy tablicę $array do make($array)
  1. $array = [
  2. 'name' => 'plik.jpg',
  3. 'extension' => 'jpg',
  4. 'height' => 200,
  5. 'width' => 300,
  6. ]
  7. $obj = new ObjectToDecorate();
  8. $obj->make($array)


i teraz ObjectToDecorate może zmienić nazwę pliku, rozszerzenie i rozmiar, więc modyfikuje tablicę (dekoruje), która jest w tym momencie obiektem dekorowanym/modyfikowanym.

Dalsza dyskusja na temat dekoratora nie ma sensu naprawdę, bo rozumiem Twoje podejście, jednak niekoniecznie się z nim zgadzam.
com
Ponieważ zaczynasz bardziej sensownie pisać to jeszcze na to Ci odpowiem

Cytat
To, czy Class2 traktujesz jako obiekt dekorowany, czy ostatni dekorator nie ma takiego znaczenia.


Ma ogromne znaczenie, bo u Ciebie nie da się nawet wyciągnąć podstawowej funkcjonalności, może poza propertisami pliku ale to podobno ma być upload. To gdzie jest ten wspólny interfejs

Cytat
To nie jest dziedziczenie tylko przypisanie, co czyni ogromną różnicę.

W programowaniu obiektowym mocno upraszczając obiekty, mogą albo po sobie dziedziczyć, albo może nastąpić ich kompozycja. Ty zrobiłeś kompozycje, ale ponieważ wszystkie te twoje klasy dziedziczą po tym samym interfejsie to zasadniczo zrobiłeś ich rozszerzenie wiec defakto uzyskałeś to samo jakby zrobić :
Class : Class1 : Class2, efekt masz dokładnie identyczny, z tym że dla takiego podejścia bez problemu napiszesz testy automatyczne, dla Twojego już niekoniecznie.
Bo to co ty zrobiłeś:
Cytat
Jest testowalne. Przetestowałem, działa, chociaż jest dość zawiłe w implementacj, jeszcze nad tym popracuję


czyli test zero jedynkowy, działa nie działa to żaden dowód, że to zawsze tak będzie działać jakby się tego oczekiwało.

Cytat
Widzę, że wszędzie piszą tak jak Ty tutaj, rzuciłem okiem na ten wzorzec i jakoś mi umknęło to, że ostatni obiekt ma być traktowany jako obiekt dekorowany, bo sensowniejsze dla mnie jest podejście, że obiekt dekorowany to ten który przekazujesz do make


ten obiekt który przekazujesz do make nie jest wgl z żadna z klas związany, to jest po prostu model na którym operujesz. Ani dekoratory ani klasa która dekorujesz wgl nie musza istnieć, żeby istniał ten objekt, a to może być cokolwiek.

Cytat
i teraz ObjectToDecorate może zmienić nazwę pliku, rozszerzenie i rozmiar, więc modyfikuje tablicę (dekoruje), która jest w tym momencie obiektem dekorowanym/modyfikowanym.


Nadal nie rozumiesz co to jest dekorator, ten obiekt to model danych, jaki, taki jaki sobie wymarzysz. On wgl nie ma związku ze wzorcem dekorator.

Cytat
bo rozumiem Twoje podejście

No właśnie nie rozumiesz, uwierz, że gdybyś stworzył dekorator nie potrzebowałbyś obserwatora który śledzi zmiany w poszczególnych klasach.

Ja tego podejścia nie wymyśliłem, ja po prostu tłumacze Ci jak działa dekorator. Dekorator ma jedna jedyna definicje i jest to najprostszy z możliwych wzorców, ale jak się go nie zrozumie to wychodzi taki problem, że można uzyskać coś zupełnie innego.

a wystarczyło napisać to tak z użyciem dekoratorów skoro tak bardzo to chciałeś zrobić:

  1. $obj = new Thumbnail(new Upload());
  2. $obj->make($model);


No i które rozwiązanie jest bardziej akceptowalne te czy te:
  1. $image = new PropertyGeter(
  2. new ContentImageGeter(
  3. new ImageNoResizer(
  4. new Encoder(
  5. new FilePuter))));
  6. $image->make($request->file);


Wgl spójrz ile zależności i klas potrzebujesz żeby stworzyć cokolwiek.

Chcesz mieć rożne typy obsłużone, proszę bardzo:
  1. $obj0 = new Thumbnail(new Upload());
  2. $obj0->make($model0);
  3.  
  4. $obj = new Thumbnail(new ImageUpload());
  5. $obj->make($model);
  6.  
  7. $obj1 = new Thumbnail(new VideoUpload());
  8. $obj1->make($model1);
  9.  
  10. ...


Wiec nie:
  1. - zwykły obraz:
  2. $image = new Property(
  3. new ContentImage(
  4. new Image(
  5. new Encoder(
  6. new File))));
  7. $image->make($request->file);
  8. - miniaturka:
  9. $thumbnail = new Property(
  10. new ContentImage(
  11. new Thumbnail(
  12. new Encoder(
  13. new File))));
  14. $image->make($request->file);
  15. - video:
  16. $video = new Property(
  17. new Content(
  18. new Video(
  19. new File)));
  20. $video->make($request->file);
  21. - video z miniaturką
  22. $video = new Property(
  23. new ThumbnailFromVideo(
  24. new Content(
  25. new Video(
  26. new File))));
  27. $video->make($request->file);
  28. - video z trzema miniaturką
  29. $video = new Property(
  30. new ThumbnailFromVideo(
  31. new GetThumbnail(
  32. new ThumbnailFromVideo(
  33. new GetThumbnail(
  34. new ThumbnailFromVideo(
  35. new GetThumbnail(
  36. new Content(
  37. new Video(
  38. new File))));
  39.  
  40. lub
  41.  
  42. $video = new ThreeThumbnailsFromVideo(
  43. new Content(
  44. new Video(
  45. new File))));
  46. $video->make($request->file);

A :
  1. - zwykły obraz:
  2. $obj = new ImageUpload(); // objekt który może zostać udekorowany lub nie
  3. $obj->upload($request->file);
  4. - miniaturka:
  5. $obj = new Thumbnail(new ImageUpload());
  6. $obj->upload($request->file);
  7. - video:
  8. $obj1 = new VideoUpload(); // objekt który może zostać udekorowany lub nie
  9. $obj1->upload($request->file);
  10. - video z miniaturką:
  11. $obj1 = new Thumbnail(new VideoUpload());
  12. $obj1->upload($request->file);
  13. - video z trzema miniaturkami:
  14. $obj1 = new ThreeThumbnails(new VideoUpload());
  15. $obj1->upload($request->file);


Chcesz mieć jakiś Encoder, nie ma problemu
  1. - zwykły obraz:
  2. $encoder = new DummyEncoder();
  3. $obj = new ImageUpload($encoder); // objekt Encoder wstrzykiwany jako zależność czyli DI
  4. $obj->upload($request->file);
Omenomn
Napisałeś praktycznie to samo co Ja, z tym, że u mnie jest rozbite na więcej mniejszych kroków...

Zdecyduj się, czy klasa ImageUpload pobiera parametr do konstruktora czy nie.

  1. $obj = new ImageUpload(); // objekt który może zostać udekorowany lub nie
  2. $obj->upload($request->file);

  1. $encoder = new DummyEncoder();
  2. $obj = new ImageUpload($encoder); // objekt Encoder wstrzykiwany jako zależność czyli DI
  3. $obj->upload($request->file);


Poniżej jest pomniejszanie obrazu, czy generowanie obrazu z miniaturką?
  1. $obj = new Thumbnail(new ImageUpload());
  2. $obj->upload($request->file);


Jak pobierzesz w Thumbnail informacje o pliku, skoro ImageUpload to klasa bazowa, która za pewne pobiera te dane?
Chcesz w każdej klasie pobierać dane o pliku?

Moje rozwiązanie startuje od klasy PropertyGet, żeby pobrało informacje o pliku raz, a później na nich bazowało.
Gdzie pobierasz kontent pliku, gdzie go enkodujesz, jak chciałbyś zrobić zmianę nazwy?

Taki uproszczony schemat to Ja też mogłem napisać w poście, ale jest bezużyteczny.
com
Cytat
Napisałeś praktycznie to samo co Ja, z tym, że u mnie jest rozbite na więcej mniejszych kroków...


No właśnie, że nie bo ja nie potrzebuje u mnie nic więcej poza klasa bazowa i jej interfejsem, żeby stworzyć dowolny dekorator teraz.

Cytat
Zdecyduj się, czy klasa ImageUpload pobiera parametr do konstruktora czy nie.


To nie ma znaczenia, jeśli potrzebujesz dodatkowe zależności to je dodajesz, co pokazałem, ale do tego jest inny wzorzec.

Cytat
Poniżej jest pomniejszanie obrazu, czy generowanie obrazu z miniaturką?


Generowanie miniaturki, ale wystarczy zmienić ten dekorator na inny i masz inne zadanie.

Cytat
Jak pobierzesz w Thumbnail informacje o pliku, skoro ImageUpload to klasa bazowa, która za pewne pobiera te dane?
Chcesz w każdej klasie pobierać dane o pliku?


Ale do czego Ci są potrzebne te dane? Zrobienie miniaturki nie wymaga niczego więcej jak tylko pliku na którym ma zostać to wygenerowane.

Cytat
Taki uproszczony schemat to Ja też mogłem napisać w poście, ale jest bezużyteczny.


To nie jest uproszczony schemat tylko pełna funkcjonalność taka sama jak u Ciebie.

Implementacja:
  1. class ImageUpload : UploadInterface
  2. {
  3. private $encoder;
  4. private $renamer;
  5.  
  6. public function __construct (EncoderInterface $encoder, RenamerInterface $renamer)
  7. {
  8. $this->encoder = $encoder;
  9. $this->renamer = $renamer;
  10. }
  11.  
  12. public function upload($file)
  13. {
  14. // tutaj jakieś operacje na pliku typu enkodowanie, zmiany nazwy itd
  15. (...)
  16.  
  17. return $encodedAndRenamedAndEtcFile;
  18. }
  19. }
  20.  
  21. class GenerateWithThumbnail : UploadInterface
  22. {
  23. private $base;
  24.  
  25. public function __construct (UpoladInterface $upload)
  26. {
  27. $this->base = $upload;
  28. }
  29.  
  30. public function upload($file)
  31. {
  32. // tutaj wygenerowanie miniaturki
  33. (...)
  34.  
  35. $this->base->upload($fileWithThumbnail);
  36. }
  37. }
  38.  
  39. // Wywołanie
  40. $encoder = new DummyEncoder();
  41. $renamer = new DummyRenamer();
  42. $obj = new GenerateWithThumbnail(new ImageUpload($encoder, $renamer));
  43. $obj->upload($request->file);
  44.  


Cytat
Poniżej jest pomniejszanie obrazu, czy generowanie obrazu z miniaturką?


Ok nazwa była zła zmienię na GenerateWithThumbnail
Omenomn
To co zrobiłeś jest sztywne i kompletnie nie rozszerzalne, chyba, że za każdym razem jak chcę coś dodać, będę musiał modyfikować konstruktor klasy ImageUpload i jej metodę upload() co jest bez sensu, dodatkowo wtedy musiałbym modyfikować kod wszędzie tam gdzie ta klasa jest używana.

Cytat
Ale do czego Ci są potrzebne te dane? Zrobienie miniaturki nie wymaga niczego więcej jak tylko pliku na którym ma zostać to wygenerowane.


Wegenerowanie pliku wymaga:
- nazwy pliku,
- ścieżki
- kontentu wygenerowanego ze ścieżki
- ścieżki na którą ma zostać zapisany.

Wszystkie te dane należy wyciągnąć z pliku, żeby go przesłać, oprócz ścieżki do zapisu.

Jeżeli ImageUpload pobiera te dane, to w jaki sposób dostanie je GenerateWithThumbnail, żeby mogło zmodyfikować plik i w jaki sposób prześle zmodyfikowany plik do ImageUpload. Pliku z requesta nie da się modyfikować, można co najwyżej zapisać zmodyfikowane dane o pliku i przesłać je dalej do obróbki, więc tak czy siak format przesyłanych danych się zmienia, a klasa bazowa ImageUpload musi je przyjąć.

Kod
        $propeties->name = $file->getClientOriginalName();
        $propeties->extension = $file->getClientOriginalExtension();
        $propeties->path = $file->getRealPath();
        $propeties->disk = '/uploadFolder/';


Jeżeli te dane wyciągniesz z file w ImageUpload to w jaki sposób dostanie je GenerateWithThumbnail i w jaki sposób encoder i renamer przekaże dane do GenerateWithThumbnail ze zmienioną nazwą i zmienionym rozszerzeniu.
Najpierw powinna zostać wygenerowana miniatruka później plik bazowy, bo co jeśli chcę po prostu pomniejszyć plik, a nie generować dwóch? Najpierw muszę go pomniejszyć, a później wygenerować.

Natomiast jeżeli masz podstawową kompozycję z trzech klas:

Kod
$file = new GetProperty(new GetContent(new Put)));
$file->make($request->file);


to wtedy bez problemowo można tworzyć nakładki bez jakichkolwiek modyfikacji wnętrzna klas już istniejących, przykładowo:

Kod
$file = new GetProperty(new Rename(new ToThumbnailResize(new GetContent(new Put))));
$file->make($request->file);

lub
Kod
$file = new GetProperty(new Rename(new GenerateWithThumbnail(new GetSizeOfImage(new PutToSission(new GetContent(new Put))))))));
$file->make($request->file);


w ten sposób bazę trzech klas można rozszerzać jak się chce, trzymając się jedynie kolejności klas bazowych, bo taki kod też będzie działał:
Kod
$file = new GetProperty(new PutToSession(new GenerateWithThumbnail(new GetSizeOfImage(new Rename(new GetContent(new Put))))))));
$file->make($request->file);


tylko, że do sesji nie zostanie zapisana informacja o miniaturce, bo jest wygenerowana po zapisaniu danych do sesji.

Jest to o wiele bardziej elastyczne rozwiązanie niż Twoje według mnie, nadbudowujące kod, a nie modyfikujące go.

Teraz tak sobie pomyślałem, że w sumie do dekoratora nie musi być plik przekazywany, a może być obiekt:
Kod
$object->name;
$object->extension;
$object->content
$object->saveDisk


i wtedy klient dziedziczący po klasie abstrakcyjnej metodę do utworzenia takiego obiektu wywoływałby dekorator.
Kod
$client = new Image
$client->make($request->file);


Klient pobiera plik, a w metodzie make, lub w konstruktorze klienta byłoby

Kod
class Image extends ClientAbstract
{
protected $dekorator;
public function __construct()
{
$this->decorator = new ImageUpload();
}

public function make($file)
{
$object = stdClass;
$object->name = $file->getName();
$object->content= $file->getContent();
$object->saveDisk= '/saveDisk/;

$this->decorator->make($object);
}
}


i wtedy kolejność dekoratorów nie ma żadnego znaczenia, bo można zrobić sam:
Kod
new ImageUpload()
lub
Kod
new Rename(new ImageUpload());


Kod
new Rename(new Resize(new ImageUpload)))


Co sądzicie?
rafkon1990
Cytat(Omenomn @ 13.01.2017, 13:03:39 ) *
...
Co sądzicie?

Sądzę że twoje posty są zbyt chaotyczne i trudne do zrozumienia, często zmieniasz tok myślowy a ponadto masz problem ze zrozumieniem wzorca dekorator.
Omenomn
Wydaje Ci się biggrin.gif
kapslokk
A mnie zastanawia, po co w ogóle zadajesz pytania @Omenomn, skoro później olewasz wszystkie odpowiedzi upierając się przy swoim? Zupełnie jak tutaj: http://forum.php.pl/index.php?showtopic=243138 :/
Omenomn
To się zastanawiaj dalej, możemy gadać o php, a nie o mnie i moim podejściu do Waszych wypowiedzi.

Przedstawiłem nowy pomysł rozwiązania, więc się do niego odnieście, a nie do mnie.

Co Wy jesteście, wyrocznia, że mam Wasze oceny i opinie przyjmować jako jedyne prawdziwe i niezaprzeczalne?
Dyskutować można, a to co się wyciągnie z dyskusji to prywatna sprawa każdego z osobna.

Tacy świetni jesteście, a jak com, albo daro0 wrzucili swój kod, to się okazuje, że gorzej przemyślany niż mój.
com
Cytat
To co zrobiłeś jest sztywne i kompletnie nie rozszerzalne, chyba, że za każdym razem jak chcę coś dodać, będę musiał modyfikować konstruktor klasy ImageUpload i jej metodę upload() co jest bez sensu, dodatkowo wtedy musiałbym modyfikować kod wszędzie tam gdzie ta klasa jest używana.


Nie ma takiej potrzeby bo klasa ImageUpload nigdy się nie zmieni, po to masz przecież dekorator żeby dodać do niej nowe funkcjonalności bez zmiany tej klasy. Te parametry które tam dostaje w konstruktorze one są stałe i nie maja znaczenia, równie dobrze możesz tam przekazać sam Encoder, a Renamer może być dekoratorem, bo w zasadzie zmieniasz źródło danych. Ja po prostu założyłem , że to jest podstawowa funkcjonalność i te kroki są wykonywane zawsze. Klasa mogła nie otrzymywać wgl parametrów i wszystko to dziać się w jej wnętrzu ale wtedy łamiesz zasady programowania obiektowego.

Cytat
Wegenerowanie pliku wymaga:
- nazwy pliku,
- ścieżki
- kontentu wygenerowanego ze ścieżki
- ścieżki na którą ma zostać zapisany.

Wszystkie te dane należy wyciągnąć z pliku, żeby go przesłać, oprócz ścieżki do zapisu.


No i co mi po tych danych, owszem potrzebuje je w klasie ImageUpload, która zapisuje mi przesłany plik, ale to już nie interesuje GenerateWithThumbnail, bo jego jedynym zdaniem jest stworzenie miniaturki, wiec do czego tu potrzebne są te inne informacje.

Cytat
Jeżeli te dane wyciągniesz z file w ImageUpload to w jaki sposób dostanie je GenerateWithThumbnail i w jaki sposób encoder i renamer przekaże dane do GenerateWithThumbnail ze zmienioną nazwą i zmienionym rozszerzeniu.
Najpierw powinna zostać wygenerowana miniatruka później plik bazowy, bo co jeśli chcę po prostu pomniejszyć plik, a nie generować dwóch? Najpierw muszę go pomniejszyć, a później wygenerować.


Jak chcesz coś pomniejszyć skoro to coś nie istnieje. Najpierw musisz mieć coś co będziesz zmniejszał a potem możesz to zmniejszyć, a nie najpierw zmniejszyć a potem dopiero to stworzyć.

Sadze, że nadal nie stworzyłes dekoratora bo nie rozumiesz dalej czym on jest i rozwiązanie które powstało jest strasznie nie elastyczne i wgl nie testowalne.

Cytat
Tacy świetni jesteście, a jak com, albo daro0 wrzucili swój kod, to się okazuje, że gorzej przemyślany niż mój.


Ja wrzuciłem kod dekoratora bo się na niego uparłeś, wiec skoro według Ciebie rozwiązanie jest złe to znaczy że dekorator nie był dobrym pomysłem i tyle

Implementacja wersja 2:
  1. class ImageUpload : UploadInterface
  2. {
  3.  
  4. public function save($file)
  5. {
  6. // tutaj zapis pliku
  7. (...)
  8. return true;
  9. }
  10. }
  11.  
  12. class GenerateWithThumbnail : UploadInterface
  13. {
  14. private $base;
  15.  
  16. public function __construct (UpoladInterface $upload)
  17. {
  18. $this->base = $upload;
  19. }
  20.  
  21. public function save($file)
  22. {
  23. // tutaj wygenerowanie miniaturki
  24. (...)
  25.  
  26. $this->base->save($fileWithThumbnail);
  27. }
  28. }
  29.  
  30. class Encode : UploadInterface
  31. {
  32. private $base;
  33.  
  34. public function __construct (UpoladInterface $upload)
  35. {
  36. $this->base = $upload;
  37. }
  38.  
  39. public function save($file)
  40. {
  41. // tutaj encodowanie pliku
  42. (...)
  43.  
  44. $this->base->save($encodedFile);
  45. }
  46. }
  47.  
  48. class Renamer : UploadInterface
  49. {
  50. private $base;
  51.  
  52. public function __construct (UpoladInterface $upload)
  53. {
  54. $this->base = $upload;
  55. }
  56.  
  57. public function save($file)
  58. {
  59. // tutaj zmiana nazwy pliku
  60. (...)
  61.  
  62. $this->base->save($renamedFile);
  63. }
  64. }
  65.  
  66. // Wywołanie
  67. $obj = new Encode(new Renamer(new GenerateWithThumbnail(new ImageUpload())));
  68. $obj->save($request->file);
  69.  
  70. $obj1 = new Renamer(new Encode(new GenerateWithThumbnail(new ImageUpload())));
  71. $obj1->save($request->file);
  72.  
  73. // $obj === $obj1; => true
Omenomn
Cytat
No i co mi po tych danych, owszem potrzebuje je w klasie ImageUpload, która zapisuje mi przesłany plik, ale to już nie interesuje GenerateWithThumbnail, bo jego jedynym zdaniem jest zmiana rozmiaru obrazka, do czego tu potrzebne są te inne informacje.


żeby zmienić rozmiar obrazka, musisz przekazać kontent pliku klasie GenerateWithThumbnail

Cytat
Jak chcesz coś pomniejszyć skoro to coś nie istnieje. Najpierw musisz mieć coś co będziesz zmniejszał a potem możesz to zmniejszyć, a nie najpierw zmniejszyć a potem dopiero to stworzyć.


Przed uploadem plik znajduje się folderze z plikami tymczasowymi, jak nie wiesz takich rzeczy to o czym my rozmawiamy w ogóle? Na tym pliku się operuje, chyba, że chcesz najpierw go wysłać, a później obrabiać, co jest bez sensu, bo można to zrobić przed wysłaniem.

Cytat
Te parametry które tam dostaje w konstruktorze one są stałe i nie maja znaczenia, równie dobrze możesz tam przekazać sam Encoder, a Renamer może być dekoratorem, bo w zasadzie zmieniasz źródło danych.


W ogóle parametry w konstruktorze nie mają znaczenia, zupełnie...

Cytat
Sadze, że nadal nie stworzyłes dekoratora bo nie rozumiesz dalej czym on jest i rozwiązanie które powstało jest strasznie nie elastyczne i wgl nie testowalne.


Jest elastyczne, bo można nakładać warstwy jak się chce i jakie się chce, a klasa ImageUpload działa samoistnie bez żadnego dekoratora, tak jak chciałeś i oczywiście jest testowalne, bo znowu przetestowałem i działa w różnych konfiguracjach.

Dajmy sobie spokój, bo to co przedstawiasz po prostu nie może działać, jest nieścisłe, ogólne, nierozszerzalne i nietestowalne i proszę Cię skończ mi wmawiać, że nie rozumiem dekoratora.
com
Cytat
żeby zmienić rozmiar obrazka, musisz przekazać kontent pliku klasie GenerateWithThumbnail


Niczego nie musisz przekazywać bo ta klasa otrzyma surowy plik.

Cytat
Przed uploadem plik znajduje się folderze z plikami tymczasowymi, jak nie wiesz takich rzeczy to o czym my rozmawiamy w ogóle? Na tym pliku się operuje, chyba, że chcesz najpierw go wysłać, a później obrabiać, co jest bez sensu, bo można to zrobić przed wysłaniem.


No wiec ten plik istnieje w tempie, a nie Ty go dopiero tworzysz potem.

Cytat
W ogóle parametry w konstruktorze nie mają znaczenia, zupełnie...


No nie mają, bo to są zależności potrzebne klasie ImageUpload, tam może być cokolwiek

Cytat
Jest elastyczne, bo można nakładać warstwy jak się chce i jakie się chce, a klasa ImageUpload działa samoistnie bez żadnego dekoratora, tak jak chciałeś i oczywiście jest testowalne, bo znowu przetestowałem i działa w różnych konfiguracjach.


Tak ale tylko w ściśle określonej kolejności i żadnego nie można pominąć. Zacznijmy od tego, że nawet nie wiesz co są testy, wiec nie rozumiesz tego, że to nie działa. Masz nieskończona ilość kombinacji i co przetestowałeś je wszystkie, nie, zrobiłeś testy działa nie działa, a to nie jest testowalność.

Cytat
Dajmy sobie spokój, bo to co przedstawiasz po prostu nie może działać, jest nieścisłe, ogólne, nierozszerzalne i nietestowalne i proszę Cię skończ mi wmawiać, że nie rozumiem dekoratora.


No nie rozumiesz bo mimo wielu prób dekorator nie powstał, ale tak zakończmy to, wróć za parę lat z doświadczeniem smile.gif
Omenomn
Nie kpij sobie, doświadczeniem nie nadrobisz kumatości.
Dakorator powstał. Mam to gdzieś, czy nazwiesz to dekoratorem, modyfikatorem, warstwami, czy jeszcze jakoś inaczej, ważne, że porządkuje kod.
com
Cytat
Nie kpij sobie, doświadczeniem nie nadrobisz kumatości.
Dakorator powstał. Mam to gdzieś, czy nazwiesz to dekoratorem, modyfikatorem, warstwami, czy jeszcze jakoś inaczej, ważne, że porządkuje kod.


Owszem nie nadrobisz, ale to nie ja nie kumam jak działa dekorator tylko Ty go nie zrozumiałeś.
A twój kod nie został uporządkowany, a bardziej zaciemniony niż jakbyś zrobił po prostu wszytko w kontrolerze i tyle wink.gif

Omenomn
Szaleństwo! Najlepiej piszmy apki w jednym pliku i jednej klasie biggrin.gif
com
Zróbmy tak, wrzuć całą swoja implementacje na gita i daj linka. Specjalnie dla Ciebie zrobię z tego dekorator smile.gif

Cytat
Najlepiej piszmy apki w jednym pliku i jednej klasie


Bardzo skrajne rozwiązanie, ale istnieje takie coś jak

Zasada Keep It Simple, Stupid (czyli tłumacząc: nie komplikuj, głupku) jest to reguła mówiąca, że nie należy na siłę komplikować prostych rzeczy, aby przykładowo wykorzystać niepotrzebne w danym momencie wzorce projektowe.
Omenomn
Ok wrzucę, może ktoś się czegoś nauczy.

https://github.com/omenomn/uploadDecorator

Masz, ciekawe jak to poprawisz.
daro0
@Omenomn

Zanim zaczniesz jechać po czyimś kodzie czy tam w ogóle podejściu (i nie mówię o swoim) to tak:

1. Napisz jakiś serwis pokroju tego forum albo coś innego, co się wiąże z kilkumiesięcznym nakładem pracy
2. Napisz kolejny serwis za rok i wróć do kodu sprzed roku
3. Napisz coś kolejnego za 2 lata, za 3 itd.. i znowu wróć

Gwarantuję Ci że będziesz miał niesmak po tym co zrobiłeś wcześniej. Akurat @com przedstawił tu dość klarowne rozwiązanie, łatwe do zrozumienia a konkretniej chodzi o to na czym polega i co się dzieje po zastosowaniu wzorca dekorator.

Podałem tylko wcześniej pewną realizację wczytywania zdjęć modułem Image z Kohany i ich obróbkę na podstawie parametrów z request query, podaj może tutaj praktyczną realizację jak byś zrobił taki static server, który w zależności od parametrów z paska przeglądarki wyświetlałby odpowiednio udekorowane zdjęcia na bazie swojego rozwiązania.
Omenomn
Po moim jadą wszyscy cały czas biggrin.gif

Wrzuciłem link na github.
Coś długo te poprawki biggrin.gif
com
Cytat
Coś długo te poprawki


Bo tam w tym kodzie niczego się nie dało wykorzystać, no może poza tym co jest gotowe czyli wykorzystanie FFMpeg. I wcale nie powiedziałem, że dam Ci to od razu, przecież cały czas nie siedziałem nad tym rozwianiem, robię inne rzeczy o wyższym priorytecie niż ten kod.

https://github.com/ComStudio/decorator
Omenomn
Gdzie Ty tam masz ten Twój niezrozumiały przeze mnie dekorator, bo nie widzę tam ani jednej klasy wywołanej w konstruktorze innej?

I w czym niby Twój kod jest lepszy od mojego?

info o pliku pobierasz w trzech, albo więcej miejscach:
  1. public function save(File $file)
  2. {
  3. $this->image->save($file);
  4. $storage = $this->getStorage();
  5. $fileName = $file->getName();
  6. $thumbnailFromImage = new ThumbnailFromImage(150);
  7. $thumbnail = new File(
  8. $fileName . '_thumb',
  9. $file->getExtension(),
  10. $thumbnailFromImage->create($file),
  11. $file->getPath()
  12. );
  13. if($storage->has($fileName))
  14. {
  15. $image = $storage->get($fileName);
  16. $image['thumbnail'] = $thumbnail;
  17. }
  18. }


  1. public function save(File $file)
  2. {
  3. $this->image->save($file);
  4. $storage = $this->getStorage();
  5. $fileName = $file->getName();
  6. $thumbnailFromVideo = new ThumbnailFromVideo();
  7. $thumbnail = new File(
  8. $fileName . '_thumb',
  9. $file->getExtension(),
  10. $thumbnailFromVideo->create($file),
  11. $file->getPath()
  12. );
  13. if($storage->has($fileName))
  14. {
  15. $image = $storage->get($fileName);
  16. $image['thumbnail'] = $thumbnail;
  17. }
  18. }

  1. public function __construct($name, $extension, $content, $path)
  2. {
  3. $this->name = $name;
  4. $this->extension = $extension;
  5. $this->content = $content;
  6. $this->path = $path;
  7. }
  8. public static function CreateFromRequest($file)
  9. {
  10. return new File(
  11. $file->getClientOriginalName(),
  12. $file->getClientOriginalExtension(),
  13. \File::get($file->getRealPath()),
  14. $file->getRealPath()
  15. );
  16. }


Nawet nie chce mi się już analizować tego kodu, burdel taki, że głowa boli.
com
Cytat
Gdzie Ty tam masz ten Twój niezrozumiały przeze mnie dekorator, bo nie widzę tam ani jednej klasy wywołanej w konstruktorze innej?


https://github.com/ComStudio/decorator/blob/master/index.php

Tu masz wywołanie.

Cytat
info o pliku pobierasz w trzech, albo więcej miejscach:

A tu co niby robisz https://github.com/omenomn/uploadDecorator/...sformers/Makers
W każdej klasie zwracasz 95% tych samych informacji.
Ja tam niczego nie pobieram, poza klasa File, w pozostałych miejscach tylko zwracam te informacje, żeby wygenerować nowy plik z miniaturka na bazie oryginału.
W dodatku te dwie metody save to przecież dwie rożne klasy, jedna dla Image, druga dla Video.

Ten fragment to tworzenie nowego obrazka, czyli tej miniaturki, bo coś źle chyba to zrozumiałeś.
  1. $thumbnail = new File(
  2. $fileName . '_thumb',
  3. $file->getExtension(),
  4. $thumbnailFromVideo->create($file),
  5. $file->getPath()
  6. );


Moje File to Twoje:
https://github.com/omenomn/uploadDecorator/...kerAbstract.php

Klasa abstrakcyjna ma co najmniej jedna metodę abstrakcyjna, inaczej nie jest klasą abstrakcyjną, wiec nie ma sensu.

A tu to już wgl szaleństwo
https://github.com/omenomn/uploadDecorator/...s/Puter.php#L11

Potrafisz stworzyć obiekt klasy abstrakcyjnej. Aż dziw że ten kod działa aarambo.gif

Cytat
Nawet nie chce mi się już analizować tego kodu, burdel taki, że głowa boli.


Burdel to masz w swoim kodzie, widać, że nawet nie używasz porządnego IDE, bo masz masę rzeczy które są zbędne i albo nigdzie nie używasz.
Skoro te rozwiązanie jest dla Ciebie nie zrozumiałe, to nie mamy o czym rozmawiać, bo nie potrafisz pisać jeszcze obiektowo. Za parę lat jak wrócisz i się tego nauczysz to wtedy zrozumiesz to co stworzyłem smile.gif

Twój kod to idealny przykład jak tego nie robić smile.gif

Utrzymanie takiego kodu jaki stworzyłeś już dziś jest problematyczne. Nie mówiąc już o zrozumieniu jego, bo z niego nie wynika nic co chciałeś tam zrobić i jak ktoś dostanie ten kod to będzie się bał wgl to ruszyć smile.gif

Polecam lekturę CzystyKod.
Omenomn
  1. $upload = new Upload($imageSession);
  2. $upload->save($file);
  3. //Obraz z miniaturka
  4. $uploadImageWithThumbnail = new ImageWithThumbnail(new Upload($imageSession));
  5. $uploadImageWithThumbnail->save($file);
  6. //Video
  7. $videoSession = new SessionStorage('video');
  8. //Zwykły upload
  9. $upload = new Upload($videoSession);
  10. $upload->save($file);
  11. //Wideo z miniaturka
  12. $uploadVideoWithThumbnail = new VideoWithThumbnail(new Upload($videoSession));
  13. $uploadVideoWithThumbnail->save($file);


Ciekawe jak zapiszesz plik bez zapisywania informacji o nim do sesji, skoro klasa Upload wymaga interjejsu StorageInterface.
Gdzie masz kodowanie, zmianę nazwy randomową i do ustawienia?

W dekoratorze używa się jednego interfejsu, a nie jak tutaj:
  1. class Upload implements UploadInterface{}
  2. class SessionStorage implements StorageInterface{}
  3. class ImageWithThumbnail implements UploadInterface{}


No chyba, że bazę traktujesz jako:
  1. (new Upload($videoSession))

tylko po co ta sesja w konstruktorze?

Cytat
A tu co niby robisz https://github.com/omenomn/uploadDecorator/...sformers/Makers
W każdej klasie zwracasz 95% tych samych informacji.


To są transformery, one nie pobierają danych tylko transformują otrzymane dane do postaci jaką się potrzebuje. Działają na pojedynczym elemencie i kolekcji.

Cytat
A tu to już wgl szaleństwo
https://github.com/omenomn/uploadDecorator/...s/Puter.php#L11

Potrafisz stworzyć obiekt klasy abstrakcyjnej. Aż dziw że ten kod działa aarambo.gif


Nie tworzę obiektu klasy abstrakcyjnej tylko jej interfejsu. Klasa abstrakcyjna działa jak interfejs z tym, że metody możesz w niej dodawać.


Przykłady wywołania mojego kodu:
  1. $file = new Puter(new ImageSessionPuter(new Returner)); //Wysłanie pliku, zapis do sesji, zwrócenie
  2. $file = new Returner; // Samo zwrócenie danych o pliku
  3. $file = new ImageSessionPuter(new Returner); // Zapis do sesji danych o pliku bez uploadu
  4. $file = new Puter(new Returner); // Zapis pliku i zwrócenie informacji o nim
  5. $file = new ImageSessionPuter(new Puter(new Returner)) // zapis do sesji, wysłanie pliku,, zwrócenie - odwrotna kolejność
  6. $file = new RandomNamer(new Puter(new Returner));
  7. $file = new RandomNamer(new Encoder(new Puter(new Returner))) // losowa nazwa, kodowanie, zapis, zwrócenie danych
  8. $file->make($object);


Który kod jest bardziej elastyczny?
Odpowiedź jest chyba oczywista.

Ogólnie rzecz biorąc problem nie jest tak oczywisty do rozwiązania i błachy, jak wydaje mi się, że go większość potraktowała.
daro0
Pod Laravela jest coś interesującego np. do obróbki zdjęć a i z uploadem sprawa powinna być prosta.

http://image.intervention.io/

Podobne podejście jak w Kohanie (gdzie jest moduł Image).

http://www.easylaravelbook.com/blog/2015/0...with-laravel-5/

Omenomn
Wrzucałem już link do intervention image w tym temacie i pisałem, że go wykorzystuję.

Daro0 nie rozumiesz problemu, jeśli podrzucasz mi bibliotekę do obróbki zdjęć.
Ona nie wykonuje uploadu, zmiany nazw plików, zapisu danych o plikach do sesji, wycinania miniaturek z video, czy innych czynności związanych ogólnie z plikami, a nie tylko z obrazami.

Rozwiązanie, które przedstawiłem wykonuje wszystkie te operacje plus do tego jest całkowicie elastyczne, bo można dodawać poszczególne warstwy odpowiedzialne za kolejne operacje, bez ingerencji, w którąkolwiek warstwę już istniejącą, np. pobieranie wysokość i szerokość (wykorzystanie intervention image), albo rozmiaru pliku, czy innych właściwości. Mogę np. chcieć zmienić wielkość filmiku i wtedy po prostu dodaję warstwę odpowiedzialną za to.

Jeśli nie widzicie plusów takiego rozwiązania to Ja nie wiem...

ps.
Jednak warto było mimo wszystko temat umieścić, bo pomysł z dekoratorem przyszedł mi po próbach szukania odpowiedniego wzorca zainspirowany odpowiedziami tutaj między innymi mrc, a ostateczny szlif dokonał się po uzmysłowieniu sobie, że klasa bazowa dekoratora powinna funkcjonować sama (com)
rafkon1990
Jako obserwator tematu dziwię się że com jeszcze chce z tobą dyskutować. Jego kod jest czytelny, samoopisujacy sie, zrozumiały dla osób postronnych (przynajmniej tych znających OOP). Wystarczyło mi 3 minuty na zrozumienie całości (może dlatego że sam bym to zaimplementował bardzo podobnie).

Twoj kod omenomn analizowałem 5 minut i zrezygnowałem. Nie ma tam dzbła KISS, jeszcze te nazwy klas (puter, returner) ... może i twoje rozwiązanie jest zrozumiałe dla ciebie i w twoim mniemaniu jest elastyczne, ale do cholery nie mógł bym pracować na twoim kodzie, bo jest po prostu zbyt skomplikowany.
com
Cytat
Ciekawe jak zapiszesz plik bez zapisywania informacji o nim do sesji, skoro klasa Upload wymaga interjejsu StorageInterface.
Gdzie masz kodowanie, zmianę nazwy randomową i do ustawienia?


Tam masz interfejs wiec zapisujesz do czegokolwiek co implementuje ten interfejs. Przecież nie zrobię Ci gotowca, ja tylko wskazałem drogę która masz iść, Zmiana kodowania,nazwy itd to będą kolejne dekoratory, które sobie dopiszesz w miarę potrzeb.

Dekoratorem jest z tego fragmentu tylko:
  1. class ImageWithThumbnail implements UploadInterface{}


Upload to klasa bazowa, a SessionStorage wgl nie ma znaczenia dla dekoratora. Bo to jest zależność wstrzykiwana do Upload, dlatego możesz wstawić dowolny storage jaki sobie wymarzysz, warunek jest tylko taki musi implementować zdefiniowany interfejs.

Cytat
To są transformery, one nie pobierają danych tylko transformują otrzymane dane do postaci jaką się potrzebuje. Działają na pojedynczym elemencie i kolekcji.


Nie rozumiem wgl ich zastosowania, ale ja to wszytki zawarłem w jednej klasie File która działa dla wszystkich.

Cytat
Nie tworzę obiektu klasy abstrakcyjnej tylko jej interfejsu. Klasa abstrakcyjna działa jak interfejs z tym, że metody możesz w niej dodawać.


Owszem klasa abstrakcyjna może posiadać definicje jakiś wspólnych metod ale musi posiadać co najmniej jedną metodę abstrakcyjną, żeby można było ja nazwać klasa abstrakcyjna, inaczej to jest zwykła klasa.
To są podstawy programowania obiektowego.
Cytat
Który kod jest bardziej elastyczny?
Odpowiedź jest chyba oczywista.


Owszem jest, nie Twój i to nie jest moja tylko subiektywna opinia, ale reszta osób stwierdziła to samo.

Nie wiem gdzie Ty pracujesz, czy robisz to sam czy w jakimś zespole ale jak w zespole to niech ktoś zrobi Ci code review tego co stworzyłeś smile.gif
Pyton_000
@com Co do abstrakcji to klasa abstrakcyjna nie musi zawierać metod abstrakcyjnych wink.gif Za to każda klasa która posiada przynajmniej 1 abstrakcyjną metodą musi być abstrakcyjna.

Tak więc

Kod
abstract class Test {
public function testing() {

}
}

jest poprawne.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.