• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

question-closed Jak wymusić typ interfejs w tablicy?

Object Storage Arubacloud
0 głosów
187 wizyt
pytanie zadane 3 listopada 2016 w PHP przez HaKIM Szeryf (87,590 p.)
zamknięte 3 listopada 2016 przez HaKIM

Dobry wieczór.

Potrzebuję wymusić typ interfejsu Unit w tablicy.

    public function addUnits(Unit $unit)
    {
        $this->units[] = $unit;
    }

Podczas gdy to normalny sposób, ja chcę wywołać coś w ten deseń:

    public function addUnits(Unit [$unit])
    {
        $this->units[] = $unit;
    }

Oczywiście, takowy kod zwróci błąd.

Próbowałem z czymś takim:

    public function addUnits(array $unit)
    {
        if (!$unit[0] instanceof Unit) {
            throw new \InvalidArgumentException('Objects must be instance of Units interface.');
        }
    }

Ale to sprawdza tylko pierwszy indeks, gdy takowych może być więcej.

Gdyby ktoś potrzebował całego kodu tejże klasy, to:

namespace WarCraft\Unit; 

use WarCraft\Unit\Units\Unit; 

abstract class UnitComposite extends Unit
{
    protected $units = [];

    public function addUnits(array $unit)
    {
        $this->units = $unit;
    }
}

Jakie będzie najlepsze rozwiązanie tego problemu?


Rozwiązanie to pętla foreach.

Kod:

        foreach ($unit as $units) {
            if (!$units instanceof Unit) {
                throw new \InvalidArgumentException('Objects must be instance of Units interface.');
            }
        }

 

komentarz zamknięcia: Problem rozwiązany.

1 odpowiedź

+2 głosów
odpowiedź 3 listopada 2016 przez suice Gaduła (3,960 p.)
wybrane 3 listopada 2016 przez HaKIM
 
Najlepsza
Skoro to jest tablica obiektow to czemu nie zwykly foreach?
komentarz 3 listopada 2016 przez HaKIM Szeryf (87,590 p.)
edycja 3 listopada 2016 przez HaKIM

A ja żym myślał nad while...

        foreach ($unit as $units) {
            if (!$units instanceof Unit) {
                throw new \InvalidArgumentException('Objects must be instance of Units interface.');
            }
        }

Dzięki.

komentarz 3 listopada 2016 przez efiku Szeryf (75,160 p.)
Twoje OOP idzie w złą stronę, coraz dziwniejszy kod się robi. ;d

A dlaczego nie klasa UnitCollection jako tablica przechowująca Unit. Dodawanie / kasowanie Unit, odpowiednia dostępność / metody.

Wtedy znika nam ten foreach i niepotrzebny instanceof..
komentarz 4 listopada 2016 przez HaKIM Szeryf (87,590 p.)

Hmmm...

Z tego co mówisz, wychodziłby, że UnitComposite jest mi zbędny.

Zajmę się przeróbką po szkole. Teraz pora na sen.

 Dodawanie / kasowanie Unit, odpowiednia dostępność / metody. 

 Z tego fragmentu nie idzie wywnioskować która klasa ma przechowywać te metody. 

Niee... To nie zagadka na tę porę. Przysiądę, jak już wspominałem, po szkole.

Dobranoc i bardzo dziękuję za ostrzeżenie mnie, że nie tędy droga. :)

komentarz 4 listopada 2016 przez HaKIM Szeryf (87,590 p.)

Efik, coś wykombinowałem.

Wygląda to mniej więcej tak:

Client

//Client

        $this->main_army = new Army([
            new \WarCraft\Unit\Units\Orc()
        ]);

Army

namespace WarCraft\Army;

use WarCraft\Unit\UnitCollection;

class Army extends UnitCollection
{
    protected $unit = [];

    public function getNameUnit() : string
    {
        $nameUnits = '';
        foreach ($this->unit as $unit) {
            $nameUnits .= $unit->getNameUnit();
        }

        return $nameUnits;
    }
}

UnitCollection

namespace WarCraft\Unit;

use WarCraft\Unit\Units\Unit;

class UnitCollection extends Unit
{
    protected $unit = [];

    public function __construct(array $unit)
    {
        $this->unit = $unit;
    }

    protected function getNameUnit() : string
    {
        return $this->getNameUnit();
    }
}

Proszę, daj znać czy tak jest lepiej.

Choć, foreach z instanceof nadal pozostaje.

W innym wypadku możliwym będzie dodanie kodu(Obiektu):

        $this->main_army = new Army([
            new Orc(),
            new Battle()
            // Całkowicie inny obiekt. Oczywiście, zwróci błąd podczas próby
            // wywołania metody potomnych interfejsu Unit.
        ]);

Można też wymusić coś takiego, ale potrzebaby dodać metodę addUnitToArmy(Unit $unit) w klasie UnitCollection. Czyli klasa Client wyglądałaby jakoś tak:

    $this->main_army = new Army();
    $this->main_army->addUnitToArmy(new Orc);
    $this->main_army->addUnitToArmy(new Orc);

Zamiast:

        $this->main_army = new Army([
            new Orc(),
            new Orc()
        ]);

Osobiście wolę wersję z tablicą.

No, chyba, że masz jakąś wskazówkę jak temu zaradzić. Mi w głowie nic nieskomplikowanego nie świta.

Więcej tutaj:

https://github.com/HaKIMus/oopfun/tree/feature

komentarz 4 listopada 2016 przez efiku Szeryf (75,160 p.)

https://3v4l.org/2Od6Z

IMHO Coraz bardziej sobie komplikujesz życie. Wszędzie widać powtarzalność kodu.
PHP na razie nie jest przystosowany do takich rozwiązań chyba, że wejdzie rfc z generycznymi.

Dlaczego chcesz wymusić, aby tablica była tylko kolekcją klasy Unit. Na razie to ty wsadzasz tam na siłę coś innego i oczekujesz, że rzuci błędem, nie aplikacja. W aplikacji podejrzewam, że Tworzone obiekty będą potrafiły trafić na odpowiednie miejsce  (odpowiednie typy w argumentach metod, fabryki jednostek, vaildatory). Zauważ, że  fabryki* jednostek będą zwracały tablicę z jednostkami i to z tego kontrolera w którym robi się operacje na naszej armii.

Jak widać, nie wymyśliłem też niczego lepszego, Zwykły kontener z ifem sprawdzającym typ.

* wzorzec.

komentarz 5 listopada 2016 przez HaKIM Szeryf (87,590 p.)
edycja 8 listopada 2016 przez HaKIM

Dlaczego chcesz wymusić, aby tablica była tylko kolekcją klasy Unit. Na razie to ty wsadzasz tam na siłę coś innego i oczekujesz, że rzuci błędem, nie aplikacja. 

W sumie to masz racje. Jeżeli ktoś będzie widział powód aby wcisnąć tam budynek, to czemu by nie pozwolić mu tego zrobić? (O ile spełni wymagania metod interfejsu dla poprawnego działania).

class UnitContainer extends ArrayObject{...}

To o to chodziło w:

A dlaczego nie klasa UnitCollection jako tablica przechowująca Unit. 

Przerabiając Twój kod pod moje oczekiwania byłoby to niemal niemożliwe / zostałoby na tym samym. W moim przypadku nie występuje setter w postaci __construct, jedynie sztywnie ustalony getter.

Lub, można dodać setter, lecz jako abstrakcyjna, gdyż nie każda klasa implementująca będzie chciała mieć takie samo miano, gdy pozostawimy go pustym.

//Orc
    public function setNameUnit($name = 'Orc')
    {
        $this->nameUnit = $name;
    }

Gdybym chciał to dać w __construct() odpada default'owa opcja.

   new  Unit('Orc',1),
   new  Unit('Orc',2),
   new  Unit('Orc',3),
   new  Unit('Orc',4),
   new  Unit('Foortman'',5)

Jest dość męczące.

IMO to opcja z ArrayObject jest mało korzystna.

Wszędzie widać powtarzalność kodu. 

Wyeliminowane. (O ile to o to chodziło). W ogóle nie było wyeliminowane... Jeszcze gorzej!

abstract class UnitCollection extends UnitFactory
{
    protected $units = [];

    public function __construct($units)
    {
        $this->units = $units;
    }
}
class Army extends UnitCollection
{
    protected $units = [];

    public function getNameUnit() : string
    {
        $nameUnits = '';
        foreach ($this->units as $unit) {
            $nameUnits .= $unit->getNameUnit();
        }

        return $nameUnits;
    }
}

Jeżeli mielibyśmy odwoływać się bezpośrednio do potomnych interfejsu Unit to odbywałoby się to w bardzo podobny sposób.

Co do:

abstract class UnitCollection extends UnitFactory
{
    protected $units = [];

    public function __construct($units)
    {
        $this->units = $units;
    }
}
class Army extends UnitCollection
{
    protected $units = [];
    [...]
}

Po klasie UnitCollection może dziedziczyć więcej klas, gdy zajdzie taka potrzeba. Gdybym dodał do Army __construct(), a w przyszłości któryś z obiektów potrzebowałby składowej unit nie miałoby większego sensu rozszerzanie o Army - jeżeli taka klasa nie potrzebuje metod Army.

komentarz 8 listopada 2016 przez HaKIM Szeryf (87,590 p.)

Baran ze mnie.

Miałeś rację.

Podczas próby dodania Battle zobaczyłem jak bardzo potrzebuję czegoś w stylu UnitContainer i jak zbędny jest UnitCollection.

Ehh... A było się od razu posłuchać to nie... Wstawiłem pół projektu, nieprzemyślane wizje i zadowolony, że mogę pisać dalej.

Dziękuję i zarazem przepraszam za zaślepki na oczach.

Miłego.

Podobne pytania

+1 głos
1 odpowiedź 310 wizyt
pytanie zadane 12 lipca 2022 w PHP przez mat19 Obywatel (1,580 p.)
0 głosów
1 odpowiedź 130 wizyt
pytanie zadane 1 sierpnia 2019 w JavaScript przez rafal.budzis Szeryf (85,260 p.)
0 głosów
2 odpowiedzi 1,460 wizyt

92,580 zapytań

141,432 odpowiedzi

319,664 komentarzy

61,965 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...